@crawlee/utils 3.0.0-alpha.2 → 3.0.0-alpha.20
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/index.d.ts +7 -0
- package/index.d.ts.map +1 -0
- package/index.js.map +1 -0
- package/index.mjs +18 -0
- package/internals/cheerio.d.ts +32 -0
- package/internals/cheerio.d.ts.map +1 -0
- package/internals/cheerio.js +94 -0
- package/internals/cheerio.js.map +1 -0
- package/internals/extract-urls.d.ts +39 -0
- package/internals/extract-urls.d.ts.map +1 -0
- package/internals/extract-urls.js +43 -0
- package/internals/extract-urls.js.map +1 -0
- package/internals/general.d.ts +62 -0
- package/internals/general.d.ts.map +1 -0
- package/internals/general.js +143 -0
- package/internals/general.js.map +1 -0
- package/internals/memory-info.d.ts +26 -0
- package/internals/memory-info.d.ts.map +1 -0
- package/internals/memory-info.js +142 -0
- package/internals/memory-info.js.map +1 -0
- package/internals/social.d.ts +493 -0
- package/internals/social.d.ts.map +1 -0
- package/internals/social.js +661 -0
- package/internals/social.js.map +1 -0
- package/internals/typedefs.d.ts +11 -0
- package/internals/typedefs.d.ts.map +1 -0
- package/internals/typedefs.js +15 -0
- package/internals/typedefs.js.map +1 -0
- package/package.json +2 -5
- package/tsconfig.build.tsbuildinfo +1 -0
package/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './internals/cheerio';
|
|
2
|
+
export * from './internals/extract-urls';
|
|
3
|
+
export * from './internals/general';
|
|
4
|
+
export * from './internals/memory-info';
|
|
5
|
+
export * as social from './internals/social';
|
|
6
|
+
export * from './internals/typedefs';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
package/index.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,oBAAoB,CAAC;AAC7C,cAAc,sBAAsB,CAAC"}
|
package/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;AAAA,8DAAoC;AACpC,mEAAyC;AACzC,8DAAoC;AACpC,kEAAwC;AACxC,qEAA6C;AAC7C,+DAAqC"}
|
package/index.mjs
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import mod from "./index.js";
|
|
2
|
+
|
|
3
|
+
export default mod;
|
|
4
|
+
export const URL_NO_COMMAS_REGEX = mod.URL_NO_COMMAS_REGEX;
|
|
5
|
+
export const URL_WITH_COMMAS_REGEX = mod.URL_WITH_COMMAS_REGEX;
|
|
6
|
+
export const downloadListOfUrls = mod.downloadListOfUrls;
|
|
7
|
+
export const entries = mod.entries;
|
|
8
|
+
export const extractUrls = mod.extractUrls;
|
|
9
|
+
export const getMemoryInfo = mod.getMemoryInfo;
|
|
10
|
+
export const htmlToText = mod.htmlToText;
|
|
11
|
+
export const isDocker = mod.isDocker;
|
|
12
|
+
export const keys = mod.keys;
|
|
13
|
+
export const parseContentTypeFromResponse = mod.parseContentTypeFromResponse;
|
|
14
|
+
export const purgeLocalStorage = mod.purgeLocalStorage;
|
|
15
|
+
export const sleep = mod.sleep;
|
|
16
|
+
export const snakeCaseToCamelCase = mod.snakeCaseToCamelCase;
|
|
17
|
+
export const social = mod.social;
|
|
18
|
+
export const weightedAvg = mod.weightedAvg;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { load } from 'cheerio';
|
|
2
|
+
export declare type CheerioRoot = ReturnType<typeof load>;
|
|
3
|
+
/**
|
|
4
|
+
* The function converts a HTML document to a plain text.
|
|
5
|
+
*
|
|
6
|
+
* The plain text generated by the function is similar to a text captured
|
|
7
|
+
* by pressing Ctrl+A and Ctrl+C on a page when loaded in a web browser.
|
|
8
|
+
* The function doesn't aspire to preserve the formatting or to be perfectly correct with respect to HTML specifications.
|
|
9
|
+
* However, it attempts to generate newlines and whitespaces in and around HTML elements
|
|
10
|
+
* to avoid merging distinct parts of text and thus enable extraction of data from the text (e.g. phone numbers).
|
|
11
|
+
*
|
|
12
|
+
* **Example usage**
|
|
13
|
+
* ```javascript
|
|
14
|
+
* const text = htmlToText('<html><body>Some text</body></html>');
|
|
15
|
+
* console.log(text);
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* Note that the function uses [cheerio](https://www.npmjs.com/package/cheerio) to parse the HTML.
|
|
19
|
+
* Optionally, to avoid duplicate parsing of HTML and thus improve performance, you can pass
|
|
20
|
+
* an existing Cheerio object to the function instead of the HTML text. The HTML should be parsed
|
|
21
|
+
* with the `decodeEntities` option set to `true`. For example:
|
|
22
|
+
*
|
|
23
|
+
* ```javascript
|
|
24
|
+
* const cheerio = require('cheerio');
|
|
25
|
+
* const html = '<html><body>Some text</body></html>';
|
|
26
|
+
* const text = htmlToText(cheerio.load(html, { decodeEntities: true }));
|
|
27
|
+
* ```
|
|
28
|
+
* @param htmlOrCheerioElement HTML text or parsed HTML represented using a [cheerio](https://www.npmjs.com/package/cheerio) function.
|
|
29
|
+
* @return Plain text
|
|
30
|
+
*/
|
|
31
|
+
export declare function htmlToText(htmlOrCheerioElement: string | CheerioRoot): string;
|
|
32
|
+
//# sourceMappingURL=cheerio.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cheerio.d.ts","sourceRoot":"","sources":["../../src/internals/cheerio.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAExC,oBAAY,WAAW,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC;AAMlD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,UAAU,CAAC,oBAAoB,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,CAiD7E"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.htmlToText = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const cheerio_1 = tslib_1.__importDefault(require("cheerio"));
|
|
6
|
+
// NOTE: We are skipping 'noscript' since it's content is evaluated as text, instead of HTML elements. That damages the results.
|
|
7
|
+
const SKIP_TAGS_REGEX = /^(script|style|canvas|svg|noscript)$/i;
|
|
8
|
+
const BLOCK_TAGS_REGEX = /^(p|h1|h2|h3|h4|h5|h6|ol|ul|li|pre|address|blockquote|dl|div|fieldset|form|table|tr|select|option)$/i;
|
|
9
|
+
/**
|
|
10
|
+
* The function converts a HTML document to a plain text.
|
|
11
|
+
*
|
|
12
|
+
* The plain text generated by the function is similar to a text captured
|
|
13
|
+
* by pressing Ctrl+A and Ctrl+C on a page when loaded in a web browser.
|
|
14
|
+
* The function doesn't aspire to preserve the formatting or to be perfectly correct with respect to HTML specifications.
|
|
15
|
+
* However, it attempts to generate newlines and whitespaces in and around HTML elements
|
|
16
|
+
* to avoid merging distinct parts of text and thus enable extraction of data from the text (e.g. phone numbers).
|
|
17
|
+
*
|
|
18
|
+
* **Example usage**
|
|
19
|
+
* ```javascript
|
|
20
|
+
* const text = htmlToText('<html><body>Some text</body></html>');
|
|
21
|
+
* console.log(text);
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* Note that the function uses [cheerio](https://www.npmjs.com/package/cheerio) to parse the HTML.
|
|
25
|
+
* Optionally, to avoid duplicate parsing of HTML and thus improve performance, you can pass
|
|
26
|
+
* an existing Cheerio object to the function instead of the HTML text. The HTML should be parsed
|
|
27
|
+
* with the `decodeEntities` option set to `true`. For example:
|
|
28
|
+
*
|
|
29
|
+
* ```javascript
|
|
30
|
+
* const cheerio = require('cheerio');
|
|
31
|
+
* const html = '<html><body>Some text</body></html>';
|
|
32
|
+
* const text = htmlToText(cheerio.load(html, { decodeEntities: true }));
|
|
33
|
+
* ```
|
|
34
|
+
* @param htmlOrCheerioElement HTML text or parsed HTML represented using a [cheerio](https://www.npmjs.com/package/cheerio) function.
|
|
35
|
+
* @return Plain text
|
|
36
|
+
*/
|
|
37
|
+
function htmlToText(htmlOrCheerioElement) {
|
|
38
|
+
if (!htmlOrCheerioElement)
|
|
39
|
+
return '';
|
|
40
|
+
// TODO: Add support for "html" being a Cheerio element, otherwise the only way
|
|
41
|
+
// to use it is e.g. htmlToText($('p').html())) which is inefficient
|
|
42
|
+
// Also, it seems this doesn't work well in CheerioScraper, e.g. htmlToText($)
|
|
43
|
+
// produces really text with a lot of HTML elements in it. Let's just deprecate this sort of usage,
|
|
44
|
+
// and make the parameter "htmlOrCheerioElement"
|
|
45
|
+
const $ = typeof htmlOrCheerioElement === 'function' ? htmlOrCheerioElement : cheerio_1.default.load(htmlOrCheerioElement, { decodeEntities: true });
|
|
46
|
+
let text = '';
|
|
47
|
+
// TODO: the type for elems is very annoying to work with.
|
|
48
|
+
// The correct type is Node[] from cheerio but it needs a lot more casting in each branch, or alternatively,
|
|
49
|
+
// use the is* methods from domhandler (isText, isTag, isComment, etc.)
|
|
50
|
+
// @ts-expect-error
|
|
51
|
+
const process = (elems) => {
|
|
52
|
+
const len = elems ? elems.length : 0;
|
|
53
|
+
for (let i = 0; i < len; i++) {
|
|
54
|
+
const elem = elems[i];
|
|
55
|
+
if (elem.type === 'text') {
|
|
56
|
+
// Compress spaces, unless we're inside <pre> element
|
|
57
|
+
let compr;
|
|
58
|
+
if (elem.parent && elem.parent.tagName === 'pre')
|
|
59
|
+
compr = elem.data;
|
|
60
|
+
else
|
|
61
|
+
compr = elem.data.replace(/\s+/g, ' ');
|
|
62
|
+
// If text is empty or ends with a whitespace, don't add the leading whitepsace
|
|
63
|
+
if (compr.startsWith(' ') && /(^|\s)$/.test(text))
|
|
64
|
+
compr = compr.substr(1);
|
|
65
|
+
text += compr;
|
|
66
|
+
}
|
|
67
|
+
else if (elem.type === 'comment' || SKIP_TAGS_REGEX.test(elem.tagName)) {
|
|
68
|
+
// Skip comments and special elements
|
|
69
|
+
}
|
|
70
|
+
else if (elem.tagName === 'br') {
|
|
71
|
+
text += '\n';
|
|
72
|
+
}
|
|
73
|
+
else if (elem.tagName === 'td') {
|
|
74
|
+
process(elem.children);
|
|
75
|
+
text += '\t';
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
// Block elements must be surrounded by newlines (unless beginning of text)
|
|
79
|
+
const isBlockTag = BLOCK_TAGS_REGEX.test(elem.tagName);
|
|
80
|
+
if (isBlockTag && !/(^|\n)$/.test(text))
|
|
81
|
+
text += '\n';
|
|
82
|
+
process(elem.children);
|
|
83
|
+
if (isBlockTag && !text.endsWith('\n'))
|
|
84
|
+
text += '\n';
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
// If HTML document has body, only convert that, otherwise convert the entire HTML
|
|
89
|
+
const $body = $('body');
|
|
90
|
+
process($body.length > 0 ? $body : $.root());
|
|
91
|
+
return text.trim();
|
|
92
|
+
}
|
|
93
|
+
exports.htmlToText = htmlToText;
|
|
94
|
+
//# sourceMappingURL=cheerio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cheerio.js","sourceRoot":"","sources":["../../src/internals/cheerio.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
export interface DownloadListOfUrlsOptions {
|
|
3
|
+
/**
|
|
4
|
+
* URL to the file
|
|
5
|
+
*/
|
|
6
|
+
url: string;
|
|
7
|
+
/**
|
|
8
|
+
* The encoding of the file.
|
|
9
|
+
* @default 'utf8'
|
|
10
|
+
*/
|
|
11
|
+
encoding?: BufferEncoding;
|
|
12
|
+
/**
|
|
13
|
+
* Custom regular expression to identify the URLs in the file to extract.
|
|
14
|
+
* The regular expression should be case-insensitive and have global flag set (i.e. `/something/gi`).
|
|
15
|
+
* @default Actor.utils.URL_NO_COMMAS_REGEX
|
|
16
|
+
*/
|
|
17
|
+
urlRegExp?: RegExp;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Returns a promise that resolves to an array of urls parsed from the resource available at the provided url.
|
|
21
|
+
* Optionally, custom regular expression and encoding may be provided.
|
|
22
|
+
*/
|
|
23
|
+
export declare function downloadListOfUrls(options: DownloadListOfUrlsOptions): Promise<string[]>;
|
|
24
|
+
export interface ExtractUrlsOptions {
|
|
25
|
+
/**
|
|
26
|
+
* The string to extract URLs from.
|
|
27
|
+
*/
|
|
28
|
+
string: string;
|
|
29
|
+
/**
|
|
30
|
+
* Custom regular expression
|
|
31
|
+
* @default URL_NO_COMMAS_REGEX
|
|
32
|
+
*/
|
|
33
|
+
urlRegExp?: RegExp;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Collects all URLs in an arbitrary string to an array, optionally using a custom regular expression.
|
|
37
|
+
*/
|
|
38
|
+
export declare function extractUrls(options: ExtractUrlsOptions): string[];
|
|
39
|
+
//# sourceMappingURL=extract-urls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-urls.d.ts","sourceRoot":"","sources":["../../src/internals/extract-urls.ts"],"names":[],"mappings":";AAIA,MAAM,WAAW,yBAAyB;IACtC;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;OAGG;IACH,QAAQ,CAAC,EAAE,cAAc,CAAC;IAE1B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAqB9F;AAED,MAAM,WAAW,kBAAkB;IAC/B;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,EAAE,CAOjE"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractUrls = exports.downloadListOfUrls = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const ow_1 = tslib_1.__importDefault(require("ow"));
|
|
6
|
+
const got_scraping_1 = require("got-scraping");
|
|
7
|
+
const general_1 = require("./general");
|
|
8
|
+
/**
|
|
9
|
+
* Returns a promise that resolves to an array of urls parsed from the resource available at the provided url.
|
|
10
|
+
* Optionally, custom regular expression and encoding may be provided.
|
|
11
|
+
*/
|
|
12
|
+
async function downloadListOfUrls(options) {
|
|
13
|
+
(0, ow_1.default)(options, ow_1.default.object.exactShape({
|
|
14
|
+
url: ow_1.default.string.url,
|
|
15
|
+
encoding: ow_1.default.optional.string,
|
|
16
|
+
urlRegExp: ow_1.default.optional.regExp,
|
|
17
|
+
proxyUrl: ow_1.default.optional.string,
|
|
18
|
+
}));
|
|
19
|
+
const { url, encoding = 'utf8', urlRegExp = general_1.URL_NO_COMMAS_REGEX, proxyUrl } = options;
|
|
20
|
+
// Try to detect wrong urls and fix them. Currently, detects only sharing url instead of csv download one.
|
|
21
|
+
const match = url.match(/^(https:\/\/docs\.google\.com\/spreadsheets\/d\/(?:\w|-)+)\/?/);
|
|
22
|
+
let fixedUrl = url;
|
|
23
|
+
if (match) {
|
|
24
|
+
fixedUrl = `${match[1]}/gviz/tq?tqx=out:csv`;
|
|
25
|
+
}
|
|
26
|
+
// @ts-expect-error https://github.com/apify/got-scraping/issues/66
|
|
27
|
+
const { body: string } = await (0, got_scraping_1.gotScraping)({ url: fixedUrl, encoding, proxyUrl });
|
|
28
|
+
return extractUrls({ string, urlRegExp });
|
|
29
|
+
}
|
|
30
|
+
exports.downloadListOfUrls = downloadListOfUrls;
|
|
31
|
+
/**
|
|
32
|
+
* Collects all URLs in an arbitrary string to an array, optionally using a custom regular expression.
|
|
33
|
+
*/
|
|
34
|
+
function extractUrls(options) {
|
|
35
|
+
(0, ow_1.default)(options, ow_1.default.object.exactShape({
|
|
36
|
+
string: ow_1.default.string,
|
|
37
|
+
urlRegExp: ow_1.default.optional.regExp,
|
|
38
|
+
}));
|
|
39
|
+
const { string, urlRegExp = general_1.URL_NO_COMMAS_REGEX } = options;
|
|
40
|
+
return string.match(urlRegExp) || [];
|
|
41
|
+
}
|
|
42
|
+
exports.extractUrls = extractUrls;
|
|
43
|
+
//# sourceMappingURL=extract-urls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-urls.js","sourceRoot":"","sources":["../../src/internals/extract-urls.ts"],"names":[],"mappings":";;;;AAAA,oDAAoB;AACpB,+CAA2C;AAC3C,uCAAgD;AAsBhD;;;GAGG;AACI,KAAK,UAAU,kBAAkB,CAAC,OAAkC;IACvE,IAAA,YAAE,EAAC,OAAO,EAAE,YAAE,CAAC,MAAM,CAAC,UAAU,CAAC;QAC7B,GAAG,EAAE,YAAE,CAAC,MAAM,CAAC,GAAG;QAClB,QAAQ,EAAE,YAAE,CAAC,QAAQ,CAAC,MAAM;QAC5B,SAAS,EAAE,YAAE,CAAC,QAAQ,CAAC,MAAM;QAC7B,QAAQ,EAAE,YAAE,CAAC,QAAQ,CAAC,MAAM;KAC/B,CAAC,CAAC,CAAC;IACJ,MAAM,EAAE,GAAG,EAAE,QAAQ,GAAG,MAAM,EAAE,SAAS,GAAG,6BAAmB,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IAEtF,0GAA0G;IAC1G,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,+DAA+D,CAAC,CAAC;IACzF,IAAI,QAAQ,GAAG,GAAG,CAAC;IAEnB,IAAI,KAAK,EAAE;QACP,QAAQ,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,sBAAsB,CAAC;KAChD;IAED,mEAAmE;IACnE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,IAAA,0BAAW,EAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;IAElF,OAAO,WAAW,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;AAC9C,CAAC;AArBD,gDAqBC;AAeD;;GAEG;AACH,SAAgB,WAAW,CAAC,OAA2B;IACnD,IAAA,YAAE,EAAC,OAAO,EAAE,YAAE,CAAC,MAAM,CAAC,UAAU,CAAC;QAC7B,MAAM,EAAE,YAAE,CAAC,MAAM;QACjB,SAAS,EAAE,YAAE,CAAC,QAAQ,CAAC,MAAM;KAChC,CAAC,CAAC,CAAC;IACJ,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,6BAAmB,EAAE,GAAG,OAAO,CAAC;IAC5D,OAAO,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;AACzC,CAAC;AAPD,kCAOC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type { IncomingMessage } from 'node:http';
|
|
3
|
+
/**
|
|
4
|
+
* Default regular expression to match URLs in a string that may be plain text, JSON, CSV or other. It supports common URL characters
|
|
5
|
+
* and does not support URLs containing commas or spaces. The URLs also may contain Unicode letters (not symbols).
|
|
6
|
+
*/
|
|
7
|
+
export declare const URL_NO_COMMAS_REGEX: RegExp;
|
|
8
|
+
/**
|
|
9
|
+
* Regular expression that, in addition to the default regular expression `URL_NO_COMMAS_REGEX`, supports matching commas in URL path and query.
|
|
10
|
+
* Note, however, that this may prevent parsing URLs from comma delimited lists, or the URLs may become malformed.
|
|
11
|
+
*/
|
|
12
|
+
export declare const URL_WITH_COMMAS_REGEX: RegExp;
|
|
13
|
+
/**
|
|
14
|
+
* Returns a `Promise` that resolves to true if the code is running in a Docker container.
|
|
15
|
+
*/
|
|
16
|
+
export declare function isDocker(forceReset?: boolean): Promise<boolean>;
|
|
17
|
+
/**
|
|
18
|
+
* Computes a weighted average of an array of numbers, complemented by an array of weights.
|
|
19
|
+
* @ignore
|
|
20
|
+
*/
|
|
21
|
+
export declare function weightedAvg(arrValues: number[], arrWeights: number[]): number;
|
|
22
|
+
/**
|
|
23
|
+
* Returns a `Promise` that resolves after a specific period of time. This is useful to implement waiting
|
|
24
|
+
* in your code, e.g. to prevent overloading of target website or to avoid bot detection.
|
|
25
|
+
*
|
|
26
|
+
* **Example usage:**
|
|
27
|
+
*
|
|
28
|
+
* ```
|
|
29
|
+
* const { sleep } = require('@crawlee/utils');
|
|
30
|
+
*
|
|
31
|
+
* ...
|
|
32
|
+
*
|
|
33
|
+
* // Sleep 1.5 seconds
|
|
34
|
+
* await sleep(1500);
|
|
35
|
+
* ```
|
|
36
|
+
* @param millis Period of time to sleep, in milliseconds. If not a positive number, the returned promise resolves immediately.
|
|
37
|
+
*/
|
|
38
|
+
export declare function sleep(millis?: number): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Converts SNAKE_CASE to camelCase.
|
|
41
|
+
* @ignore
|
|
42
|
+
*/
|
|
43
|
+
export declare function snakeCaseToCamelCase(snakeCaseStr: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* Cleans up the local storage folder created when testing locally.
|
|
46
|
+
* This is useful in the event you are debugging your code locally.
|
|
47
|
+
*
|
|
48
|
+
* Be careful as this will remove the folder you provide and everything in it!
|
|
49
|
+
*
|
|
50
|
+
* @param [folder] The folder to clean up
|
|
51
|
+
*/
|
|
52
|
+
export declare function purgeLocalStorage(folder?: string): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Gets parsed content type from response object
|
|
55
|
+
* @param response HTTP response object
|
|
56
|
+
* @ignore
|
|
57
|
+
*/
|
|
58
|
+
export declare function parseContentTypeFromResponse(response: IncomingMessage): {
|
|
59
|
+
type: string;
|
|
60
|
+
charset: BufferEncoding;
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=general.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"general.d.ts","sourceRoot":"","sources":["../../src/internals/general.ts"],"names":[],"mappings":";AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAMjD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,QAA0J,CAAC;AAE3L;;;GAGG;AACH,eAAO,MAAM,qBAAqB,QAA2J,CAAC;AAkB9L;;GAEG;AACH,wBAAgB,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAK/D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,CAS7E;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAUjE;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQtE;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,eAAe,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,CA6BjH"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseContentTypeFromResponse = exports.purgeLocalStorage = exports.snakeCaseToCamelCase = exports.sleep = exports.weightedAvg = exports.isDocker = exports.URL_WITH_COMMAS_REGEX = exports.URL_NO_COMMAS_REGEX = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const consts_1 = require("@apify/consts");
|
|
6
|
+
const content_type_1 = tslib_1.__importDefault(require("content-type"));
|
|
7
|
+
const mime_types_1 = tslib_1.__importDefault(require("mime-types"));
|
|
8
|
+
const promises_1 = tslib_1.__importDefault(require("node:fs/promises"));
|
|
9
|
+
const node_path_1 = tslib_1.__importDefault(require("node:path"));
|
|
10
|
+
const promises_2 = require("node:timers/promises");
|
|
11
|
+
const node_url_1 = require("node:url");
|
|
12
|
+
const ow_1 = tslib_1.__importDefault(require("ow"));
|
|
13
|
+
/**
|
|
14
|
+
* Default regular expression to match URLs in a string that may be plain text, JSON, CSV or other. It supports common URL characters
|
|
15
|
+
* and does not support URLs containing commas or spaces. The URLs also may contain Unicode letters (not symbols).
|
|
16
|
+
*/
|
|
17
|
+
exports.URL_NO_COMMAS_REGEX = RegExp('https?://(www\\.)?[\\p{L}0-9][-\\p{L}0-9@:%._\\+~#=]{0,254}[\\p{L}0-9]\\.[a-z]{2,63}(:\\d{1,5})?(/[-\\p{L}0-9@:%_\\+.~#?&//=\\(\\)]*)?', 'giu'); // eslint-disable-line
|
|
18
|
+
/**
|
|
19
|
+
* Regular expression that, in addition to the default regular expression `URL_NO_COMMAS_REGEX`, supports matching commas in URL path and query.
|
|
20
|
+
* Note, however, that this may prevent parsing URLs from comma delimited lists, or the URLs may become malformed.
|
|
21
|
+
*/
|
|
22
|
+
exports.URL_WITH_COMMAS_REGEX = RegExp('https?://(www\\.)?[\\p{L}0-9][-\\p{L}0-9@:%._\\+~#=]{0,254}[\\p{L}0-9]\\.[a-z]{2,63}(:\\d{1,5})?(/[-\\p{L}0-9@:%_\\+,.~#?&//=\\(\\)]*)?', 'giu'); // eslint-disable-line
|
|
23
|
+
let isDockerPromiseCache;
|
|
24
|
+
async function createIsDockerPromise() {
|
|
25
|
+
const promise1 = promises_1.default.stat('/.dockerenv')
|
|
26
|
+
.then(() => true)
|
|
27
|
+
.catch(() => false);
|
|
28
|
+
const promise2 = promises_1.default.readFile('/proc/self/cgroup', 'utf8')
|
|
29
|
+
.then((content) => content.includes('docker'))
|
|
30
|
+
.catch(() => false);
|
|
31
|
+
const [result1, result2] = await Promise.all([promise1, promise2]);
|
|
32
|
+
return result1 || result2;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Returns a `Promise` that resolves to true if the code is running in a Docker container.
|
|
36
|
+
*/
|
|
37
|
+
function isDocker(forceReset) {
|
|
38
|
+
// Parameter forceReset is just internal for unit tests.
|
|
39
|
+
if (!isDockerPromiseCache || forceReset)
|
|
40
|
+
isDockerPromiseCache = createIsDockerPromise();
|
|
41
|
+
return isDockerPromiseCache;
|
|
42
|
+
}
|
|
43
|
+
exports.isDocker = isDocker;
|
|
44
|
+
/**
|
|
45
|
+
* Computes a weighted average of an array of numbers, complemented by an array of weights.
|
|
46
|
+
* @ignore
|
|
47
|
+
*/
|
|
48
|
+
function weightedAvg(arrValues, arrWeights) {
|
|
49
|
+
const result = arrValues.map((value, i) => {
|
|
50
|
+
const weight = arrWeights[i];
|
|
51
|
+
const sum = value * weight;
|
|
52
|
+
return [sum, weight];
|
|
53
|
+
}).reduce((p, c) => [p[0] + c[0], p[1] + c[1]], [0, 0]);
|
|
54
|
+
return result[0] / result[1];
|
|
55
|
+
}
|
|
56
|
+
exports.weightedAvg = weightedAvg;
|
|
57
|
+
/**
|
|
58
|
+
* Returns a `Promise` that resolves after a specific period of time. This is useful to implement waiting
|
|
59
|
+
* in your code, e.g. to prevent overloading of target website or to avoid bot detection.
|
|
60
|
+
*
|
|
61
|
+
* **Example usage:**
|
|
62
|
+
*
|
|
63
|
+
* ```
|
|
64
|
+
* const { sleep } = require('@crawlee/utils');
|
|
65
|
+
*
|
|
66
|
+
* ...
|
|
67
|
+
*
|
|
68
|
+
* // Sleep 1.5 seconds
|
|
69
|
+
* await sleep(1500);
|
|
70
|
+
* ```
|
|
71
|
+
* @param millis Period of time to sleep, in milliseconds. If not a positive number, the returned promise resolves immediately.
|
|
72
|
+
*/
|
|
73
|
+
function sleep(millis) {
|
|
74
|
+
return (0, promises_2.setTimeout)(millis);
|
|
75
|
+
}
|
|
76
|
+
exports.sleep = sleep;
|
|
77
|
+
/**
|
|
78
|
+
* Converts SNAKE_CASE to camelCase.
|
|
79
|
+
* @ignore
|
|
80
|
+
*/
|
|
81
|
+
function snakeCaseToCamelCase(snakeCaseStr) {
|
|
82
|
+
return snakeCaseStr
|
|
83
|
+
.toLowerCase()
|
|
84
|
+
.split('_')
|
|
85
|
+
.map((part, index) => {
|
|
86
|
+
return index > 0
|
|
87
|
+
? part.charAt(0).toUpperCase() + part.slice(1)
|
|
88
|
+
: part;
|
|
89
|
+
})
|
|
90
|
+
.join('');
|
|
91
|
+
}
|
|
92
|
+
exports.snakeCaseToCamelCase = snakeCaseToCamelCase;
|
|
93
|
+
/**
|
|
94
|
+
* Cleans up the local storage folder created when testing locally.
|
|
95
|
+
* This is useful in the event you are debugging your code locally.
|
|
96
|
+
*
|
|
97
|
+
* Be careful as this will remove the folder you provide and everything in it!
|
|
98
|
+
*
|
|
99
|
+
* @param [folder] The folder to clean up
|
|
100
|
+
*/
|
|
101
|
+
async function purgeLocalStorage(folder) {
|
|
102
|
+
// If the user did not provide a folder, try to get it from the env variables, or the default one
|
|
103
|
+
if (!folder) {
|
|
104
|
+
folder = process.env[consts_1.ENV_VARS.LOCAL_STORAGE_DIR] || 'apify_storage';
|
|
105
|
+
}
|
|
106
|
+
// Clear the folder
|
|
107
|
+
await promises_1.default.rm(folder, { recursive: true, force: true });
|
|
108
|
+
}
|
|
109
|
+
exports.purgeLocalStorage = purgeLocalStorage;
|
|
110
|
+
/**
|
|
111
|
+
* Gets parsed content type from response object
|
|
112
|
+
* @param response HTTP response object
|
|
113
|
+
* @ignore
|
|
114
|
+
*/
|
|
115
|
+
function parseContentTypeFromResponse(response) {
|
|
116
|
+
(0, ow_1.default)(response, ow_1.default.object.partialShape({
|
|
117
|
+
url: ow_1.default.string.url,
|
|
118
|
+
headers: ow_1.default.object,
|
|
119
|
+
}));
|
|
120
|
+
const { url, headers } = response;
|
|
121
|
+
let parsedContentType;
|
|
122
|
+
if (headers['content-type']) {
|
|
123
|
+
try {
|
|
124
|
+
parsedContentType = content_type_1.default.parse(headers['content-type']);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Can not parse content type from Content-Type header. Try to parse it from file extension.
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Parse content type from file extension as fallback
|
|
131
|
+
if (!parsedContentType) {
|
|
132
|
+
const parsedUrl = new node_url_1.URL(url);
|
|
133
|
+
const contentTypeFromExtname = mime_types_1.default.contentType(node_path_1.default.extname(parsedUrl.pathname))
|
|
134
|
+
|| 'application/octet-stream; charset=utf-8'; // Fallback content type, specified in https://tools.ietf.org/html/rfc7231#section-3.1.1.5
|
|
135
|
+
parsedContentType = content_type_1.default.parse(contentTypeFromExtname);
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
type: parsedContentType.type,
|
|
139
|
+
charset: parsedContentType.parameters.charset,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
exports.parseContentTypeFromResponse = parseContentTypeFromResponse;
|
|
143
|
+
//# sourceMappingURL=general.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"general.js","sourceRoot":"","sources":["../../src/internals/general.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Describes memory usage of the process.
|
|
3
|
+
*/
|
|
4
|
+
export interface MemoryInfo {
|
|
5
|
+
/** Total memory available in the system or container */
|
|
6
|
+
totalBytes: number;
|
|
7
|
+
/** Amount of free memory in the system or container */
|
|
8
|
+
freeBytes: number;
|
|
9
|
+
/** Amount of memory used (= totalBytes - freeBytes) */
|
|
10
|
+
usedBytes: number;
|
|
11
|
+
/** Amount of memory used the current Node.js process */
|
|
12
|
+
mainProcessBytes: number;
|
|
13
|
+
/** Amount of memory used by child processes of the current Node.js process */
|
|
14
|
+
childProcessesBytes: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Returns memory statistics of the process and the system, see {@link MemoryInfo}.
|
|
18
|
+
*
|
|
19
|
+
* If the process runs inside of Docker, the `getMemoryInfo` gets container memory limits,
|
|
20
|
+
* otherwise it gets system memory limits.
|
|
21
|
+
*
|
|
22
|
+
* Beware that the function is quite inefficient because it spawns a new process.
|
|
23
|
+
* Therefore you shouldn't call it too often, like more than once per second.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getMemoryInfo(): Promise<MemoryInfo>;
|
|
26
|
+
//# sourceMappingURL=memory-info.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-info.d.ts","sourceRoot":"","sources":["../../src/internals/memory-info.ts"],"names":[],"mappings":"AAqBA;;GAEG;AACH,MAAM,WAAW,UAAU;IACvB,wDAAwD;IACxD,UAAU,EAAE,MAAM,CAAC;IAEnB,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAC;IAElB,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAC;IAElB,wDAAwD;IACxD,gBAAgB,EAAE,MAAM,CAAC;IAEzB,8EAA8E;IAC9E,mBAAmB,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;;;;GAQG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC,CAmHzD"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getMemoryInfo = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const log_1 = tslib_1.__importDefault(require("@apify/log"));
|
|
6
|
+
// @ts-expect-error We need to add typings for @apify/ps-tree
|
|
7
|
+
const ps_tree_1 = tslib_1.__importDefault(require("@apify/ps-tree"));
|
|
8
|
+
const node_child_process_1 = require("node:child_process");
|
|
9
|
+
const promises_1 = tslib_1.__importDefault(require("node:fs/promises"));
|
|
10
|
+
const node_os_1 = tslib_1.__importDefault(require("node:os"));
|
|
11
|
+
const node_util_1 = tslib_1.__importDefault(require("node:util"));
|
|
12
|
+
const general_1 = require("./general");
|
|
13
|
+
const MEMORY_FILE_PATHS = {
|
|
14
|
+
TOTAL: {
|
|
15
|
+
V1: '/sys/fs/cgroup/memory/memory.limit_in_bytes',
|
|
16
|
+
V2: '/sys/fs/cgroup/memory.max',
|
|
17
|
+
},
|
|
18
|
+
USED: {
|
|
19
|
+
V1: '/sys/fs/cgroup/memory/memory.usage_in_bytes',
|
|
20
|
+
V2: '/sys/fs/cgroup/memory.current',
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Returns memory statistics of the process and the system, see {@link MemoryInfo}.
|
|
25
|
+
*
|
|
26
|
+
* If the process runs inside of Docker, the `getMemoryInfo` gets container memory limits,
|
|
27
|
+
* otherwise it gets system memory limits.
|
|
28
|
+
*
|
|
29
|
+
* Beware that the function is quite inefficient because it spawns a new process.
|
|
30
|
+
* Therefore you shouldn't call it too often, like more than once per second.
|
|
31
|
+
*/
|
|
32
|
+
async function getMemoryInfo() {
|
|
33
|
+
const psTreePromised = node_util_1.default.promisify(ps_tree_1.default);
|
|
34
|
+
// lambda does *not* have `ps` and other command line tools
|
|
35
|
+
// required to extract memory usage.
|
|
36
|
+
const isLambdaEnvironment = process.platform === 'linux'
|
|
37
|
+
&& !!process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE;
|
|
38
|
+
const isDockerVar = !isLambdaEnvironment && await (0, general_1.isDocker)();
|
|
39
|
+
let mainProcessBytes = -1;
|
|
40
|
+
let childProcessesBytes = 0;
|
|
41
|
+
if (isLambdaEnvironment) {
|
|
42
|
+
// reported in bytes
|
|
43
|
+
mainProcessBytes = process.memoryUsage().rss;
|
|
44
|
+
// TODO this is quite ugly, let's introduce some local variables
|
|
45
|
+
// https://stackoverflow.com/a/55914335/129415
|
|
46
|
+
childProcessesBytes = +(0, node_child_process_1.execSync)('cat /proc/meminfo')
|
|
47
|
+
.toString()
|
|
48
|
+
.split(/[\n: ]/)
|
|
49
|
+
.filter((val) => val.trim())[19]
|
|
50
|
+
* 1000 // meminfo reports in kb, not bytes
|
|
51
|
+
// the total used memory is reported by meminfo
|
|
52
|
+
// subtract memory used by the main node process
|
|
53
|
+
// in order to infer memory used by any child processes
|
|
54
|
+
- mainProcessBytes;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
// Query both root and child processes
|
|
58
|
+
const processes = await psTreePromised(process.pid, true);
|
|
59
|
+
processes.forEach((rec) => {
|
|
60
|
+
// Skip the 'ps' or 'wmic' commands used by ps-tree to query the processes
|
|
61
|
+
if (rec.COMMAND === 'ps' || rec.COMMAND === 'WMIC.exe') {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const bytes = parseInt(rec.RSS, 10);
|
|
65
|
+
// Obtain main process' memory separately
|
|
66
|
+
if (rec.PID === `${process.pid}`) {
|
|
67
|
+
mainProcessBytes = bytes;
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
childProcessesBytes += bytes;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
let totalBytes;
|
|
74
|
+
let usedBytes;
|
|
75
|
+
let freeBytes;
|
|
76
|
+
if (isLambdaEnvironment) {
|
|
77
|
+
// memory size is defined in megabytes
|
|
78
|
+
totalBytes = parseInt(process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE, 10) * 1000000;
|
|
79
|
+
usedBytes = mainProcessBytes + childProcessesBytes;
|
|
80
|
+
freeBytes = totalBytes - usedBytes;
|
|
81
|
+
log_1.default.debug(`lambda size of ${totalBytes} with ${freeBytes} free bytes`);
|
|
82
|
+
}
|
|
83
|
+
else if (isDockerVar) {
|
|
84
|
+
// When running inside Docker container, use container memory limits
|
|
85
|
+
// Check whether cgroups V1 or V2 is used
|
|
86
|
+
let cgroupsVersion = 'V1';
|
|
87
|
+
try {
|
|
88
|
+
// If this directory does not exists, assume docker is using cgroups V2
|
|
89
|
+
await promises_1.default.access('/sys/fs/cgroup/memory/');
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
cgroupsVersion = 'V2';
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
let [totalBytesStr, usedBytesStr] = await Promise.all([
|
|
96
|
+
promises_1.default.readFile(MEMORY_FILE_PATHS.TOTAL[cgroupsVersion], 'utf8'),
|
|
97
|
+
promises_1.default.readFile(MEMORY_FILE_PATHS.USED[cgroupsVersion], 'utf8'),
|
|
98
|
+
]);
|
|
99
|
+
// Cgroups V2 files contains newline character. Getting rid of it for better handling in later part of the code.
|
|
100
|
+
totalBytesStr = totalBytesStr.replace(/[^a-zA-Z0-9 ]/g, '');
|
|
101
|
+
usedBytesStr = usedBytesStr.replace(/[^a-zA-Z0-9 ]/g, '');
|
|
102
|
+
// Cgroups V2 contains 'max' string if memory is not limited
|
|
103
|
+
// See https://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup.git/tree/Documentation/admin-guide/cgroup-v2.rst (see "memory.max")
|
|
104
|
+
if (totalBytesStr === 'max') {
|
|
105
|
+
totalBytes = node_os_1.default.totalmem();
|
|
106
|
+
// Cgroups V1 is set to number related to platform and page size if memory is not limited
|
|
107
|
+
// See https://unix.stackexchange.com/q/420906
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
totalBytes = parseInt(totalBytesStr, 10);
|
|
111
|
+
const containerRunsWithUnlimitedMemory = totalBytes > Number.MAX_SAFE_INTEGER;
|
|
112
|
+
if (containerRunsWithUnlimitedMemory)
|
|
113
|
+
totalBytes = node_os_1.default.totalmem();
|
|
114
|
+
}
|
|
115
|
+
usedBytes = parseInt(usedBytesStr, 10);
|
|
116
|
+
freeBytes = totalBytes - usedBytes;
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
// log.deprecated logs a warning only once
|
|
120
|
+
log_1.default.deprecated('Your environment is Docker, but your system does not support memory cgroups. '
|
|
121
|
+
+ 'If you\'re running containers with limited memory, memory auto-scaling will not work properly.\n\n'
|
|
122
|
+
+ `Cause: ${err.message}`);
|
|
123
|
+
totalBytes = node_os_1.default.totalmem();
|
|
124
|
+
freeBytes = node_os_1.default.freemem();
|
|
125
|
+
usedBytes = totalBytes - freeBytes;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
totalBytes = node_os_1.default.totalmem();
|
|
130
|
+
freeBytes = node_os_1.default.freemem();
|
|
131
|
+
usedBytes = totalBytes - freeBytes;
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
totalBytes,
|
|
135
|
+
freeBytes,
|
|
136
|
+
usedBytes,
|
|
137
|
+
mainProcessBytes,
|
|
138
|
+
childProcessesBytes,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
exports.getMemoryInfo = getMemoryInfo;
|
|
142
|
+
//# sourceMappingURL=memory-info.js.map
|