@crawlee/basic 4.0.0-beta.184 → 4.0.0-beta.185
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.
|
@@ -743,7 +743,7 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
|
|
|
743
743
|
private isTaskReadyFunction;
|
|
744
744
|
/**
|
|
745
745
|
* Unwraps errors thrown by the context pipeline to get the actual user error.
|
|
746
|
-
* RequestHandlerError and
|
|
746
|
+
* RequestHandlerError and ContextPipelineCleanupError wrap the actual error.
|
|
747
747
|
*/
|
|
748
748
|
private unwrapError;
|
|
749
749
|
/**
|
|
@@ -13,7 +13,7 @@ import { ConcurrencySystem } from './autoscaling/concurrency_system.js';
|
|
|
13
13
|
import { mergeCookies } from './cookie_utils.js';
|
|
14
14
|
import { ContextPipeline } from './crawlers/context_pipeline.js';
|
|
15
15
|
import { CrawlingRequest, RequestState } from './crawling_request.js';
|
|
16
|
-
import { ContextPipelineCleanupError,
|
|
16
|
+
import { ContextPipelineCleanupError, ContextPipelineInterruptedError, MissingSessionError, NavigationSkippedError, PersistentRateLimitError, RequestHandlerError, RequestThrottledError, RetryRequestError, } from './errors.js';
|
|
17
17
|
import { Statistics } from './crawlers/statistics.js';
|
|
18
18
|
import { applyRequestTransform, buildEnqueueStrategyPatterns, constructUrlPatternObjects, createRequestOptions, createSkippedRequestArgs, filterRequestOptionsByPatterns, } from './enqueue_links/index.js';
|
|
19
19
|
import { parseRetryAfterHeader } from './http.js';
|
|
@@ -54,7 +54,6 @@ class LazyDefaultHttpClient extends BaseHttpClient {
|
|
|
54
54
|
* @ignore
|
|
55
55
|
*/
|
|
56
56
|
const SAFE_MIGRATION_WAIT_MILLIS = 20000;
|
|
57
|
-
const deferredCleanupKey = Symbol('deferredCleanup');
|
|
58
57
|
// The request timeout plumbing (the window helper, the context symbols, and the race) lives in its own module.
|
|
59
58
|
export { navigationDeadlineKey, remainingNavigationWindowMillis } from './request-timeout.js';
|
|
60
59
|
const urlPatternSchema = z.union([
|
|
@@ -463,6 +462,28 @@ export class BasicCrawler {
|
|
|
463
462
|
// matching `recordRequestStart`.
|
|
464
463
|
this.statistics.recordRequestStart(request.id || request.uniqueKey);
|
|
465
464
|
const crawlingContext = { request };
|
|
465
|
+
// Runs inside the pipeline call, so the deferred cleanups registered by earlier middlewares
|
|
466
|
+
// (e.g. closing the page) only fire after the error handler has seen the partial context.
|
|
467
|
+
const handleInitializationError = async (rawError) => {
|
|
468
|
+
// Roll back *before* the error handler runs, for the same reason `handleRequest` does.
|
|
469
|
+
currentStorageTransaction()?.rollback();
|
|
470
|
+
// ContextPipelineInterruptedError means the request was intentionally skipped
|
|
471
|
+
// (e.g., doesn't match enqueue strategy after redirect). Just return gracefully.
|
|
472
|
+
if (rawError instanceof ContextPipelineInterruptedError) {
|
|
473
|
+
this.statistics.discardRequestRecord(request.id || request.uniqueKey);
|
|
474
|
+
await this.timeoutAndRetry(async () => this.requestManager?.markRequestAsHandled(request), this.internalTimeoutMillis, `Marking request ${crawlingContext.request.url} (${crawlingContext.request.id}) as handled timed out after ${this.internalTimeoutMillis / 1e3} seconds.`);
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
// An error during pipeline initialization (e.g., navigation timeout, session/proxy error,
|
|
478
|
+
// i.e. not in user's requestHandler) goes through the normal error flow.
|
|
479
|
+
const error = this.unwrapError(rawError);
|
|
480
|
+
await this.requestFunctionErrorHandler(error, crawlingContext, request, this.requestManager);
|
|
481
|
+
// SessionError already retired the session in `requestFunctionErrorHandler`;
|
|
482
|
+
// skip `markBad` to avoid double-counting usage/error score.
|
|
483
|
+
if (!this.errorAbsolvesSession(error)) {
|
|
484
|
+
crawlingContext.session?.markBad();
|
|
485
|
+
}
|
|
486
|
+
};
|
|
466
487
|
try {
|
|
467
488
|
// The transaction spans the whole pipeline call, covering the navigation hooks
|
|
468
489
|
// and `extendContext` too; `handleRequest` drives its outcome explicitly.
|
|
@@ -472,27 +493,15 @@ export class BasicCrawler {
|
|
|
472
493
|
// catch-all for that - see `raceWithTimeout` for why it is a bare timer, not a timeout frame.
|
|
473
494
|
await this.withRequestTimeout(crawlingContext, this.basicContextPipeline
|
|
474
495
|
.chain(this.contextPipeline)
|
|
475
|
-
.call(crawlingContext, (ctx) => this.handleRequest(ctx, source, request))));
|
|
496
|
+
.call(crawlingContext, (ctx) => this.handleRequest(ctx, source, request), handleInitializationError)));
|
|
476
497
|
}
|
|
477
498
|
catch (error) {
|
|
478
|
-
//
|
|
479
|
-
//
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
await this.timeoutAndRetry(async () => this.requestManager?.markRequestAsHandled(request), this.internalTimeoutMillis, `Marking request ${crawlingContext.request.url} (${crawlingContext.request.id}) as handled timed out after ${this.internalTimeoutMillis / 1e3} seconds.`);
|
|
483
|
-
return;
|
|
484
|
-
}
|
|
485
|
-
// If the error happened during pipeline initialization (e.g., navigation timeout, session/proxy error,
|
|
486
|
-
// i.e. not in user's requestHandler), handle it through the normal error flow. A bare `TimeoutError`
|
|
487
|
-
// here is the internal timeout above firing - anything else thrown inside the pipeline arrives wrapped.
|
|
488
|
-
const isPipelineError = error instanceof ContextPipelineInitializationError ||
|
|
489
|
-
error instanceof SessionError ||
|
|
490
|
-
error instanceof TimeoutError;
|
|
491
|
-
if (isPipelineError) {
|
|
499
|
+
// A bare `TimeoutError` is the internal timeout above firing; a `SessionError` can only be a
|
|
500
|
+
// secondary error from `handleRequest` - both take the normal error flow. Anything else thrown
|
|
501
|
+
// inside the pipeline arrives wrapped.
|
|
502
|
+
if (error instanceof SessionError || error instanceof TimeoutError) {
|
|
492
503
|
const unwrappedError = this.unwrapError(error);
|
|
493
504
|
await this.requestFunctionErrorHandler(unwrappedError, crawlingContext, request, this.requestManager);
|
|
494
|
-
// SessionError already retired the session in `requestFunctionErrorHandler`;
|
|
495
|
-
// skip `markBad` to avoid double-counting usage/error score.
|
|
496
505
|
if (!this.errorAbsolvesSession(unwrappedError)) {
|
|
497
506
|
crawlingContext.session?.markBad();
|
|
498
507
|
}
|
|
@@ -500,11 +509,6 @@ export class BasicCrawler {
|
|
|
500
509
|
}
|
|
501
510
|
throw this.unwrapError(error);
|
|
502
511
|
}
|
|
503
|
-
finally {
|
|
504
|
-
// Run request-scoped deferred cleanups only after the whole request lifecycle - including the user's error handler - has finished.
|
|
505
|
-
const deferredCleanup = crawlingContext[deferredCleanupKey] ?? [];
|
|
506
|
-
await Promise.all(deferredCleanup.map((fn) => fn().catch((cleanupError) => this.log.debug('Error in deferred cleanup', { error: cleanupError }))));
|
|
507
|
-
}
|
|
508
512
|
},
|
|
509
513
|
isTaskReadyFunction: async () => {
|
|
510
514
|
if (isMaxPagesExceeded()) {
|
|
@@ -594,10 +598,10 @@ export class BasicCrawler {
|
|
|
594
598
|
*/
|
|
595
599
|
buildBasicContextPipeline() {
|
|
596
600
|
return ContextPipeline.create()
|
|
597
|
-
.compose(
|
|
598
|
-
.compose(
|
|
599
|
-
.compose(
|
|
600
|
-
.compose(
|
|
601
|
+
.compose(this.checkRobotsTxt.bind(this))
|
|
602
|
+
.compose(this.createBaseContext.bind(this))
|
|
603
|
+
.compose(this.resolveSession.bind(this))
|
|
604
|
+
.compose(this.createContextHelpers.bind(this));
|
|
601
605
|
}
|
|
602
606
|
async checkRobotsTxt({ request }) {
|
|
603
607
|
if (!(await this.isAllowedBasedOnRobotsTxtFile(request.url))) {
|
|
@@ -619,8 +623,7 @@ export class BasicCrawler {
|
|
|
619
623
|
buildContextPipeline() {
|
|
620
624
|
return ContextPipeline.create();
|
|
621
625
|
}
|
|
622
|
-
createBaseContext(context) {
|
|
623
|
-
const deferredCleanup = [];
|
|
626
|
+
createBaseContext(context, onCleanup) {
|
|
624
627
|
return {
|
|
625
628
|
id: cryptoRandomObjectId(10),
|
|
626
629
|
log: this.log,
|
|
@@ -628,7 +631,14 @@ export class BasicCrawler {
|
|
|
628
631
|
useState: this.useState.bind(this),
|
|
629
632
|
getKeyValueStore: async (identifier) => KeyValueStore.open(identifier),
|
|
630
633
|
registerDeferredCleanup: (cleanup) => {
|
|
631
|
-
|
|
634
|
+
onCleanup(async () => {
|
|
635
|
+
try {
|
|
636
|
+
await cleanup();
|
|
637
|
+
}
|
|
638
|
+
catch (error) {
|
|
639
|
+
this.log.debug('Error in deferred cleanup', { error });
|
|
640
|
+
}
|
|
641
|
+
});
|
|
632
642
|
},
|
|
633
643
|
afterStorageCommit: (callback) => {
|
|
634
644
|
const transaction = currentStorageTransaction();
|
|
@@ -651,7 +661,6 @@ export class BasicCrawler {
|
|
|
651
661
|
context[navigationDeadlineKey] += extraMillis;
|
|
652
662
|
}
|
|
653
663
|
},
|
|
654
|
-
[deferredCleanupKey]: deferredCleanup,
|
|
655
664
|
};
|
|
656
665
|
}
|
|
657
666
|
/**
|
|
@@ -673,7 +682,7 @@ export class BasicCrawler {
|
|
|
673
682
|
const session = await this.timeoutAndRetry(async () => {
|
|
674
683
|
const existingSession = await this.sessionPool.getSession(request.sessionId);
|
|
675
684
|
if (!existingSession) {
|
|
676
|
-
throw new
|
|
685
|
+
throw new MissingSessionError(request.sessionId);
|
|
677
686
|
}
|
|
678
687
|
return existingSession;
|
|
679
688
|
}, this.internalTimeoutMillis, `Fetching session timed out after ${this.internalTimeoutMillis / 1e3} seconds.`);
|
|
@@ -705,26 +714,24 @@ export class BasicCrawler {
|
|
|
705
714
|
let contextPipeline;
|
|
706
715
|
if (extendContext !== undefined) {
|
|
707
716
|
contextPipeline = ContextPipeline.create()
|
|
708
|
-
.compose(
|
|
717
|
+
.compose(async (context) => await extendContext(context))
|
|
709
718
|
.chain(subclassPipeline);
|
|
710
719
|
}
|
|
711
720
|
else {
|
|
712
721
|
contextPipeline = subclassPipeline;
|
|
713
722
|
}
|
|
714
|
-
contextPipeline = contextPipeline.compose({
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
return context;
|
|
727
|
-
},
|
|
723
|
+
contextPipeline = contextPipeline.compose(async (context) => {
|
|
724
|
+
const { request } = context;
|
|
725
|
+
if (request && !this.requestMatchesEnqueueStrategy(request)) {
|
|
726
|
+
// eslint-disable-next-line dot-notation
|
|
727
|
+
const message = `Skipping request ${request.id} (starting url: ${request.url} -> loaded url: ${request.loadedUrl}) because it does not match the enqueue strategy (${request['enqueueStrategy']}).`;
|
|
728
|
+
this.log.debug(message);
|
|
729
|
+
request.noRetry = true;
|
|
730
|
+
request.state = RequestState.SKIPPED;
|
|
731
|
+
await this.#handleSkippedRequest({ request, reason: 'redirect' });
|
|
732
|
+
throw new ContextPipelineInterruptedError(message);
|
|
733
|
+
}
|
|
734
|
+
return context;
|
|
728
735
|
});
|
|
729
736
|
return contextPipeline;
|
|
730
737
|
}
|
|
@@ -1642,12 +1649,10 @@ export class BasicCrawler {
|
|
|
1642
1649
|
}
|
|
1643
1650
|
/**
|
|
1644
1651
|
* Unwraps errors thrown by the context pipeline to get the actual user error.
|
|
1645
|
-
* RequestHandlerError and
|
|
1652
|
+
* RequestHandlerError and ContextPipelineCleanupError wrap the actual error.
|
|
1646
1653
|
*/
|
|
1647
1654
|
unwrapError(error) {
|
|
1648
|
-
if (error instanceof RequestHandlerError ||
|
|
1649
|
-
error instanceof ContextPipelineInitializationError ||
|
|
1650
|
-
error instanceof ContextPipelineCleanupError) {
|
|
1655
|
+
if (error instanceof RequestHandlerError || error instanceof ContextPipelineCleanupError) {
|
|
1651
1656
|
return this.unwrapError(error.cause);
|
|
1652
1657
|
}
|
|
1653
1658
|
return error;
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import type { Awaitable } from '@crawlee/types';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Registers a callback that runs after the final context consumer finishes. `error` is the consumer's failure, if any;
|
|
4
|
+
* failures of downstream middlewares are not reported. Cleanups run in reverse registration order.
|
|
5
|
+
*/
|
|
6
|
+
export type CleanupRegistrar = (cleanup: (error?: unknown) => Awaitable<void>) => void;
|
|
7
|
+
/**
|
|
8
|
+
* A middleware step in the context pipeline: receives the context built so far and returns an extension that gets
|
|
9
|
+
* merged into it. Anything that needs tearing down is registered via `onCleanup`.
|
|
4
10
|
*
|
|
5
11
|
* @template TCrawlingContext - The input context type for this middleware
|
|
6
12
|
* @template TCrawlingContextExtension - The enhanced output context type
|
|
7
13
|
*/
|
|
8
|
-
export
|
|
9
|
-
/** The main middleware function that enhances the context */
|
|
10
|
-
action: (context: TCrawlingContext) => Awaitable<TCrawlingContextExtension>;
|
|
11
|
-
/** Optional cleanup function called after the consumer finishes or fails */
|
|
12
|
-
cleanup?: (context: TCrawlingContext & TCrawlingContextExtension, error?: unknown) => Awaitable<void>;
|
|
13
|
-
}
|
|
14
|
+
export type ContextMiddleware<TCrawlingContext, TCrawlingContextExtension> = (context: TCrawlingContext, onCleanup: CleanupRegistrar) => Awaitable<TCrawlingContextExtension>;
|
|
14
15
|
/**
|
|
15
16
|
* Encapsulates the logic of gradually enhancing the crawling context with additional information and utilities.
|
|
16
17
|
*
|
|
@@ -59,12 +60,12 @@ export declare abstract class ContextPipeline<TContextBase, TCrawlingContext ext
|
|
|
59
60
|
*
|
|
60
61
|
* @param crawlingContext - The initial context to process through the pipeline
|
|
61
62
|
* @param finalContextConsumer - The function that will receive the final enhanced context
|
|
63
|
+
* @param onInitializationError - Receives a middleware failure as-is; the consumer is then skipped. Runs before
|
|
64
|
+
* the cleanups, so it still sees whatever the failed middleware's predecessors set up.
|
|
62
65
|
*
|
|
63
|
-
* @throws {ContextPipelineInitializationError} When a middleware fails during initialization
|
|
64
|
-
* @throws {ContextPipelineInterruptedError} When the pipeline is intentionally interrupted during initialization
|
|
65
66
|
* @throws {RequestHandlerError} When the final context consumer throws an exception
|
|
66
67
|
* @throws {ContextPipelineCleanupError} When cleanup operations fail
|
|
67
68
|
* @throws {SessionError} Session errors are re-thrown as-is for special handling
|
|
68
69
|
*/
|
|
69
|
-
abstract call(crawlingContext: TContextBase, finalContextConsumer: (finalContext: TCrawlingContext) => Awaitable<unknown>): Promise<void>;
|
|
70
|
+
abstract call(crawlingContext: TContextBase, finalContextConsumer: (finalContext: TCrawlingContext) => Awaitable<unknown>, onInitializationError: (error: unknown) => Awaitable<void>): Promise<void>;
|
|
70
71
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { serviceLocator, SessionError } from '@crawlee/core';
|
|
2
|
-
import { ContextPipelineCleanupError,
|
|
2
|
+
import { ContextPipelineCleanupError, RequestHandlerError } from '../errors.js';
|
|
3
3
|
/**
|
|
4
4
|
* Encapsulates the logic of gradually enhancing the crawling context with additional information and utilities.
|
|
5
5
|
*
|
|
@@ -18,7 +18,7 @@ export class ContextPipeline {
|
|
|
18
18
|
* @returns A new ContextPipeline instance with no transformations
|
|
19
19
|
*/
|
|
20
20
|
static create() {
|
|
21
|
-
return new ContextPipelineImpl(
|
|
21
|
+
return new ContextPipelineImpl(async (context) => context);
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
@@ -57,14 +57,17 @@ class ContextPipelineImpl extends ContextPipeline {
|
|
|
57
57
|
/**
|
|
58
58
|
* @inheritdoc
|
|
59
59
|
*/
|
|
60
|
-
async call(crawlingContext, finalContextConsumer) {
|
|
60
|
+
async call(crawlingContext, finalContextConsumer, onInitializationError) {
|
|
61
61
|
const middlewares = Array.from(this.middlewareChain()).reverse();
|
|
62
62
|
const cleanupStack = [];
|
|
63
|
+
const onCleanup = (cleanup) => {
|
|
64
|
+
cleanupStack.push(cleanup);
|
|
65
|
+
};
|
|
63
66
|
let consumerException;
|
|
64
67
|
try {
|
|
65
|
-
for (const
|
|
68
|
+
for (const middleware of middlewares) {
|
|
66
69
|
try {
|
|
67
|
-
const contextExtension = await
|
|
70
|
+
const contextExtension = await middleware(crawlingContext, onCleanup);
|
|
68
71
|
const extensionNames = [
|
|
69
72
|
...Object.getOwnPropertyNames(contextExtension),
|
|
70
73
|
...Object.getOwnPropertySymbols(contextExtension),
|
|
@@ -81,18 +84,10 @@ class ContextPipelineImpl extends ContextPipeline {
|
|
|
81
84
|
.debug(`Context pipeline failed to define property ${key.toString()}:`, error);
|
|
82
85
|
}
|
|
83
86
|
}
|
|
84
|
-
if (cleanup) {
|
|
85
|
-
cleanupStack.push(cleanup);
|
|
86
|
-
}
|
|
87
87
|
}
|
|
88
88
|
catch (exception) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
if (exception instanceof ContextPipelineInterruptedError) {
|
|
93
|
-
throw exception;
|
|
94
|
-
}
|
|
95
|
-
throw new ContextPipelineInitializationError(exception);
|
|
89
|
+
await onInitializationError(exception);
|
|
90
|
+
return;
|
|
96
91
|
}
|
|
97
92
|
}
|
|
98
93
|
try {
|
|
@@ -110,7 +105,7 @@ class ContextPipelineImpl extends ContextPipeline {
|
|
|
110
105
|
finally {
|
|
111
106
|
try {
|
|
112
107
|
for (const cleanup of cleanupStack.reverse()) {
|
|
113
|
-
await cleanup(
|
|
108
|
+
await cleanup(consumerException);
|
|
114
109
|
}
|
|
115
110
|
}
|
|
116
111
|
catch (exception) {
|
package/internals/errors.d.ts
CHANGED
|
@@ -41,9 +41,6 @@ export declare class MissingSessionError extends Error {
|
|
|
41
41
|
export declare class ContextPipelineInterruptedError extends Error {
|
|
42
42
|
constructor(message?: string);
|
|
43
43
|
}
|
|
44
|
-
export declare class ContextPipelineInitializationError extends Error {
|
|
45
|
-
constructor(error: unknown, options?: ErrorOptions);
|
|
46
|
-
}
|
|
47
44
|
export declare class ContextPipelineCleanupError extends CriticalError {
|
|
48
45
|
constructor(error: unknown, options?: ErrorOptions);
|
|
49
46
|
}
|
package/internals/errors.js
CHANGED
|
@@ -49,11 +49,6 @@ export class ContextPipelineInterruptedError extends Error {
|
|
|
49
49
|
super(`Request handling was interrupted during context initialization ${message ? ` - ${message}` : ''}`);
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
export class ContextPipelineInitializationError extends Error {
|
|
53
|
-
constructor(error, options) {
|
|
54
|
-
super(undefined, { cause: error, ...options });
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
52
|
export class ContextPipelineCleanupError extends CriticalError {
|
|
58
53
|
constructor(error, options) {
|
|
59
54
|
super(undefined, { cause: error, ...options });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crawlee/basic",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.185",
|
|
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": ">=22.0.0"
|
|
@@ -42,10 +42,10 @@
|
|
|
42
42
|
"@apify/datastructures": "^3.0.1",
|
|
43
43
|
"@apify/timeout": "^1.0.1",
|
|
44
44
|
"@apify/utilities": "^3.0.1",
|
|
45
|
-
"@crawlee/core": "4.0.0-beta.
|
|
46
|
-
"@crawlee/http-client": "4.0.0-beta.
|
|
47
|
-
"@crawlee/types": "4.0.0-beta.
|
|
48
|
-
"@crawlee/utils": "4.0.0-beta.
|
|
45
|
+
"@crawlee/core": "4.0.0-beta.185",
|
|
46
|
+
"@crawlee/http-client": "4.0.0-beta.185",
|
|
47
|
+
"@crawlee/types": "4.0.0-beta.185",
|
|
48
|
+
"@crawlee/utils": "4.0.0-beta.185",
|
|
49
49
|
"@sapphire/async-queue": "^1.5.5",
|
|
50
50
|
"@standard-schema/spec": "^1.0.0",
|
|
51
51
|
"csv-stringify": "^6.5.2",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"zod": "^4.5.4"
|
|
58
58
|
},
|
|
59
59
|
"optionalDependencies": {
|
|
60
|
-
"@crawlee/impit-client": "4.0.0-beta.
|
|
60
|
+
"@crawlee/impit-client": "4.0.0-beta.185"
|
|
61
61
|
},
|
|
62
62
|
"lerna": {
|
|
63
63
|
"command": {
|
|
@@ -66,5 +66,5 @@
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
},
|
|
69
|
-
"gitHead": "
|
|
69
|
+
"gitHead": "642c670956a5a60384d5ed5e67ee809927cc419b"
|
|
70
70
|
}
|