@wikicasa-dev/node-common 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Cache.d.ts +20 -0
- package/dist/Cache.js +114 -0
- package/dist/CacheES.d.ts +17 -0
- package/dist/CacheES.js +119 -0
- package/dist/CallPool.d.ts +32 -0
- package/dist/CallPool.js +226 -0
- package/dist/Console.d.ts +36 -0
- package/dist/Console.js +113 -0
- package/dist/Crawler.d.ts +29 -0
- package/dist/Crawler.js +85 -0
- package/dist/Pool.d.ts +13 -0
- package/dist/Pool.js +70 -0
- package/dist/common.d.ts +14 -0
- package/dist/common.js +150 -0
- package/dist/src/CallPool.js +20 -20
- package/dist/src/Crawler.js +8 -8
- package/dist/src/Pool.js +8 -8
- package/dist/src/common.js +5 -5
- package/dist/utils.d.ts +30 -0
- package/dist/utils.js +209 -0
- package/package.json +6 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ThenableWebDriver } from "selenium-webdriver";
|
|
2
|
+
interface ICrawler {
|
|
3
|
+
drivers: Array<ThenableWebDriver>;
|
|
4
|
+
options: ICrawlerOptions;
|
|
5
|
+
first: boolean;
|
|
6
|
+
crawlFn(driver: ThenableWebDriver, url: string): Promise<any>;
|
|
7
|
+
execute(): Promise<Array<any>>;
|
|
8
|
+
}
|
|
9
|
+
interface ICrawlerOptions {
|
|
10
|
+
concurrent?: number;
|
|
11
|
+
throttling?: boolean | number;
|
|
12
|
+
crawlHub(driver: ThenableWebDriver): Promise<Array<string>>;
|
|
13
|
+
crawlListing(driver: ThenableWebDriver): Promise<any>;
|
|
14
|
+
nextPage(driver: ThenableWebDriver): Promise<any>;
|
|
15
|
+
crawlDetail?(driver: ThenableWebDriver): Promise<any>;
|
|
16
|
+
pageCallback?: (results: Array<Record<string, any>>, index: number) => void;
|
|
17
|
+
callback?: (results: Array<Record<string, any>>, index: number, first: boolean) => void;
|
|
18
|
+
}
|
|
19
|
+
export default class Crawler implements ICrawler {
|
|
20
|
+
drivers: Array<ThenableWebDriver>;
|
|
21
|
+
first: boolean;
|
|
22
|
+
options: ICrawlerOptions;
|
|
23
|
+
constructor(options: ICrawlerOptions);
|
|
24
|
+
execute(): Promise<any>;
|
|
25
|
+
createDrivers(): void;
|
|
26
|
+
destroyDrivers(): void;
|
|
27
|
+
crawlFn(driver: ThenableWebDriver, url: string): Promise<any>;
|
|
28
|
+
}
|
|
29
|
+
export {};
|
package/dist/Crawler.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { pool } from "./common.js";
|
|
2
|
+
import Webdriver from "selenium-webdriver";
|
|
3
|
+
import Console from "./Console.js";
|
|
4
|
+
import { Options } from "selenium-webdriver/chrome.js";
|
|
5
|
+
export default class Crawler {
|
|
6
|
+
drivers = [];
|
|
7
|
+
first = true;
|
|
8
|
+
options = {
|
|
9
|
+
concurrent: 1,
|
|
10
|
+
throttling: false,
|
|
11
|
+
crawlListing: null,
|
|
12
|
+
nextPage: null,
|
|
13
|
+
crawlHub: null,
|
|
14
|
+
crawlDetail: null
|
|
15
|
+
};
|
|
16
|
+
constructor(options) {
|
|
17
|
+
this.options = { ...this.options, ...options };
|
|
18
|
+
}
|
|
19
|
+
execute() {
|
|
20
|
+
this.createDrivers();
|
|
21
|
+
try {
|
|
22
|
+
return new Promise(resolve => {
|
|
23
|
+
this.options.crawlHub(this.drivers[0]).then(urls => {
|
|
24
|
+
const tasks = urls.map((url, index) => () => {
|
|
25
|
+
const driver = this.drivers.pop();
|
|
26
|
+
Console.log(`[${index + 1}/${Math.floor(urls.length)}] Valuating url ${urls[index]}`);
|
|
27
|
+
return this.crawlFn(driver, url)
|
|
28
|
+
.then(results => this.options.callback?.call(null, results, index, this.first))
|
|
29
|
+
.finally(() => {
|
|
30
|
+
this.first = false;
|
|
31
|
+
this.drivers.push(driver);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
pool(tasks, this.options.concurrent).then(results => {
|
|
35
|
+
this.destroyDrivers();
|
|
36
|
+
resolve(results);
|
|
37
|
+
Console.success("FINISHED");
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
console.error(e);
|
|
44
|
+
this.destroyDrivers();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
createDrivers() {
|
|
48
|
+
for (let i = 0; i < this.options.concurrent; i++) {
|
|
49
|
+
this.drivers.push(new Webdriver.Builder()
|
|
50
|
+
.withCapabilities(Webdriver.Capabilities.chrome())
|
|
51
|
+
.setChromeOptions(new Options().headless())
|
|
52
|
+
.forBrowser("chrome")
|
|
53
|
+
.build());
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
destroyDrivers() {
|
|
57
|
+
this.drivers.forEach(driver => driver.close());
|
|
58
|
+
}
|
|
59
|
+
crawlFn(driver, url) {
|
|
60
|
+
const _self = this;
|
|
61
|
+
return new Promise(resolve => {
|
|
62
|
+
driver.get(url).then(() => {
|
|
63
|
+
_self.options.crawlListing(driver).then(listings => {
|
|
64
|
+
if (_self.options.crawlDetail) {
|
|
65
|
+
pool(listings.map((listing) => () => new Promise(resolve1 => {
|
|
66
|
+
driver.get(listing.url).then(() => _self.options.crawlDetail(driver).then(detail => resolve1({ ...listing, ...detail })));
|
|
67
|
+
})), 1)
|
|
68
|
+
.then(details => {
|
|
69
|
+
_self.options.pageCallback?.call(null, details);
|
|
70
|
+
driver.get(url).then(() => _self.options.nextPage(driver)
|
|
71
|
+
.then(url => _self.crawlFn(driver, url).then(acc => resolve([...details, ...acc])))
|
|
72
|
+
.catch(() => resolve(details)));
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
_self.options.pageCallback?.call(null, listings);
|
|
77
|
+
_self.options.nextPage(driver)
|
|
78
|
+
.then(url => _self.crawlFn(driver, url).then(acc => resolve([...listings, ...acc])))
|
|
79
|
+
.catch(() => resolve(listings));
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
package/dist/Pool.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class Pool {
|
|
2
|
+
private name;
|
|
3
|
+
private readonly concurrency;
|
|
4
|
+
private queue;
|
|
5
|
+
private activeCount;
|
|
6
|
+
constructor(name: string, concurrency: number);
|
|
7
|
+
static run: (calls: Array<() => Promise<any>>, concurrency: number) => Promise<any[]>;
|
|
8
|
+
enqueue(...calls: Array<() => Promise<any>>): Promise<any[]>;
|
|
9
|
+
private schedule;
|
|
10
|
+
print(): string;
|
|
11
|
+
updatePrint(): void;
|
|
12
|
+
private _print;
|
|
13
|
+
}
|
package/dist/Pool.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import Console, { COLORS } from "./Console.js";
|
|
2
|
+
const BATCH_SIZE = 1000;
|
|
3
|
+
export class Pool {
|
|
4
|
+
name;
|
|
5
|
+
concurrency;
|
|
6
|
+
queue = [];
|
|
7
|
+
activeCount;
|
|
8
|
+
constructor(name = "default", concurrency) {
|
|
9
|
+
this.name = name;
|
|
10
|
+
this.concurrency = concurrency;
|
|
11
|
+
this.queue = [];
|
|
12
|
+
this.activeCount = 0;
|
|
13
|
+
this.updatePrint();
|
|
14
|
+
}
|
|
15
|
+
static run = async (calls, concurrency) => {
|
|
16
|
+
const pool = new Pool("", concurrency);
|
|
17
|
+
const batchPromises = Array.from({ length: Math.ceil(calls.length / BATCH_SIZE) }, async (_, index) => {
|
|
18
|
+
const start = index * BATCH_SIZE;
|
|
19
|
+
const end = Math.min((index + 1) * BATCH_SIZE, calls.length);
|
|
20
|
+
const batch = calls.slice(start, end);
|
|
21
|
+
return await pool.enqueue(...batch);
|
|
22
|
+
});
|
|
23
|
+
const batchResults = await Promise.all(batchPromises);
|
|
24
|
+
return batchResults.flat();
|
|
25
|
+
};
|
|
26
|
+
enqueue(...calls) {
|
|
27
|
+
return Promise.all(calls.map(call => new Promise((resolve, reject) => {
|
|
28
|
+
this.queue.push({
|
|
29
|
+
call,
|
|
30
|
+
resolve,
|
|
31
|
+
reject,
|
|
32
|
+
});
|
|
33
|
+
this.schedule();
|
|
34
|
+
})));
|
|
35
|
+
}
|
|
36
|
+
schedule() {
|
|
37
|
+
while (this.activeCount < this.concurrency && this.queue.length > 0) {
|
|
38
|
+
const { call, resolve, reject } = this.queue.shift();
|
|
39
|
+
this.activeCount++;
|
|
40
|
+
call()
|
|
41
|
+
.then((result) => {
|
|
42
|
+
resolve(result);
|
|
43
|
+
})
|
|
44
|
+
.catch((error) => {
|
|
45
|
+
reject(error);
|
|
46
|
+
})
|
|
47
|
+
.finally(() => {
|
|
48
|
+
this.activeCount--;
|
|
49
|
+
this.updatePrint();
|
|
50
|
+
this.schedule();
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
print() {
|
|
55
|
+
return this._print;
|
|
56
|
+
}
|
|
57
|
+
updatePrint() {
|
|
58
|
+
const textStart = `${Console.color(this.name.padEnd(16), COLORS.YELLOW)} [POOL][`;
|
|
59
|
+
const textEnd = `][${this.activeCount + this.queue?.length}/${this.concurrency}]`;
|
|
60
|
+
let progress = "";
|
|
61
|
+
const max = process.stdout.columns - Math.max(textStart.length + textEnd.length, process.stdout.columns * 0.5);
|
|
62
|
+
const percent = this.activeCount / this.concurrency * max;
|
|
63
|
+
for (let j = 0; j < max; j++) {
|
|
64
|
+
const char = j < percent ? "=" : " ";
|
|
65
|
+
progress += Console.color(char, j < (max * 0.8) ? COLORS.GREEN : j < (max * 0.9) ? COLORS.YELLOW : COLORS.RED);
|
|
66
|
+
}
|
|
67
|
+
this._print = textStart + progress + textEnd;
|
|
68
|
+
}
|
|
69
|
+
_print;
|
|
70
|
+
}
|
package/dist/common.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare class CustomError extends Error {
|
|
2
|
+
critical: boolean;
|
|
3
|
+
constructor(message: string, critical?: boolean);
|
|
4
|
+
}
|
|
5
|
+
export declare function sequentially(promises: Array<() => Promise<unknown>>, step?: number, exit?: boolean): Promise<unknown>;
|
|
6
|
+
export declare function pool(promises: Array<() => Promise<unknown>>, concurrent: number, stopIfError?: boolean): Promise<any>;
|
|
7
|
+
export declare function promisesFirst<T>(fns: Array<() => Promise<T>>): Promise<T>;
|
|
8
|
+
export declare function readCache(path: string): JSON;
|
|
9
|
+
export declare function writeCache(path: string, data: any): void;
|
|
10
|
+
export declare function readJson(path: string, strict?: boolean): Record<string, any>;
|
|
11
|
+
export declare function readCSV(path: string, delimiter?: string, strict?: boolean): Record<string, any>;
|
|
12
|
+
export declare function groupBy<T>(list: any, keyGetter: any): Map<string, T>;
|
|
13
|
+
export declare function sleep(ms: any, feedback?: boolean): Promise<void>;
|
|
14
|
+
export declare function unique<T>(array: Array<T>): Array<T> | undefined;
|
package/dist/common.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import { PromisePool } from "@supercharge/promise-pool";
|
|
3
|
+
import Console from "./Console.js";
|
|
4
|
+
import { brotliCompressSync, brotliDecompressSync } from "zlib";
|
|
5
|
+
import { parse } from "csv-parse/sync";
|
|
6
|
+
export class CustomError extends Error {
|
|
7
|
+
critical;
|
|
8
|
+
constructor(message, critical = false) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.critical = critical;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function sequentially(promises, step = 1, exit = false) {
|
|
14
|
+
return new Promise(resolve => {
|
|
15
|
+
let results = [];
|
|
16
|
+
const groupedPromises = promises.reduce((acc, fn, index) => {
|
|
17
|
+
const i = Math.floor(index / step);
|
|
18
|
+
if (acc[i])
|
|
19
|
+
acc[i].push(fn);
|
|
20
|
+
else
|
|
21
|
+
acc[i] = [fn];
|
|
22
|
+
return acc;
|
|
23
|
+
}, []);
|
|
24
|
+
groupedPromises.reduce((accumulatorPromise, promise) => {
|
|
25
|
+
return accumulatorPromise.then(() => {
|
|
26
|
+
return Promise.all(promise.map(f => f())).then(res => {
|
|
27
|
+
results = [...results, ...res];
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}, Promise.resolve([])).then(() => {
|
|
31
|
+
resolve(results);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export function pool(promises, concurrent, stopIfError = true) {
|
|
36
|
+
return new Promise((resolve, reject) => {
|
|
37
|
+
PromisePool
|
|
38
|
+
.for(promises)
|
|
39
|
+
.withConcurrency(concurrent)
|
|
40
|
+
.handleError(async (error, user, pool) => {
|
|
41
|
+
if (stopIfError || (error instanceof CustomError && error.critical)) {
|
|
42
|
+
reject(error);
|
|
43
|
+
pool.stop();
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
.useCorrespondingResults()
|
|
47
|
+
.process((fn) => fn())
|
|
48
|
+
.then((poolResult) => resolve(poolResult.results))
|
|
49
|
+
.catch((poolResult) => reject(poolResult.errors));
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
export async function promisesFirst(fns) {
|
|
53
|
+
for await (const f of fns) {
|
|
54
|
+
try {
|
|
55
|
+
const a = await f();
|
|
56
|
+
if (a) {
|
|
57
|
+
return a;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
//console.error(e);
|
|
62
|
+
//
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
throw "No promises resolve";
|
|
66
|
+
}
|
|
67
|
+
export function readCache(path) {
|
|
68
|
+
const brotliPath = `${path}.br`;
|
|
69
|
+
if (!fs.existsSync(brotliPath))
|
|
70
|
+
return null;
|
|
71
|
+
let data;
|
|
72
|
+
try {
|
|
73
|
+
data = JSON.parse(brotliDecompressSync(fs.readFileSync(brotliPath)).toString());
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
data = null;
|
|
77
|
+
}
|
|
78
|
+
return data;
|
|
79
|
+
}
|
|
80
|
+
export function writeCache(path, data) {
|
|
81
|
+
const brotliPath = `${path}.br`;
|
|
82
|
+
try {
|
|
83
|
+
fs.writeFileSync(brotliPath, brotliCompressSync(JSON.stringify(data)), "binary");
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
console.error("Can't pack data to file");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export function readJson(path, strict = false) {
|
|
90
|
+
if (!fs.existsSync(path) && strict)
|
|
91
|
+
throw new Error(`File ${path} not found`);
|
|
92
|
+
if (!fs.existsSync(path))
|
|
93
|
+
return null;
|
|
94
|
+
let json;
|
|
95
|
+
try {
|
|
96
|
+
json = JSON.parse(fs.readFileSync(path, "utf8"));
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
json = null;
|
|
100
|
+
}
|
|
101
|
+
return json;
|
|
102
|
+
}
|
|
103
|
+
export function readCSV(path, delimiter = ",", strict = false) {
|
|
104
|
+
if (!fs.existsSync(path) && strict)
|
|
105
|
+
throw new Error(`File ${path} not found`);
|
|
106
|
+
if (!fs.existsSync(path))
|
|
107
|
+
return null;
|
|
108
|
+
let csv;
|
|
109
|
+
try {
|
|
110
|
+
csv = parse(fs.readFileSync(path, "utf8"), {
|
|
111
|
+
columns: true,
|
|
112
|
+
skip_empty_lines: true,
|
|
113
|
+
delimiter: delimiter
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
csv = null;
|
|
118
|
+
}
|
|
119
|
+
return csv;
|
|
120
|
+
}
|
|
121
|
+
export function groupBy(list, keyGetter) {
|
|
122
|
+
const map = new Map();
|
|
123
|
+
list.forEach((item) => {
|
|
124
|
+
const key = keyGetter(item);
|
|
125
|
+
const collection = map.get(key);
|
|
126
|
+
if (!collection) {
|
|
127
|
+
map.set(key, [item]);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
collection.push(item);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
return map;
|
|
134
|
+
}
|
|
135
|
+
export function sleep(ms, feedback = false) {
|
|
136
|
+
return new Promise((resolve) => {
|
|
137
|
+
const t = performance.now();
|
|
138
|
+
setTimeout(resolve, ms);
|
|
139
|
+
if (feedback)
|
|
140
|
+
setInterval(function () {
|
|
141
|
+
const left = (ms - (performance.now() - t)) / 1000;
|
|
142
|
+
Console.log(`Time left: ${Math.round(left)}s`, true);
|
|
143
|
+
}, 1000);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
export function unique(array) {
|
|
147
|
+
if (!array || !array.length)
|
|
148
|
+
return;
|
|
149
|
+
return Array.from(new Set(array));
|
|
150
|
+
}
|
package/dist/src/CallPool.js
CHANGED
|
@@ -157,28 +157,28 @@ export class CallPool {
|
|
|
157
157
|
this.limiter(() => {
|
|
158
158
|
call()
|
|
159
159
|
.then((result) => {
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
resolve(result);
|
|
161
|
+
})
|
|
162
162
|
.catch((error) => {
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
reject(error);
|
|
164
|
+
})
|
|
165
165
|
.finally(() => {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
166
|
+
this.activeCount--;
|
|
167
|
+
this.clock--;
|
|
168
|
+
if (this.clock < 0) {
|
|
169
|
+
this.clock = this.CLOCK_NUMBER;
|
|
170
|
+
if (this.mean > this.targetMean && this.mean > 10)
|
|
171
|
+
this.concurrency = Math.max(this.concurrency - Math.floor(this.concurrency / 10), this.minConcurrency);
|
|
172
|
+
if (this.mean < this.targetMean || this.mean < 2)
|
|
173
|
+
this.concurrency = Math.min(this.concurrency + Math.ceil(this.concurrency / 20), this.maxConcurrency);
|
|
174
|
+
this.targetMean = this.mean;
|
|
175
|
+
}
|
|
176
|
+
const t = (performance.now() - time) / 1000;
|
|
177
|
+
this.mean = this.mean > 0 ? this.mean + (t - this.mean) / this.WEIGHT : t;
|
|
178
|
+
this.updatePrint();
|
|
179
|
+
//console.debug(this.name, t, this.mean, this.targetMean, `${this.activeCount}/${this.queue.length}`, this.concurrency, `${(60 / this.mean) * this.concurrency} avg/m`);
|
|
180
|
+
this.schedule();
|
|
181
|
+
});
|
|
182
182
|
});
|
|
183
183
|
}
|
|
184
184
|
}
|
package/dist/src/Crawler.js
CHANGED
|
@@ -27,9 +27,9 @@ export default class Crawler {
|
|
|
27
27
|
return this.crawlFn(driver, url)
|
|
28
28
|
.then(results => this.options.callback?.call(null, results, index, this.first))
|
|
29
29
|
.finally(() => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
this.first = false;
|
|
31
|
+
this.drivers.push(driver);
|
|
32
|
+
});
|
|
33
33
|
});
|
|
34
34
|
pool(tasks, this.options.concurrent).then(results => {
|
|
35
35
|
this.destroyDrivers();
|
|
@@ -66,11 +66,11 @@ export default class Crawler {
|
|
|
66
66
|
driver.get(listing.url).then(() => _self.options.crawlDetail(driver).then(detail => resolve1({ ...listing, ...detail })));
|
|
67
67
|
})), 1)
|
|
68
68
|
.then(details => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
_self.options.pageCallback?.call(null, details);
|
|
70
|
+
driver.get(url).then(() => _self.options.nextPage(driver)
|
|
71
|
+
.then(url => _self.crawlFn(driver, url).then(acc => resolve([...details, ...acc])))
|
|
72
|
+
.catch(() => resolve(details)));
|
|
73
|
+
});
|
|
74
74
|
}
|
|
75
75
|
else {
|
|
76
76
|
_self.options.pageCallback?.call(null, listings);
|
package/dist/src/Pool.js
CHANGED
|
@@ -39,16 +39,16 @@ export class Pool {
|
|
|
39
39
|
this.activeCount++;
|
|
40
40
|
call()
|
|
41
41
|
.then((result) => {
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
resolve(result);
|
|
43
|
+
})
|
|
44
44
|
.catch((error) => {
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
reject(error);
|
|
46
|
+
})
|
|
47
47
|
.finally(() => {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
this.activeCount--;
|
|
49
|
+
this.updatePrint();
|
|
50
|
+
this.schedule();
|
|
51
|
+
});
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
print() {
|
package/dist/src/common.js
CHANGED
|
@@ -38,11 +38,11 @@ export function pool(promises, concurrent, stopIfError = true) {
|
|
|
38
38
|
.for(promises)
|
|
39
39
|
.withConcurrency(concurrent)
|
|
40
40
|
.handleError(async (error, user, pool) => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
if (stopIfError || (error instanceof CustomError && error.critical)) {
|
|
42
|
+
reject(error);
|
|
43
|
+
pool.stop();
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
46
|
.useCorrespondingResults()
|
|
47
47
|
.process((fn) => fn())
|
|
48
48
|
.then((poolResult) => resolve(poolResult.results))
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import hash from "object-hash";
|
|
2
|
+
export declare function arrayRotate<T>(arr: Array<T>, n: any): Array<T>;
|
|
3
|
+
export declare function cleanASCII(str: string): string;
|
|
4
|
+
export declare function cleanUpSpecialChars(str: string): string;
|
|
5
|
+
export declare function normalizeString(str: string): string;
|
|
6
|
+
export declare function trimAll(str: string): string;
|
|
7
|
+
export declare function cleanUpSpecialChars2(str: string): string;
|
|
8
|
+
export declare function capitalizeWords(str: string): string;
|
|
9
|
+
export declare function capitalizeFirstLetter(str: string): string | undefined;
|
|
10
|
+
export declare function readJson(path: string): any;
|
|
11
|
+
export declare function orderBy<T extends object>(arr: Array<T>, prop: any): Array<T>;
|
|
12
|
+
export declare function pan(number: number, digits: number): string;
|
|
13
|
+
export declare function uniqWith<T>(array: Array<T>, compareFn: any): Array<T>;
|
|
14
|
+
export declare function pop<T>(array: Array<T>, n: number): Array<T>;
|
|
15
|
+
export declare function splitChunks<T>(array: Array<T>, chunkSize: number): Array<T>;
|
|
16
|
+
export declare function newpopList<T>(list: Array<T>, fn?: (a: T) => boolean): Array<T>;
|
|
17
|
+
export declare function popList<T extends hash.NotUndefined>(list1: Array<T>, list2: Array<T>, fn?: (a: T) => string): Array<T>;
|
|
18
|
+
export declare function diffList<T extends hash.NotUndefined>(list1: Array<T>, list2: Array<T>, fn?: any): Array<T>;
|
|
19
|
+
export declare function truncator(num: any, digits: any): number;
|
|
20
|
+
export declare function parseNumber(num: string, thousand?: string): number;
|
|
21
|
+
export declare function getRandom<T>(arr: Array<T>): T;
|
|
22
|
+
export declare function shuffleArray<T>(array: T[]): T[];
|
|
23
|
+
export declare function extractNumbers(str: string): string | undefined;
|
|
24
|
+
export declare function parseFloatFromString(str: string): number | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Convert MySQL POLYGON string into coordinates array
|
|
27
|
+
* @param {string} polygonString - The POLYGON string from MySQL
|
|
28
|
+
* @returns {Array<Array<number>>} - Array of coordinates in [longitude, latitude] format
|
|
29
|
+
*/
|
|
30
|
+
export declare function convertPolygonStringToArray(polygonString: string): Array<Array<number>>;
|