@crawlee/core 3.17.1-beta.22 → 3.17.1-beta.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -5
- package/router.d.ts +55 -14
- package/router.js +25 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crawlee/core",
|
|
3
|
-
"version": "3.17.1-beta.
|
|
3
|
+
"version": "3.17.1-beta.24",
|
|
4
4
|
"description": "The scalable web crawling and scraping library for JavaScript/Node.js. Enables development of data extraction and web automation jobs (not only) with headless Chrome and Puppeteer.",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=16.0.0"
|
|
@@ -59,9 +59,9 @@
|
|
|
59
59
|
"@apify/pseudo_url": "^2.0.30",
|
|
60
60
|
"@apify/timeout": "^0.3.0",
|
|
61
61
|
"@apify/utilities": "^2.7.10",
|
|
62
|
-
"@crawlee/memory-storage": "3.17.1-beta.
|
|
63
|
-
"@crawlee/types": "3.17.1-beta.
|
|
64
|
-
"@crawlee/utils": "3.17.1-beta.
|
|
62
|
+
"@crawlee/memory-storage": "3.17.1-beta.24",
|
|
63
|
+
"@crawlee/types": "3.17.1-beta.24",
|
|
64
|
+
"@crawlee/utils": "3.17.1-beta.24",
|
|
65
65
|
"@sapphire/async-queue": "^1.5.1",
|
|
66
66
|
"@vladfrangu/async_event_emitter": "^2.2.2",
|
|
67
67
|
"csv-stringify": "^6.2.0",
|
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
},
|
|
86
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "187d1180d326bac968c64807edf90aa590f8aceb"
|
|
87
87
|
}
|
package/router.d.ts
CHANGED
|
@@ -2,13 +2,26 @@ import type { Dictionary } from '@crawlee/types';
|
|
|
2
2
|
import type { CrawlingContext, LoadedRequest, RestrictedCrawlingContext } from './crawlers/crawler_commons';
|
|
3
3
|
import type { Request } from './request';
|
|
4
4
|
import type { Awaitable } from './typedefs';
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The crawling context received by a route handler, with `request.userData` narrowed to `UserData`.
|
|
7
|
+
*/
|
|
8
|
+
export type RouterHandlerContext<Context, UserData extends Dictionary> = Omit<Context, 'request'> & {
|
|
9
|
+
request: LoadedRequest<Request<UserData>>;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* The set of labels accepted by {@link Router.addHandler}. When the router declares a concrete
|
|
13
|
+
* route map (e.g. `{ PRODUCT: ...; CATEGORY: ... }`), only those labels (plus symbols) are
|
|
14
|
+
* allowed — unknown labels become a compile-time error. When the map is left open (the default
|
|
15
|
+
* `Record<string, ...>`), any string or symbol label is accepted, preserving the original behaviour.
|
|
16
|
+
*/
|
|
17
|
+
export type RouterLabel<Routes extends Record<keyof Routes, Dictionary>> = string extends keyof Routes ? string | symbol : (keyof Routes & string) | symbol;
|
|
18
|
+
export interface RouterHandler<Context extends Omit<RestrictedCrawlingContext, 'enqueueLinks'> = CrawlingContext, Routes extends Record<keyof Routes, Dictionary> = Record<string, GetUserDataFromRequest<Context['request']>>> extends Router<Context, Routes> {
|
|
6
19
|
(ctx: Context): Awaitable<void>;
|
|
7
20
|
}
|
|
8
21
|
export type GetUserDataFromRequest<T> = T extends Request<infer Y> ? Y : never;
|
|
9
|
-
export type RouterRoutes<Context,
|
|
10
|
-
[
|
|
11
|
-
request: Request<
|
|
22
|
+
export type RouterRoutes<Context, Routes extends Record<keyof Routes, Dictionary>> = {
|
|
23
|
+
[Label in keyof Routes]: (ctx: Omit<Context, 'request'> & {
|
|
24
|
+
request: Request<Routes[Label]>;
|
|
12
25
|
}) => Awaitable<void>;
|
|
13
26
|
};
|
|
14
27
|
/**
|
|
@@ -75,8 +88,30 @@ export type RouterRoutes<Context, UserData extends Dictionary> = {
|
|
|
75
88
|
* ctx.log.info('...');
|
|
76
89
|
* });
|
|
77
90
|
* ```
|
|
91
|
+
*
|
|
92
|
+
* To get `request.userData` typed per label, declare a route map and pass it as the second
|
|
93
|
+
* type argument. The label passed to {@link Router.addHandler} then drives the type of
|
|
94
|
+
* `request.userData`, and unknown labels are rejected at compile time:
|
|
95
|
+
*
|
|
96
|
+
* ```ts
|
|
97
|
+
* import { createCheerioRouter, CheerioCrawlingContext } from 'crawlee';
|
|
98
|
+
*
|
|
99
|
+
* interface Routes {
|
|
100
|
+
* PRODUCT: { sku: string; price: number };
|
|
101
|
+
* CATEGORY: { categoryId: string };
|
|
102
|
+
* }
|
|
103
|
+
*
|
|
104
|
+
* const router = createCheerioRouter<CheerioCrawlingContext, Routes>();
|
|
105
|
+
*
|
|
106
|
+
* router.addHandler('PRODUCT', async ({ request }) => {
|
|
107
|
+
* request.userData.sku; // string
|
|
108
|
+
* request.userData.price; // number
|
|
109
|
+
* });
|
|
110
|
+
*
|
|
111
|
+
* router.addHandler('TYPO', async () => {}); // compile error: not a known label
|
|
112
|
+
* ```
|
|
78
113
|
*/
|
|
79
|
-
export declare class Router<Context extends Omit<RestrictedCrawlingContext, 'enqueueLinks'
|
|
114
|
+
export declare class Router<Context extends Omit<RestrictedCrawlingContext, 'enqueueLinks'>, Routes extends Record<keyof Routes, Dictionary> = Record<string, GetUserDataFromRequest<Context['request']>>> {
|
|
80
115
|
private readonly routes;
|
|
81
116
|
private readonly middlewares;
|
|
82
117
|
/**
|
|
@@ -85,17 +120,22 @@ export declare class Router<Context extends Omit<RestrictedCrawlingContext, 'enq
|
|
|
85
120
|
*/
|
|
86
121
|
protected constructor();
|
|
87
122
|
/**
|
|
88
|
-
* Registers new route handler for given label.
|
|
123
|
+
* Registers new route handler for given label. When the router declares a route map, the
|
|
124
|
+
* `label` is restricted to the declared labels and `request.userData` is typed accordingly.
|
|
125
|
+
*/
|
|
126
|
+
addHandler<Label extends keyof Routes & string>(label: Label, handler: (ctx: RouterHandlerContext<Context, Routes[Label]>) => Awaitable<void>): void;
|
|
127
|
+
/**
|
|
128
|
+
* Registers new route handler for given label, explicitly typing `request.userData` via the
|
|
129
|
+
* `UserData` type argument. Useful when the router has no declared route map (the open default)
|
|
130
|
+
* and you want to type a single handler, or to register a handler under a `symbol` label.
|
|
89
131
|
*/
|
|
90
|
-
addHandler<UserData extends Dictionary = GetUserDataFromRequest<Context['request']>>(label:
|
|
91
|
-
request: LoadedRequest<Request<UserData>>;
|
|
92
|
-
}) => Awaitable<void>): void;
|
|
132
|
+
addHandler<UserData extends Dictionary = GetUserDataFromRequest<Context['request']>>(label: RouterLabel<Routes>, handler: (ctx: RouterHandlerContext<Context, UserData>) => Awaitable<void>): void;
|
|
93
133
|
/**
|
|
94
|
-
* Registers default route handler.
|
|
134
|
+
* Registers default route handler. As a fallback it can receive any request (including labels not
|
|
135
|
+
* declared in the route map), so `request.userData` defaults to the context's `userData` type
|
|
136
|
+
* (loosely typed by default). Pass an explicit `UserData` type argument to narrow it.
|
|
95
137
|
*/
|
|
96
|
-
addDefaultHandler<UserData extends Dictionary = GetUserDataFromRequest<Context['request']>>(handler: (ctx:
|
|
97
|
-
request: LoadedRequest<Request<UserData>>;
|
|
98
|
-
}) => Awaitable<void>): void;
|
|
138
|
+
addDefaultHandler<UserData extends Dictionary = GetUserDataFromRequest<Context['request']>>(handler: (ctx: RouterHandlerContext<Context, UserData>) => Awaitable<void>): void;
|
|
99
139
|
/**
|
|
100
140
|
* Registers a middleware that will be fired before the matching route handler.
|
|
101
141
|
* Multiple middlewares can be registered, they will be fired in the same order.
|
|
@@ -129,5 +169,6 @@ export declare class Router<Context extends Omit<RestrictedCrawlingContext, 'enq
|
|
|
129
169
|
* await crawler.run();
|
|
130
170
|
* ```
|
|
131
171
|
*/
|
|
132
|
-
static create<Context extends Omit<RestrictedCrawlingContext, 'enqueueLinks'> = CrawlingContext,
|
|
172
|
+
static create<Context extends Omit<RestrictedCrawlingContext, 'enqueueLinks'> = CrawlingContext, Routes extends Record<keyof Routes, Dictionary> = Record<string, GetUserDataFromRequest<Context['request']>>>(routes?: RouterRoutes<Context, Routes>): RouterHandler<Context, Routes>;
|
|
173
|
+
static create<Context extends Omit<RestrictedCrawlingContext, 'enqueueLinks'> = CrawlingContext, UserData extends Dictionary = GetUserDataFromRequest<Context['request']>>(routes?: RouterRoutes<Context, Record<string, UserData>>): RouterHandler<Context, Record<string, UserData>>;
|
|
133
174
|
}
|
package/router.js
CHANGED
|
@@ -67,6 +67,28 @@ const defaultRoute = Symbol('default-route');
|
|
|
67
67
|
* ctx.log.info('...');
|
|
68
68
|
* });
|
|
69
69
|
* ```
|
|
70
|
+
*
|
|
71
|
+
* To get `request.userData` typed per label, declare a route map and pass it as the second
|
|
72
|
+
* type argument. The label passed to {@link Router.addHandler} then drives the type of
|
|
73
|
+
* `request.userData`, and unknown labels are rejected at compile time:
|
|
74
|
+
*
|
|
75
|
+
* ```ts
|
|
76
|
+
* import { createCheerioRouter, CheerioCrawlingContext } from 'crawlee';
|
|
77
|
+
*
|
|
78
|
+
* interface Routes {
|
|
79
|
+
* PRODUCT: { sku: string; price: number };
|
|
80
|
+
* CATEGORY: { categoryId: string };
|
|
81
|
+
* }
|
|
82
|
+
*
|
|
83
|
+
* const router = createCheerioRouter<CheerioCrawlingContext, Routes>();
|
|
84
|
+
*
|
|
85
|
+
* router.addHandler('PRODUCT', async ({ request }) => {
|
|
86
|
+
* request.userData.sku; // string
|
|
87
|
+
* request.userData.price; // number
|
|
88
|
+
* });
|
|
89
|
+
*
|
|
90
|
+
* router.addHandler('TYPO', async () => {}); // compile error: not a known label
|
|
91
|
+
* ```
|
|
70
92
|
*/
|
|
71
93
|
class Router {
|
|
72
94
|
/**
|
|
@@ -87,15 +109,14 @@ class Router {
|
|
|
87
109
|
value: []
|
|
88
110
|
});
|
|
89
111
|
}
|
|
90
|
-
/**
|
|
91
|
-
* Registers new route handler for given label.
|
|
92
|
-
*/
|
|
93
112
|
addHandler(label, handler) {
|
|
94
113
|
this.validate(label);
|
|
95
114
|
this.routes.set(label, handler);
|
|
96
115
|
}
|
|
97
116
|
/**
|
|
98
|
-
* Registers default route handler.
|
|
117
|
+
* Registers default route handler. As a fallback it can receive any request (including labels not
|
|
118
|
+
* declared in the route map), so `request.userData` defaults to the context's `userData` type
|
|
119
|
+
* (loosely typed by default). Pass an explicit `UserData` type argument to narrow it.
|
|
99
120
|
*/
|
|
100
121
|
addDefaultHandler(handler) {
|
|
101
122
|
this.validate(defaultRoute);
|
|
@@ -133,26 +154,6 @@ class Router {
|
|
|
133
154
|
throw new Error(message);
|
|
134
155
|
}
|
|
135
156
|
}
|
|
136
|
-
/**
|
|
137
|
-
* Creates new router instance. This instance can then serve as a `requestHandler` of your crawler.
|
|
138
|
-
*
|
|
139
|
-
* ```ts
|
|
140
|
-
* import { Router, CheerioCrawler, CheerioCrawlingContext } from 'crawlee';
|
|
141
|
-
*
|
|
142
|
-
* const router = Router.create<CheerioCrawlingContext>();
|
|
143
|
-
* router.addHandler('label-a', async (ctx) => {
|
|
144
|
-
* ctx.log.info('...');
|
|
145
|
-
* });
|
|
146
|
-
* router.addDefaultHandler(async (ctx) => {
|
|
147
|
-
* ctx.log.info('...');
|
|
148
|
-
* });
|
|
149
|
-
*
|
|
150
|
-
* const crawler = new CheerioCrawler({
|
|
151
|
-
* requestHandler: router,
|
|
152
|
-
* });
|
|
153
|
-
* await crawler.run();
|
|
154
|
-
* ```
|
|
155
|
-
*/
|
|
156
157
|
static create(routes) {
|
|
157
158
|
const router = new Router();
|
|
158
159
|
const obj = Object.create(Function.prototype);
|