recker 1.0.72 → 1.0.75-next.2e5a94f
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/README.md +5 -18
- package/dist/browser/core/client.d.ts +14 -8
- package/dist/browser/core/client.js +199 -17
- package/dist/browser/core/errors.d.ts +15 -1
- package/dist/browser/core/errors.js +140 -9
- package/dist/browser/core/request.d.ts +5 -0
- package/dist/browser/core/request.js +33 -2
- package/dist/browser/core-runtime/plugin-manifest.d.ts +24 -0
- package/dist/browser/core-runtime/plugin-manifest.js +159 -0
- package/dist/browser/core-runtime/request-context.d.ts +13 -0
- package/dist/browser/core-runtime/request-context.js +24 -0
- package/dist/browser/core-runtime/typed-events.d.ts +89 -0
- package/dist/browser/core-runtime/typed-events.js +34 -0
- package/dist/browser/index.iife.min.js +79 -79
- package/dist/browser/index.min.js +79 -79
- package/dist/browser/index.mini.iife.js +913 -97
- package/dist/browser/index.mini.iife.min.js +46 -46
- package/dist/browser/index.mini.min.js +46 -46
- package/dist/browser/index.mini.umd.js +913 -97
- package/dist/browser/index.mini.umd.min.js +46 -46
- package/dist/browser/index.umd.min.js +79 -79
- package/dist/browser/plugins/auth/aws-sigv4.d.ts +1 -0
- package/dist/browser/plugins/auth/aws-sigv4.js +19 -2
- package/dist/browser/plugins/retry.js +29 -1
- package/dist/browser/presets/aws.d.ts +1 -0
- package/dist/browser/presets/aws.js +62 -1
- package/dist/browser/runner/request-runner.d.ts +15 -5
- package/dist/browser/runner/request-runner.js +164 -30
- package/dist/browser/scrape/parser/nodes/html.d.ts +6 -0
- package/dist/browser/scrape/parser/nodes/html.js +70 -18
- package/dist/browser/scrape/parser/nodes/node.d.ts +1 -0
- package/dist/browser/scrape/parser/nodes/node.js +5 -0
- package/dist/browser/scrape/spider.d.ts +1 -0
- package/dist/browser/scrape/spider.js +39 -26
- package/dist/browser/seo/analyzer.d.ts +1 -1
- package/dist/browser/seo/analyzer.js +73 -42
- package/dist/browser/seo/index.d.ts +1 -1
- package/dist/browser/seo/rules/types.d.ts +2 -0
- package/dist/browser/seo/seo-spider.d.ts +2 -3
- package/dist/browser/seo/seo-spider.js +26 -202
- package/dist/browser/seo/types.d.ts +4 -0
- package/dist/browser/seo/validators/sitemap.js +9 -2
- package/dist/browser/transport/fetch.js +38 -5
- package/dist/browser/transport/undici.js +73 -11
- package/dist/browser/transport/worker.d.ts +0 -1
- package/dist/browser/transport/worker.js +1 -3
- package/dist/browser/types/index.d.ts +24 -0
- package/dist/cli/commands/mcp.js +5 -3
- package/dist/core/client.d.ts +14 -8
- package/dist/core/client.js +199 -17
- package/dist/core/errors.d.ts +15 -1
- package/dist/core/errors.js +140 -9
- package/dist/core/request.d.ts +5 -0
- package/dist/core/request.js +33 -2
- package/dist/core-runtime/plugin-manifest.d.ts +24 -0
- package/dist/core-runtime/plugin-manifest.js +159 -0
- package/dist/core-runtime/request-context.d.ts +13 -0
- package/dist/core-runtime/request-context.js +24 -0
- package/dist/core-runtime/typed-events.d.ts +89 -0
- package/dist/core-runtime/typed-events.js +34 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/mcp/cli.js +10 -8
- package/dist/mcp/profiles.d.ts +1 -1
- package/dist/mcp/profiles.js +31 -6
- package/dist/mcp/tools/categories.js +0 -1
- package/dist/mcp/tools/seo.js +320 -4
- package/dist/plugins/auth/aws-sigv4.d.ts +1 -0
- package/dist/plugins/auth/aws-sigv4.js +19 -2
- package/dist/plugins/retry.js +29 -1
- package/dist/presets/aws.d.ts +1 -0
- package/dist/presets/aws.js +62 -1
- package/dist/recker.d.ts +3 -0
- package/dist/recker.js +5 -0
- package/dist/runner/request-runner.d.ts +15 -5
- package/dist/runner/request-runner.js +164 -30
- package/dist/scrape/parser/nodes/html.d.ts +6 -0
- package/dist/scrape/parser/nodes/html.js +70 -18
- package/dist/scrape/parser/nodes/node.d.ts +1 -0
- package/dist/scrape/parser/nodes/node.js +5 -0
- package/dist/scrape/spider.d.ts +1 -0
- package/dist/scrape/spider.js +39 -26
- package/dist/search/google.d.ts +67 -0
- package/dist/search/google.js +480 -0
- package/dist/search/index.d.ts +3 -0
- package/dist/search/index.js +1 -0
- package/dist/seo/analyzer.d.ts +1 -1
- package/dist/seo/analyzer.js +73 -42
- package/dist/seo/index.d.ts +1 -1
- package/dist/seo/rules/types.d.ts +2 -0
- package/dist/seo/seo-spider.d.ts +2 -3
- package/dist/seo/seo-spider.js +26 -202
- package/dist/seo/types.d.ts +4 -0
- package/dist/seo/validators/sitemap.js +9 -2
- package/dist/transport/fetch.js +38 -5
- package/dist/transport/undici.js +73 -11
- package/dist/transport/worker.d.ts +0 -1
- package/dist/transport/worker.js +1 -3
- package/dist/types/index.d.ts +24 -0
- package/dist/version.js +1 -1
- package/package.json +9 -1
|
@@ -8,3 +8,4 @@ export interface AWSSignatureV4Options {
|
|
|
8
8
|
}
|
|
9
9
|
export declare function awsSignatureV4(options: AWSSignatureV4Options): Middleware;
|
|
10
10
|
export declare function awsSignatureV4Plugin(options: AWSSignatureV4Options): Plugin;
|
|
11
|
+
export declare function clearAwsSigV4PluginCache(): void;
|
|
@@ -81,8 +81,25 @@ export function awsSignatureV4(options) {
|
|
|
81
81
|
return next(newReq);
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
|
+
const awsSigV4PluginCache = new Map();
|
|
84
85
|
export function awsSignatureV4Plugin(options) {
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
const cacheKey = [
|
|
87
|
+
options.accessKeyId,
|
|
88
|
+
options.secretAccessKey,
|
|
89
|
+
options.region,
|
|
90
|
+
options.service,
|
|
91
|
+
options.sessionToken || '',
|
|
92
|
+
].join('|');
|
|
93
|
+
const cached = awsSigV4PluginCache.get(cacheKey);
|
|
94
|
+
if (cached)
|
|
95
|
+
return cached;
|
|
96
|
+
const safeOptions = { ...options };
|
|
97
|
+
const plugin = (client) => {
|
|
98
|
+
client.use(awsSignatureV4(safeOptions));
|
|
87
99
|
};
|
|
100
|
+
awsSigV4PluginCache.set(cacheKey, plugin);
|
|
101
|
+
return plugin;
|
|
102
|
+
}
|
|
103
|
+
export function clearAwsSigV4PluginCache() {
|
|
104
|
+
awsSigV4PluginCache.clear();
|
|
88
105
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { HttpError, NetworkError, TimeoutError } from '../core/errors.js';
|
|
1
|
+
import { HttpError, NetworkError, TimeoutError, classifyTransportError } from '../core/errors.js';
|
|
2
|
+
import { getRequestContext } from '../core-runtime/request-context.js';
|
|
2
3
|
function calculateDelay(attempt, baseDelay, maxDelay, strategy, useJitter) {
|
|
3
4
|
let calculatedDelay;
|
|
4
5
|
switch (strategy) {
|
|
@@ -54,6 +55,13 @@ export function retryPlugin(options = {}) {
|
|
|
54
55
|
if (error instanceof HttpError) {
|
|
55
56
|
return statusCodes.includes(error.status);
|
|
56
57
|
}
|
|
58
|
+
const classification = classifyTransportError(error);
|
|
59
|
+
if (classification && classification.canRetry === false) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
if (classification) {
|
|
63
|
+
return classification.canRetry;
|
|
64
|
+
}
|
|
57
65
|
if (error && typeof error === 'object' && 'code' in error) {
|
|
58
66
|
const code = error.code;
|
|
59
67
|
return code === 'ECONNRESET' || code === 'ETIMEDOUT' || code === 'ENOTFOUND';
|
|
@@ -62,6 +70,24 @@ export function retryPlugin(options = {}) {
|
|
|
62
70
|
};
|
|
63
71
|
const shouldRetry = options.shouldRetry || defaultShouldRetry;
|
|
64
72
|
return (client) => {
|
|
73
|
+
const emitRequestRetry = (error, attempt, delayMs, req) => {
|
|
74
|
+
const eventBus = client.runtimeEventBus;
|
|
75
|
+
if (!eventBus?.emit) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const context = getRequestContext(req);
|
|
79
|
+
if (!context) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const classification = classifyTransportError(error);
|
|
83
|
+
eventBus.emit('request:retry', {
|
|
84
|
+
context,
|
|
85
|
+
req,
|
|
86
|
+
attempt,
|
|
87
|
+
delayMs,
|
|
88
|
+
reason: classification?.reason || error?.message || 'retry requested'
|
|
89
|
+
});
|
|
90
|
+
};
|
|
65
91
|
const middleware = async (req, next) => {
|
|
66
92
|
let attempt = 0;
|
|
67
93
|
while (true) {
|
|
@@ -80,6 +106,7 @@ export function retryPlugin(options = {}) {
|
|
|
80
106
|
delayMs = calculateDelay(attempt, baseDelay, maxDelay, backoffStrategy, useJitter);
|
|
81
107
|
}
|
|
82
108
|
const err = new HttpError(res, req);
|
|
109
|
+
emitRequestRetry(err, attempt, delayMs, req);
|
|
83
110
|
if (onRetry) {
|
|
84
111
|
onRetry(attempt, err, delayMs);
|
|
85
112
|
}
|
|
@@ -96,6 +123,7 @@ export function retryPlugin(options = {}) {
|
|
|
96
123
|
catch (error) {
|
|
97
124
|
if (attempt < maxAttempts && shouldRetry(error)) {
|
|
98
125
|
const delayMs = calculateDelay(attempt, baseDelay, maxDelay, backoffStrategy, useJitter);
|
|
126
|
+
emitRequestRetry(error, attempt, delayMs, req);
|
|
99
127
|
if (onRetry) {
|
|
100
128
|
onRetry(attempt, error, delayMs);
|
|
101
129
|
}
|
|
@@ -10,6 +10,7 @@ export interface AWSPresetOptions {
|
|
|
10
10
|
export type AWSService = 's3' | 'dynamodb' | 'lambda' | 'sqs' | 'sns' | 'ses' | 'secretsmanager' | 'ssm' | 'sts' | 'iam' | 'ec2' | 'ecs' | 'eks' | 'cloudwatch' | 'logs' | 'events' | 'kinesis' | 'firehose' | 'apigateway' | 'execute-api' | 'cognito-idp' | 'cognito-identity' | 'kms' | 'athena' | 'glue' | 'stepfunctions' | 'states' | 'bedrock' | 'bedrock-runtime';
|
|
11
11
|
export declare function aws(options: AWSPresetOptions): ClientOptions;
|
|
12
12
|
export declare function awsS3(options: Omit<AWSPresetOptions, 'service'>): ClientOptions;
|
|
13
|
+
export declare function s3(options: Omit<AWSPresetOptions, 'service'>): ClientOptions;
|
|
13
14
|
export declare function awsDynamoDB(options: Omit<AWSPresetOptions, 'service'>): ClientOptions;
|
|
14
15
|
export declare function awsLambda(options: Omit<AWSPresetOptions, 'service'>): ClientOptions;
|
|
15
16
|
export declare function awsSQS(options: Omit<AWSPresetOptions, 'service'>): ClientOptions;
|
|
@@ -1,4 +1,62 @@
|
|
|
1
1
|
import { awsSignatureV4Plugin } from '../plugins/auth.js';
|
|
2
|
+
const S3_STORAGE_PRESET = {
|
|
3
|
+
timeout: {
|
|
4
|
+
lookup: 5000,
|
|
5
|
+
connect: 10000,
|
|
6
|
+
secureConnect: 10000,
|
|
7
|
+
response: 30000,
|
|
8
|
+
request: 60000,
|
|
9
|
+
},
|
|
10
|
+
http2: 'performance',
|
|
11
|
+
expectContinue: 2 * 1024 * 1024,
|
|
12
|
+
observability: false,
|
|
13
|
+
concurrency: {
|
|
14
|
+
agent: {
|
|
15
|
+
connections: 100,
|
|
16
|
+
pipelining: 10,
|
|
17
|
+
keepAlive: true,
|
|
18
|
+
keepAliveTimeout: 4000,
|
|
19
|
+
keepAliveMaxTimeout: 600000,
|
|
20
|
+
keepAliveTimeoutThreshold: 1000,
|
|
21
|
+
maxRequestsPerClient: 100,
|
|
22
|
+
clientTtl: null,
|
|
23
|
+
maxCachedSessions: 100,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
retry: {
|
|
27
|
+
maxDelay: 30000,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
function createAwsS3Preset(options) {
|
|
31
|
+
const baseConfig = aws({
|
|
32
|
+
...options,
|
|
33
|
+
service: 's3',
|
|
34
|
+
});
|
|
35
|
+
const baseConcurrency = baseConfig.concurrency;
|
|
36
|
+
let concurrency;
|
|
37
|
+
if (typeof baseConcurrency === 'number') {
|
|
38
|
+
concurrency = baseConcurrency;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
const mergedConcurrency = {};
|
|
42
|
+
if (baseConcurrency && typeof baseConcurrency === 'object') {
|
|
43
|
+
Object.assign(mergedConcurrency, S3_STORAGE_PRESET.concurrency, baseConcurrency);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
Object.assign(mergedConcurrency, S3_STORAGE_PRESET.concurrency);
|
|
47
|
+
}
|
|
48
|
+
concurrency = mergedConcurrency;
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
...baseConfig,
|
|
52
|
+
...S3_STORAGE_PRESET,
|
|
53
|
+
retry: {
|
|
54
|
+
...baseConfig.retry,
|
|
55
|
+
...S3_STORAGE_PRESET.retry,
|
|
56
|
+
},
|
|
57
|
+
concurrency,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
2
60
|
function getServiceEndpoint(service, region) {
|
|
3
61
|
const globalServices = ['iam', 'sts', 'cloudfront', 'route53'];
|
|
4
62
|
if (globalServices.includes(service)) {
|
|
@@ -43,7 +101,10 @@ export function aws(options) {
|
|
|
43
101
|
};
|
|
44
102
|
}
|
|
45
103
|
export function awsS3(options) {
|
|
46
|
-
return
|
|
104
|
+
return createAwsS3Preset(options);
|
|
105
|
+
}
|
|
106
|
+
export function s3(options) {
|
|
107
|
+
return createAwsS3Preset(options);
|
|
47
108
|
}
|
|
48
109
|
export function awsDynamoDB(options) {
|
|
49
110
|
return aws({ ...options, service: 'dynamodb' });
|
|
@@ -3,6 +3,12 @@ export interface RunnerOptions {
|
|
|
3
3
|
retries?: number;
|
|
4
4
|
retryDelay?: number;
|
|
5
5
|
}
|
|
6
|
+
export interface RunnerRunOptions {
|
|
7
|
+
priority?: number;
|
|
8
|
+
retries?: number;
|
|
9
|
+
signal?: AbortSignal;
|
|
10
|
+
deadlineMs?: number;
|
|
11
|
+
}
|
|
6
12
|
export interface RequestTask<T = any> {
|
|
7
13
|
id: string;
|
|
8
14
|
fn: () => Promise<T>;
|
|
@@ -35,10 +41,14 @@ export declare class RequestRunner extends SimpleEmitter {
|
|
|
35
41
|
private queue;
|
|
36
42
|
private activeCount;
|
|
37
43
|
private paused;
|
|
38
|
-
private results;
|
|
39
44
|
private stats;
|
|
40
45
|
private startTime;
|
|
41
46
|
private pendingRetries;
|
|
47
|
+
private isCancelled;
|
|
48
|
+
private cancelReason;
|
|
49
|
+
private retryTimers;
|
|
50
|
+
private timeoutId?;
|
|
51
|
+
private abortUnsubscribe?;
|
|
42
52
|
constructor(options?: RunnerOptions);
|
|
43
53
|
add<T>(fn: () => Promise<T>, options?: {
|
|
44
54
|
priority?: number;
|
|
@@ -47,13 +57,13 @@ export declare class RequestRunner extends SimpleEmitter {
|
|
|
47
57
|
trackTotal?: boolean;
|
|
48
58
|
resolve?: (value: T | Error) => void;
|
|
49
59
|
}): void;
|
|
50
|
-
run<T>(items: any[], processor: (item: any, index: number) => Promise<T>, options?:
|
|
51
|
-
priority?: number;
|
|
52
|
-
retries?: number;
|
|
53
|
-
}): Promise<RunnerResult<T>>;
|
|
60
|
+
run<T>(items: any[], processor: (item: any, index: number) => Promise<T>, options?: RunnerRunOptions): Promise<RunnerResult<T>>;
|
|
54
61
|
private queueTask;
|
|
55
62
|
private scheduleRetry;
|
|
56
63
|
private processNext;
|
|
64
|
+
private cancelAll;
|
|
65
|
+
private resolveQueue;
|
|
66
|
+
private resolveTask;
|
|
57
67
|
getProgress(): {
|
|
58
68
|
total: number;
|
|
59
69
|
completed: number;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { QueueCancelledError } from '../core/errors.js';
|
|
1
2
|
class SimpleEmitter {
|
|
2
3
|
listeners = new Map();
|
|
3
4
|
on(event, listener) {
|
|
@@ -43,10 +44,14 @@ export class RequestRunner extends SimpleEmitter {
|
|
|
43
44
|
queue = [];
|
|
44
45
|
activeCount = 0;
|
|
45
46
|
paused = false;
|
|
46
|
-
results = new Map();
|
|
47
47
|
stats = { total: 0, successful: 0, failed: 0 };
|
|
48
48
|
startTime = 0;
|
|
49
49
|
pendingRetries = 0;
|
|
50
|
+
isCancelled = false;
|
|
51
|
+
cancelReason = new QueueCancelledError('Request runner cancelled');
|
|
52
|
+
retryTimers = new Map();
|
|
53
|
+
timeoutId;
|
|
54
|
+
abortUnsubscribe;
|
|
50
55
|
constructor(options = {}) {
|
|
51
56
|
super();
|
|
52
57
|
this.concurrency = options.concurrency || 5;
|
|
@@ -67,48 +72,146 @@ export class RequestRunner extends SimpleEmitter {
|
|
|
67
72
|
this.processNext();
|
|
68
73
|
}
|
|
69
74
|
async run(items, processor, options = {}) {
|
|
75
|
+
this.queue = [];
|
|
76
|
+
this.activeCount = 0;
|
|
77
|
+
this.pendingRetries = 0;
|
|
78
|
+
this.stats = { total: 0, successful: 0, failed: 0 };
|
|
79
|
+
this.isCancelled = false;
|
|
80
|
+
if (this.retryTimers.size > 0) {
|
|
81
|
+
for (const [, timer] of this.retryTimers) {
|
|
82
|
+
clearTimeout(timer);
|
|
83
|
+
}
|
|
84
|
+
this.retryTimers.clear();
|
|
85
|
+
}
|
|
86
|
+
if (this.timeoutId) {
|
|
87
|
+
clearTimeout(this.timeoutId);
|
|
88
|
+
this.timeoutId = undefined;
|
|
89
|
+
}
|
|
90
|
+
if (this.abortUnsubscribe) {
|
|
91
|
+
this.abortUnsubscribe();
|
|
92
|
+
this.abortUnsubscribe = undefined;
|
|
93
|
+
}
|
|
70
94
|
this.startTime = Date.now();
|
|
71
95
|
this.stats = { total: items.length, successful: 0, failed: 0 };
|
|
72
|
-
this.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
96
|
+
this.isCancelled = false;
|
|
97
|
+
this.cancelReason = new QueueCancelledError('Request runner cancelled', {
|
|
98
|
+
queueName: 'request-runner',
|
|
99
|
+
request: undefined
|
|
100
|
+
});
|
|
101
|
+
if (options.signal) {
|
|
102
|
+
const signal = options.signal;
|
|
103
|
+
if (signal.aborted) {
|
|
104
|
+
const reason = signal.reason instanceof Error
|
|
105
|
+
? signal.reason
|
|
106
|
+
: new QueueCancelledError('Request runner signal was aborted', {
|
|
107
|
+
queueName: 'request-runner',
|
|
108
|
+
request: undefined
|
|
109
|
+
});
|
|
110
|
+
this.cancelAll(reason);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
const handleAbort = () => {
|
|
114
|
+
const reason = signal.reason instanceof Error
|
|
115
|
+
? signal.reason
|
|
116
|
+
: new QueueCancelledError('Request runner signal was aborted', {
|
|
117
|
+
queueName: 'request-runner',
|
|
118
|
+
request: undefined
|
|
119
|
+
});
|
|
120
|
+
this.cancelAll(reason);
|
|
121
|
+
};
|
|
122
|
+
signal.addEventListener('abort', handleAbort, { once: true });
|
|
123
|
+
this.abortUnsubscribe = () => signal.removeEventListener('abort', handleAbort);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (options.deadlineMs !== undefined) {
|
|
127
|
+
const deadline = options.deadlineMs;
|
|
128
|
+
if (deadline <= 0) {
|
|
129
|
+
this.cancelAll(new QueueCancelledError('Request runner deadline elapsed', {
|
|
130
|
+
queueName: 'request-runner',
|
|
131
|
+
request: undefined
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
this.timeoutId = setTimeout(() => {
|
|
136
|
+
this.cancelAll(new QueueCancelledError('Request runner deadline exceeded', {
|
|
137
|
+
queueName: 'request-runner',
|
|
138
|
+
request: undefined
|
|
139
|
+
}));
|
|
140
|
+
}, deadline);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const promises = items.map((item, index) => {
|
|
145
|
+
return new Promise((resolve) => {
|
|
146
|
+
this.add(() => processor(item, index), {
|
|
147
|
+
priority: options.priority,
|
|
148
|
+
id: String(index),
|
|
149
|
+
retries: options.retries,
|
|
150
|
+
resolve,
|
|
151
|
+
trackTotal: false
|
|
152
|
+
});
|
|
81
153
|
});
|
|
82
154
|
});
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
155
|
+
const results = await Promise.all(promises);
|
|
156
|
+
return {
|
|
157
|
+
results,
|
|
158
|
+
stats: {
|
|
159
|
+
...this.stats,
|
|
160
|
+
duration: Date.now() - this.startTime
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
finally {
|
|
165
|
+
if (this.timeoutId) {
|
|
166
|
+
clearTimeout(this.timeoutId);
|
|
167
|
+
this.timeoutId = undefined;
|
|
90
168
|
}
|
|
91
|
-
|
|
169
|
+
if (this.abortUnsubscribe) {
|
|
170
|
+
this.abortUnsubscribe();
|
|
171
|
+
this.abortUnsubscribe = undefined;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
92
174
|
}
|
|
93
175
|
queueTask(task) {
|
|
94
176
|
this.queue.push(task);
|
|
95
177
|
this.queue.sort((a, b) => b.priority - a.priority);
|
|
96
178
|
}
|
|
97
179
|
scheduleRetry(task, delay) {
|
|
98
|
-
this.
|
|
99
|
-
|
|
100
|
-
setTimeout(() => {
|
|
101
|
-
this.pendingRetries--;
|
|
102
|
-
this.queueTask(task);
|
|
103
|
-
this.processNext();
|
|
104
|
-
}, delay);
|
|
180
|
+
if (this.isCancelled) {
|
|
181
|
+
this.resolveTask(task, this.cancelReason);
|
|
105
182
|
return;
|
|
106
183
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
184
|
+
if (delay <= 0) {
|
|
185
|
+
this.queueTask(task);
|
|
186
|
+
this.processNext();
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const timerKey = `${task.id}-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
|
|
190
|
+
const enqueueTask = () => {
|
|
191
|
+
this.retryTimers.delete(timerKey);
|
|
192
|
+
this.pendingRetries--;
|
|
193
|
+
if (this.isCancelled) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
this.queueTask(task);
|
|
197
|
+
this.processNext();
|
|
198
|
+
};
|
|
199
|
+
this.retryTimers.set(timerKey, setTimeout(() => {
|
|
200
|
+
if (this.isCancelled) {
|
|
201
|
+
this.pendingRetries--;
|
|
202
|
+
this.retryTimers.delete(timerKey);
|
|
203
|
+
this.resolveTask(task, this.cancelReason);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
enqueueTask();
|
|
207
|
+
}, delay));
|
|
208
|
+
this.pendingRetries++;
|
|
110
209
|
}
|
|
111
210
|
async processNext() {
|
|
211
|
+
if (this.isCancelled) {
|
|
212
|
+
this.resolveQueue();
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
112
215
|
if (this.paused || this.activeCount >= this.concurrency || this.queue.length === 0) {
|
|
113
216
|
return;
|
|
114
217
|
}
|
|
@@ -124,6 +227,10 @@ export class RequestRunner extends SimpleEmitter {
|
|
|
124
227
|
this.emit('taskComplete', { task, result });
|
|
125
228
|
}
|
|
126
229
|
catch (error) {
|
|
230
|
+
if (this.isCancelled) {
|
|
231
|
+
this.resolveTask(task, this.cancelReason);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
127
234
|
const remaining = task.retries ?? 0;
|
|
128
235
|
if (remaining > 0) {
|
|
129
236
|
task.retries = remaining - 1;
|
|
@@ -131,8 +238,7 @@ export class RequestRunner extends SimpleEmitter {
|
|
|
131
238
|
this.scheduleRetry(task, this.retryDelay);
|
|
132
239
|
}
|
|
133
240
|
else {
|
|
134
|
-
this.
|
|
135
|
-
task.resolve?.(error);
|
|
241
|
+
this.resolveTask(task, error);
|
|
136
242
|
this.emit('taskError', { task, error });
|
|
137
243
|
}
|
|
138
244
|
}
|
|
@@ -145,6 +251,34 @@ export class RequestRunner extends SimpleEmitter {
|
|
|
145
251
|
this.processNext();
|
|
146
252
|
}
|
|
147
253
|
}
|
|
254
|
+
cancelAll(reason) {
|
|
255
|
+
if (this.isCancelled) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
this.isCancelled = true;
|
|
259
|
+
this.cancelReason = reason;
|
|
260
|
+
for (const [, timer] of this.retryTimers) {
|
|
261
|
+
clearTimeout(timer);
|
|
262
|
+
}
|
|
263
|
+
this.retryTimers.clear();
|
|
264
|
+
this.pendingRetries = 0;
|
|
265
|
+
this.resolveQueue();
|
|
266
|
+
}
|
|
267
|
+
resolveQueue() {
|
|
268
|
+
while (this.queue.length > 0) {
|
|
269
|
+
const task = this.queue.shift();
|
|
270
|
+
if (task) {
|
|
271
|
+
this.resolveTask(task, this.cancelReason);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
resolveTask(task, error) {
|
|
276
|
+
if (!task.resolve) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
this.stats.failed++;
|
|
280
|
+
task.resolve?.(error);
|
|
281
|
+
}
|
|
148
282
|
getProgress() {
|
|
149
283
|
const completed = this.stats.successful + this.stats.failed;
|
|
150
284
|
return {
|
|
@@ -33,11 +33,16 @@ export default class HTMLElement extends Node {
|
|
|
33
33
|
private voidTag;
|
|
34
34
|
private _attrs?;
|
|
35
35
|
private _rawAttrs?;
|
|
36
|
+
private _queryCache?;
|
|
36
37
|
private _parseOptions;
|
|
37
38
|
rawTagName: string;
|
|
38
39
|
id: string;
|
|
39
40
|
classList: DOMTokenList;
|
|
40
41
|
nodeType: NodeType;
|
|
42
|
+
private get isSelectorCacheEnabled();
|
|
43
|
+
private getQueryCache;
|
|
44
|
+
private clearQueryCache;
|
|
45
|
+
invalidateSelectorCacheRecursively(): void;
|
|
41
46
|
private quoteAttribute;
|
|
42
47
|
constructor(tagName: string, keyAttrs: KeyAttributes, rawAttrs?: string, parentNode?: HTMLElement | null, range?: [number, number], voidTag?: VoidTag, _parseOptions?: Partial<Options>);
|
|
43
48
|
removeChild(node: Node): this;
|
|
@@ -104,6 +109,7 @@ export interface Options {
|
|
|
104
109
|
tags?: string[];
|
|
105
110
|
closingSlash?: boolean;
|
|
106
111
|
};
|
|
112
|
+
selectorCache?: boolean;
|
|
107
113
|
}
|
|
108
114
|
export declare function base_parse(data: string, options?: Partial<Options>): HTMLElement[];
|
|
109
115
|
export declare function parse(data: string, options?: Partial<Options>): HTMLElement;
|