@rtbnext/core 2.0.0-alpha.1
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/LICENSE +21 -0
- package/README.md +11 -0
- package/dist/abstract/Cache.d.ts +9 -0
- package/dist/abstract/Cache.js +19 -0
- package/dist/abstract/Index.d.ts +22 -0
- package/dist/abstract/Index.js +81 -0
- package/dist/abstract/Job.d.ts +19 -0
- package/dist/abstract/Job.js +49 -0
- package/dist/abstract/Snapshot.d.ts +22 -0
- package/dist/abstract/Snapshot.js +78 -0
- package/dist/bin/cli.d.ts +2 -0
- package/dist/bin/cli.js +25 -0
- package/dist/bin/cron.d.ts +2 -0
- package/dist/bin/cron.js +4 -0
- package/dist/core/Config.d.ts +30 -0
- package/dist/core/Config.js +66 -0
- package/dist/core/Cron.d.ts +12 -0
- package/dist/core/Cron.js +52 -0
- package/dist/core/Fetch.d.ts +28 -0
- package/dist/core/Fetch.js +172 -0
- package/dist/core/Logger.d.ts +30 -0
- package/dist/core/Logger.js +92 -0
- package/dist/core/Queue.d.ts +37 -0
- package/dist/core/Queue.js +136 -0
- package/dist/core/Storage.d.ts +28 -0
- package/dist/core/Storage.js +166 -0
- package/dist/core/Utils.d.ts +33 -0
- package/dist/core/Utils.js +167 -0
- package/dist/interfaces/cache.d.ts +6 -0
- package/dist/interfaces/config.d.ts +21 -0
- package/dist/interfaces/cron.d.ts +3 -0
- package/dist/interfaces/fetch.d.ts +13 -0
- package/dist/interfaces/filter.d.ts +12 -0
- package/dist/interfaces/index.d.ts +30 -0
- package/dist/interfaces/job.d.ts +9 -0
- package/dist/interfaces/list.d.ts +9 -0
- package/dist/interfaces/logger.d.ts +20 -0
- package/dist/interfaces/mover.d.ts +7 -0
- package/dist/interfaces/parser.d.ts +68 -0
- package/dist/interfaces/profile.d.ts +30 -0
- package/dist/interfaces/queue.d.ts +17 -0
- package/dist/interfaces/snapshot.d.ts +16 -0
- package/dist/interfaces/stats.d.ts +45 -0
- package/dist/interfaces/storage.d.ts +16 -0
- package/dist/job/Alias.d.ts +8 -0
- package/dist/job/Alias.js +42 -0
- package/dist/job/Annual.d.ts +8 -0
- package/dist/job/Annual.js +41 -0
- package/dist/job/List.d.ts +11 -0
- package/dist/job/List.js +101 -0
- package/dist/job/Merge.d.ts +10 -0
- package/dist/job/Merge.js +59 -0
- package/dist/job/Move.d.ts +7 -0
- package/dist/job/Move.js +33 -0
- package/dist/job/Performance.d.ts +8 -0
- package/dist/job/Performance.js +27 -0
- package/dist/job/Profile.d.ts +11 -0
- package/dist/job/Profile.js +76 -0
- package/dist/job/Queue.d.ts +8 -0
- package/dist/job/Queue.js +54 -0
- package/dist/job/RTB.d.ts +12 -0
- package/dist/job/RTB.js +121 -0
- package/dist/job/Stats.d.ts +11 -0
- package/dist/job/Stats.js +46 -0
- package/dist/job/Top10.d.ts +9 -0
- package/dist/job/Top10.js +48 -0
- package/dist/job/Wiki.d.ts +9 -0
- package/dist/job/Wiki.js +40 -0
- package/dist/job/index.d.ts +26 -0
- package/dist/job/index.js +26 -0
- package/dist/lib/const.d.ts +31 -0
- package/dist/lib/const.js +74 -0
- package/dist/lib/list.d.ts +90 -0
- package/dist/lib/list.js +72 -0
- package/dist/lib/regex.d.ts +7 -0
- package/dist/lib/regex.js +7 -0
- package/dist/model/Filter.d.ts +28 -0
- package/dist/model/Filter.js +122 -0
- package/dist/model/List.d.ts +12 -0
- package/dist/model/List.js +43 -0
- package/dist/model/ListIndex.d.ts +8 -0
- package/dist/model/ListIndex.js +10 -0
- package/dist/model/Mover.d.ts +15 -0
- package/dist/model/Mover.js +74 -0
- package/dist/model/Profile.d.ts +49 -0
- package/dist/model/Profile.js +181 -0
- package/dist/model/ProfileIndex.d.ts +20 -0
- package/dist/model/ProfileIndex.js +140 -0
- package/dist/model/Stats.d.ts +56 -0
- package/dist/model/Stats.js +435 -0
- package/dist/parser/BillionairesListParser.d.ts +3 -0
- package/dist/parser/BillionairesListParser.js +2 -0
- package/dist/parser/ListParser.d.ts +7 -0
- package/dist/parser/ListParser.js +11 -0
- package/dist/parser/Parser.d.ts +43 -0
- package/dist/parser/Parser.js +146 -0
- package/dist/parser/PersonListParser.d.ts +29 -0
- package/dist/parser/PersonListParser.js +111 -0
- package/dist/parser/ProfileParser.d.ts +44 -0
- package/dist/parser/ProfileParser.js +193 -0
- package/dist/parser/RTBListParser.d.ts +15 -0
- package/dist/parser/RTBListParser.js +91 -0
- package/dist/types/annual.d.ts +7 -0
- package/dist/types/config.d.ts +35 -0
- package/dist/types/fetch.d.ts +3 -0
- package/dist/types/generic.d.ts +10 -0
- package/dist/types/job.d.ts +71 -0
- package/dist/types/list.d.ts +49 -0
- package/dist/types/parser.d.ts +7 -0
- package/dist/types/profile.d.ts +9 -0
- package/dist/types/queue.d.ts +15 -0
- package/dist/types/response.d.ts +183 -0
- package/dist/types/storage.d.ts +3 -0
- package/dist/types/wiki.d.ts +1 -0
- package/dist/utils/Annual.d.ts +7 -0
- package/dist/utils/Annual.js +99 -0
- package/dist/utils/Performance.d.ts +8 -0
- package/dist/utils/Performance.js +39 -0
- package/dist/utils/ProfileManager.d.ts +24 -0
- package/dist/utils/ProfileManager.js +60 -0
- package/dist/utils/ProfileMerger.d.ts +11 -0
- package/dist/utils/ProfileMerger.js +67 -0
- package/dist/utils/Ranking.d.ts +11 -0
- package/dist/utils/Ranking.js +77 -0
- package/dist/utils/Wiki.d.ts +11 -0
- package/dist/utils/Wiki.js +168 -0
- package/package.json +45 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Utils } from '../core/Utils.js';
|
|
2
|
+
import { ListParser } from './ListParser.js';
|
|
3
|
+
import { Parser } from './Parser.js';
|
|
4
|
+
import { ProfileParser } from './ProfileParser.js';
|
|
5
|
+
export class PersonListParser extends ListParser {
|
|
6
|
+
uri() {
|
|
7
|
+
return this.cache('uri', () => Utils.sanitize(this.raw.uri));
|
|
8
|
+
}
|
|
9
|
+
id() {
|
|
10
|
+
return this.cache('id', () => Utils.hash(this.raw.naturalId));
|
|
11
|
+
}
|
|
12
|
+
// --- basic fields ---
|
|
13
|
+
date() {
|
|
14
|
+
return this.cache('date', () => Parser.date(this.raw.date ?? this.raw.timestamp, 'ymd'));
|
|
15
|
+
}
|
|
16
|
+
year() {
|
|
17
|
+
return this.cache('year', () => Number(this.date().slice(0, 4)));
|
|
18
|
+
}
|
|
19
|
+
rank() {
|
|
20
|
+
return this.cache(
|
|
21
|
+
'rank',
|
|
22
|
+
() => Parser.strict(this.raw.rank, 'number') ?? Parser.strict(this.raw.position, 'number')
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
networth() {
|
|
26
|
+
return this.cache('networth', () => Parser.strict(this.raw.finalWorth, 'money'));
|
|
27
|
+
}
|
|
28
|
+
dropOff() {
|
|
29
|
+
return this.cache('dropOff', () => (this.raw.finalWorth ? this.raw.finalWorth < 1e3 : undefined));
|
|
30
|
+
}
|
|
31
|
+
name() {
|
|
32
|
+
return this.cache('name', () =>
|
|
33
|
+
ProfileParser.name(this.raw.person?.name ?? this.raw.personName, this.raw.lastName)
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
// --- profile data ---
|
|
37
|
+
info() {
|
|
38
|
+
return this.cache('info', () => ({
|
|
39
|
+
flags: { dropOff: this.dropOff() },
|
|
40
|
+
...this.name(),
|
|
41
|
+
...Parser.container({
|
|
42
|
+
gender: { value: this.raw.gender, type: 'gender' },
|
|
43
|
+
birthDate: { value: this.raw.birthDate, type: 'date' },
|
|
44
|
+
citizenship: { value: this.raw.countryOfCitizenship, type: 'country' },
|
|
45
|
+
industry: { value: this.raw.industries?.[0], type: 'industry' },
|
|
46
|
+
source: { value: this.raw.source, type: 'list' }
|
|
47
|
+
}),
|
|
48
|
+
residence: this.residence(),
|
|
49
|
+
selfMade: this.selfMade(),
|
|
50
|
+
philanthropyScore: this.philanthropyScore(),
|
|
51
|
+
organization: this.organization()
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
residence() {
|
|
55
|
+
return Parser.location({ country: this.raw.country, state: this.raw.state, city: this.raw.city });
|
|
56
|
+
}
|
|
57
|
+
selfMade() {
|
|
58
|
+
return this.cache('selfMade', () => {
|
|
59
|
+
if (this.raw.selfMadeRank)
|
|
60
|
+
return Parser.container({
|
|
61
|
+
is: { value: this.raw.selfMadeRank > 7, type: 'boolean' },
|
|
62
|
+
rank: { value: this.raw.selfMadeRank, type: 'number' }
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
philanthropyScore() {
|
|
67
|
+
return this.cache('philanthropyScore', () => Parser.strict(this.raw.philanthropyScore, 'number'));
|
|
68
|
+
}
|
|
69
|
+
organization() {
|
|
70
|
+
return this.cache('organization', () => {
|
|
71
|
+
if (this.raw.organization)
|
|
72
|
+
return Parser.container({
|
|
73
|
+
name: { value: this.raw.organization, type: 'string' },
|
|
74
|
+
title: { value: this.raw.title, type: 'string' }
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
bio() {
|
|
79
|
+
return this.cache('bio', () =>
|
|
80
|
+
Parser.container({
|
|
81
|
+
cv: { value: this.raw.bios, type: 'list', args: ['safeStr'] },
|
|
82
|
+
facts: { value: this.raw.abouts, type: 'list', args: ['safeStr'] },
|
|
83
|
+
quotes: { value: [], type: 'list', args: ['safeStr'] }
|
|
84
|
+
})
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
age() {
|
|
88
|
+
return this.cache('age', () => Parser.strict(this.raw.birthDate, 'age'));
|
|
89
|
+
}
|
|
90
|
+
// --- prepare raw list data ---
|
|
91
|
+
static prepareList(res, filter) {
|
|
92
|
+
if (!res?.success || !res.data) throw new Error('Response does not contain valid list data');
|
|
93
|
+
const rawList = res.data.personList.personsLists;
|
|
94
|
+
const entries = rawList
|
|
95
|
+
.filter(filter ?? Boolean)
|
|
96
|
+
.filter(Boolean)
|
|
97
|
+
.sort((a, b) => (a.rank ?? a.position ?? Infinity) - (b.rank ?? b.position ?? Infinity));
|
|
98
|
+
if (!entries.length) throw new Error('Response does not contain any valid list entries');
|
|
99
|
+
return { rawData: res.data, rawList, entries };
|
|
100
|
+
}
|
|
101
|
+
// --- prepare stats ---
|
|
102
|
+
static stats(data) {
|
|
103
|
+
return Parser.container({
|
|
104
|
+
date: { value: data.date, type: 'string' },
|
|
105
|
+
count: { value: data.count, type: 'number' },
|
|
106
|
+
total: { value: data.total, type: 'money' },
|
|
107
|
+
woman: { value: data.woman, type: 'number' },
|
|
108
|
+
quota: { value: ((data.woman ?? 0) / (data.count ?? 1)) * 100, type: 'pct' }
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
TEducation,
|
|
3
|
+
TImage,
|
|
4
|
+
TLocation,
|
|
5
|
+
TOrganization,
|
|
6
|
+
TRelation,
|
|
7
|
+
TSelfMade
|
|
8
|
+
} from '@rtbnext/schema/src/base/generic';
|
|
9
|
+
import type { TProfileBio, TProfileFlags, TProfileInfo, TProfileName } from '@rtbnext/schema/src/model/profile';
|
|
10
|
+
import { Cache } from '../abstract/Cache.js';
|
|
11
|
+
import type { IProfileParser } from '../interfaces/parser';
|
|
12
|
+
import type { TProfileResponse } from '../types/response';
|
|
13
|
+
export declare class ProfileParser extends Cache implements IProfileParser {
|
|
14
|
+
private readonly raw;
|
|
15
|
+
private readonly lists;
|
|
16
|
+
constructor(res: TProfileResponse);
|
|
17
|
+
rawData(): TProfileResponse['person'];
|
|
18
|
+
sortedLists(): TProfileResponse['person']['personLists'];
|
|
19
|
+
uri(): string;
|
|
20
|
+
id(): string;
|
|
21
|
+
aliases(): string[];
|
|
22
|
+
name(): { name: TProfileName; family: boolean };
|
|
23
|
+
flags(): TProfileFlags;
|
|
24
|
+
info(): TProfileInfo;
|
|
25
|
+
citizenship(): string | undefined;
|
|
26
|
+
residence(): TLocation | undefined;
|
|
27
|
+
birthPlace(): TLocation | undefined;
|
|
28
|
+
education(): TEducation[];
|
|
29
|
+
selfMade(): TSelfMade;
|
|
30
|
+
philanthropyScore(): number | undefined;
|
|
31
|
+
organization(): TOrganization | undefined;
|
|
32
|
+
bio(): TProfileBio;
|
|
33
|
+
cv(): string[];
|
|
34
|
+
facts(): string[];
|
|
35
|
+
quotes(): string[];
|
|
36
|
+
related(): TRelation[];
|
|
37
|
+
media(): TImage[];
|
|
38
|
+
static name(
|
|
39
|
+
value: unknown,
|
|
40
|
+
lastName?: unknown,
|
|
41
|
+
firstName?: unknown,
|
|
42
|
+
asianFormat?: boolean
|
|
43
|
+
): { name: TProfileName; family: boolean };
|
|
44
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { Cache } from '../abstract/Cache.js';
|
|
2
|
+
import { Utils } from '../core/Utils.js';
|
|
3
|
+
import { RelationType } from '../lib/const.js';
|
|
4
|
+
import { REGEX_FAMILY, REGEX_SPACE_DELIMITER } from '../lib/regex.js';
|
|
5
|
+
import { Parser } from './Parser.js';
|
|
6
|
+
export class ProfileParser extends Cache {
|
|
7
|
+
raw;
|
|
8
|
+
lists;
|
|
9
|
+
constructor(res) {
|
|
10
|
+
super();
|
|
11
|
+
this.raw = res.person;
|
|
12
|
+
this.lists = res.person.personLists.sort((a, b) => Number(b.date ?? 0) - Number(a.date ?? 0));
|
|
13
|
+
}
|
|
14
|
+
// --- raw data ---
|
|
15
|
+
rawData() {
|
|
16
|
+
return this.raw;
|
|
17
|
+
}
|
|
18
|
+
sortedLists() {
|
|
19
|
+
return this.lists;
|
|
20
|
+
}
|
|
21
|
+
// --- URIs & IDs ---
|
|
22
|
+
uri() {
|
|
23
|
+
return this.cache('uri', () => Utils.sanitize(this.raw.uri));
|
|
24
|
+
}
|
|
25
|
+
id() {
|
|
26
|
+
return this.cache('id', () => Utils.hash(this.raw.naturalId));
|
|
27
|
+
}
|
|
28
|
+
aliases() {
|
|
29
|
+
return this.cache('aliases', () => {
|
|
30
|
+
const uri = this.uri();
|
|
31
|
+
return this.raw.uris
|
|
32
|
+
.filter(Boolean)
|
|
33
|
+
.map(i => Utils.sanitize(i))
|
|
34
|
+
.filter(i => i !== uri)
|
|
35
|
+
.sort();
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
// --- profile info ---
|
|
39
|
+
name() {
|
|
40
|
+
return this.cache('name', () =>
|
|
41
|
+
ProfileParser.name(this.raw.name, this.raw.lastName, this.raw.firstName, Parser.boolean(this.raw.asianFormat))
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
flags() {
|
|
45
|
+
return this.cache('flags', () =>
|
|
46
|
+
Parser.container({
|
|
47
|
+
deceased: { value: this.raw.deceased, type: 'boolean' },
|
|
48
|
+
family: { value: this.name().family, type: 'boolean' },
|
|
49
|
+
dropOff: { value: this.raw.dropOff, type: 'boolean' },
|
|
50
|
+
embargo: { value: this.raw.embargo, type: 'boolean' }
|
|
51
|
+
})
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
info() {
|
|
55
|
+
return this.cache('info', () => ({
|
|
56
|
+
...Parser.container({
|
|
57
|
+
gender: { value: this.raw.gender, type: 'gender' },
|
|
58
|
+
birthDate: { value: this.raw.birthDate, type: 'date' },
|
|
59
|
+
maritalStatus: { value: this.raw.maritalStatus, type: 'maritalStatus' },
|
|
60
|
+
children: { value: this.raw.numberOfChildren, type: 'number' },
|
|
61
|
+
industry: { value: this.raw.industries?.[0], type: 'industry' },
|
|
62
|
+
source: { value: this.raw.source, type: 'list' }
|
|
63
|
+
}),
|
|
64
|
+
flags: this.flags(),
|
|
65
|
+
name: this.name().name,
|
|
66
|
+
citizenship: this.citizenship(),
|
|
67
|
+
birthPlace: this.birthPlace(),
|
|
68
|
+
residence: this.residence(),
|
|
69
|
+
education: this.education(),
|
|
70
|
+
selfMade: this.selfMade(),
|
|
71
|
+
philanthropyScore: this.philanthropyScore(),
|
|
72
|
+
organization: this.organization()
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
citizenship() {
|
|
76
|
+
return this.cache('citizenship', () =>
|
|
77
|
+
Parser.strict(this.raw.countryOfCitizenship ?? this.raw.countryOfResidence, 'country')
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
residence() {
|
|
81
|
+
return Parser.location({
|
|
82
|
+
country: this.raw.countryOfResidence,
|
|
83
|
+
state: this.raw.stateProvince,
|
|
84
|
+
city: this.raw.city
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
birthPlace() {
|
|
88
|
+
return Parser.location({ country: this.raw.birthCountry, state: this.raw.birthState, city: this.raw.birthCity });
|
|
89
|
+
}
|
|
90
|
+
education() {
|
|
91
|
+
return this.cache('education', () =>
|
|
92
|
+
(this.raw.educations ?? [])
|
|
93
|
+
.filter(Boolean)
|
|
94
|
+
.map(item =>
|
|
95
|
+
Parser.container({
|
|
96
|
+
school: { value: item.school, type: 'string' },
|
|
97
|
+
degree: { value: item.degree, type: 'string' }
|
|
98
|
+
})
|
|
99
|
+
)
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
selfMade() {
|
|
103
|
+
return this.cache('selfMade', () =>
|
|
104
|
+
Parser.container({
|
|
105
|
+
type: { value: this.raw.selfMadeType, type: 'string' },
|
|
106
|
+
is: { value: this.raw.selfMade, type: 'boolean' },
|
|
107
|
+
rank: { value: this.raw.selfMadeRank, type: 'number' }
|
|
108
|
+
})
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
philanthropyScore() {
|
|
112
|
+
return this.cache('philanthropyScore', () => Utils.aggregate(this.lists, 'philanthropyScore', 'first'));
|
|
113
|
+
}
|
|
114
|
+
organization() {
|
|
115
|
+
return this.cache('organization', () => {
|
|
116
|
+
if (this.raw.organization)
|
|
117
|
+
return Parser.container({
|
|
118
|
+
name: { value: this.raw.organization, type: 'string' },
|
|
119
|
+
title: { value: this.raw.title, type: 'string' }
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
// --- bio ---
|
|
124
|
+
bio() {
|
|
125
|
+
return this.cache('bio', () => ({ cv: this.cv(), facts: this.facts(), quotes: this.quotes() }));
|
|
126
|
+
}
|
|
127
|
+
cv() {
|
|
128
|
+
return this.cache('cv', () => Parser.list(Utils.aggregate(this.lists, 'bios', 'first'), 'safeStr'));
|
|
129
|
+
}
|
|
130
|
+
facts() {
|
|
131
|
+
return this.cache('facts', () => Parser.list(Utils.aggregate(this.lists, 'abouts', 'first'), 'safeStr'));
|
|
132
|
+
}
|
|
133
|
+
quotes() {
|
|
134
|
+
return this.cache('quotes', () => Parser.list([this.raw.quote ?? ''], 'safeStr'));
|
|
135
|
+
}
|
|
136
|
+
// --- related entries ---
|
|
137
|
+
related() {
|
|
138
|
+
return this.cache('related', () =>
|
|
139
|
+
(this.raw.relatedEntities ?? [])
|
|
140
|
+
.filter(Boolean)
|
|
141
|
+
.map(item => ({
|
|
142
|
+
uri: item.uri ? Utils.sanitize(item.uri) : undefined,
|
|
143
|
+
...Parser.container({
|
|
144
|
+
type: { value: item.type, type: 'map', args: [RelationType] },
|
|
145
|
+
name: { value: item.name, type: 'string' },
|
|
146
|
+
relation: { value: item.relationshipType, type: 'string' }
|
|
147
|
+
})
|
|
148
|
+
}))
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
// --- media ---
|
|
152
|
+
media() {
|
|
153
|
+
return this.cache('media', () =>
|
|
154
|
+
(this.raw.listImages ?? [])
|
|
155
|
+
.filter(Boolean)
|
|
156
|
+
.map(item =>
|
|
157
|
+
Parser.container({
|
|
158
|
+
url: { value: item.uri, type: 'string' },
|
|
159
|
+
credits: { value: item.credit, type: 'safeStr' },
|
|
160
|
+
file: { value: item.image, type: 'string' },
|
|
161
|
+
caption: { value: item.caption, type: 'safeStr' },
|
|
162
|
+
desc: { value: item.description, type: 'safeStr' }
|
|
163
|
+
})
|
|
164
|
+
)
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
// --- static methods ---
|
|
168
|
+
static name(value, lastName = undefined, firstName = undefined, asianFormat = false) {
|
|
169
|
+
const name = Parser.string(value);
|
|
170
|
+
const clean = name.replace(REGEX_FAMILY, '').trim();
|
|
171
|
+
const family = REGEX_FAMILY.test(name);
|
|
172
|
+
const parts = clean.split(REGEX_SPACE_DELIMITER).filter(Boolean);
|
|
173
|
+
const fN = firstName
|
|
174
|
+
? Parser.string(firstName)
|
|
175
|
+
: asianFormat
|
|
176
|
+
? parts.slice(1).join(' ')
|
|
177
|
+
: parts.slice(0, -1).join(' ');
|
|
178
|
+
const lN = lastName
|
|
179
|
+
? Parser.string(lastName).replace(REGEX_FAMILY, '')
|
|
180
|
+
: asianFormat
|
|
181
|
+
? (parts[0] ?? '')
|
|
182
|
+
: (parts.pop() ?? '');
|
|
183
|
+
return {
|
|
184
|
+
family,
|
|
185
|
+
name: {
|
|
186
|
+
fullName: clean + (family ? ' & family' : ''),
|
|
187
|
+
shortName: `${fN.split(' ')[0]} ${lN}`.trim(),
|
|
188
|
+
lastName: lN,
|
|
189
|
+
firstName: fN
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { TAsset, TRealtime } from '@rtbnext/schema/src/base/assets';
|
|
2
|
+
import type { TChangeFlag } from '@rtbnext/schema/src/base/const';
|
|
3
|
+
import type { TProfileData } from '@rtbnext/schema/src/model/profile';
|
|
4
|
+
import type { TGenericStats } from '@rtbnext/schema/src/model/stats';
|
|
5
|
+
import type { IRTBListParser } from '../interfaces/parser';
|
|
6
|
+
import { PersonListParser } from './PersonListParser.js';
|
|
7
|
+
import { TPreparedList } from '../types/list';
|
|
8
|
+
import { TListResponse, TPersonListEntry, TResponse } from '../types/response';
|
|
9
|
+
export declare class RTBListParser extends PersonListParser implements IRTBListParser {
|
|
10
|
+
assets(): TAsset[];
|
|
11
|
+
realtime(data?: Partial<TProfileData>, prev?: string, next?: string): TRealtime | undefined;
|
|
12
|
+
rankDiff(data?: Partial<TProfileData>): { flag: TChangeFlag; rankDiff?: number };
|
|
13
|
+
static prepareList(res: TResponse<TListResponse<TPersonListEntry>>): TPreparedList<TPersonListEntry>;
|
|
14
|
+
static stats(data: Partial<TGenericStats>): TGenericStats;
|
|
15
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Parser } from './Parser.js';
|
|
2
|
+
import { PersonListParser } from './PersonListParser.js';
|
|
3
|
+
export class RTBListParser extends PersonListParser {
|
|
4
|
+
assets() {
|
|
5
|
+
return this.cache('assets', () =>
|
|
6
|
+
(this.raw.financialAssets ?? []).map(a =>
|
|
7
|
+
Parser.container({
|
|
8
|
+
type: { value: 'public', type: 'string' },
|
|
9
|
+
label: { value: a.companyName, type: 'string' },
|
|
10
|
+
value: {
|
|
11
|
+
value:
|
|
12
|
+
a.numberOfShares && (a.currentPrice ?? a.sharePrice)
|
|
13
|
+
? (a.numberOfShares * (a.currentPrice ?? a.sharePrice)) / 1e6
|
|
14
|
+
: undefined,
|
|
15
|
+
type: 'money'
|
|
16
|
+
},
|
|
17
|
+
info: {
|
|
18
|
+
value:
|
|
19
|
+
a.exchange && a.ticker
|
|
20
|
+
? Parser.container({
|
|
21
|
+
exchange: { value: a.exchange, type: 'string' },
|
|
22
|
+
ticker: { value: a.ticker, type: 'string' },
|
|
23
|
+
shares: { value: a.numberOfShares, type: 'number' },
|
|
24
|
+
price: { value: a.currentPrice ?? a.sharePrice, type: 'number', args: [6] },
|
|
25
|
+
currency: { value: a.currencyCode, type: 'string' },
|
|
26
|
+
exRate: { value: a.exchangeRate, type: 'number', args: [6] }
|
|
27
|
+
})
|
|
28
|
+
: undefined,
|
|
29
|
+
type: 'container'
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
)
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
realtime(data, prev, next) {
|
|
36
|
+
return this.cache('realtime', () => {
|
|
37
|
+
if (!this.raw.finalWorth) return;
|
|
38
|
+
const cur = this.raw.finalWorth;
|
|
39
|
+
const lastDay = data?.realtime?.networth;
|
|
40
|
+
const lastYear = data?.annual?.find(i => i.year === this.year() - 1)?.networth?.last;
|
|
41
|
+
let diff;
|
|
42
|
+
return {
|
|
43
|
+
date: this.date(),
|
|
44
|
+
rank: this.rank(),
|
|
45
|
+
networth: this.networth(),
|
|
46
|
+
prev,
|
|
47
|
+
next,
|
|
48
|
+
today: lastDay
|
|
49
|
+
? { value: Parser.money((diff = cur - lastDay)), percent: Parser.pct((diff / lastDay) * 100) }
|
|
50
|
+
: undefined,
|
|
51
|
+
ytd: lastYear
|
|
52
|
+
? { value: Parser.money((diff = cur - lastYear)), percent: Parser.pct((diff / lastYear) * 100) }
|
|
53
|
+
: undefined
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
rankDiff(data) {
|
|
58
|
+
if (this.rank() === undefined || (this.networth() ?? 0) < 1000) return { flag: 'dropoff' };
|
|
59
|
+
const item = data?.annual?.find(i => i.year === this.year() - 1);
|
|
60
|
+
if (!item) return { flag: data?.annual?.filter(i => i.rank?.last !== undefined).length ? 'returned' : 'new' };
|
|
61
|
+
if (item.rank?.last === undefined) return { flag: 'unknown' };
|
|
62
|
+
const rankDiff = item.rank.last - this.rank();
|
|
63
|
+
return { flag: rankDiff > 0 ? 'up' : rankDiff < 0 ? 'down' : 'unchanged', rankDiff };
|
|
64
|
+
}
|
|
65
|
+
// --- prepare raw list data ---
|
|
66
|
+
static prepareList(res) {
|
|
67
|
+
return super.prepareList(res, item => !!(item.rank || item.position) && !!item.finalWorth);
|
|
68
|
+
}
|
|
69
|
+
// --- prepare stats ---
|
|
70
|
+
static stats(data) {
|
|
71
|
+
return {
|
|
72
|
+
...super.stats(data),
|
|
73
|
+
...Parser.container({
|
|
74
|
+
today: {
|
|
75
|
+
value: Parser.container({
|
|
76
|
+
value: { value: data.today?.value, type: 'money' },
|
|
77
|
+
percent: { value: data.today?.percent, type: 'pct' }
|
|
78
|
+
}),
|
|
79
|
+
type: 'container'
|
|
80
|
+
},
|
|
81
|
+
ytd: {
|
|
82
|
+
value: Parser.container({
|
|
83
|
+
value: { value: data.ytd?.value, type: 'money' },
|
|
84
|
+
percent: { value: data.ytd?.percent, type: 'pct' }
|
|
85
|
+
}),
|
|
86
|
+
type: 'container'
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export type TLoggingLevel = 'error' | 'warn' | 'info' | 'debug';
|
|
2
|
+
|
|
3
|
+
export type TLoggingConfig = { level: TLoggingLevel; console?: boolean; file?: boolean };
|
|
4
|
+
|
|
5
|
+
export type TJobConfig = { silent: boolean; safeMode: boolean };
|
|
6
|
+
|
|
7
|
+
export type TCronConfig = { timezone: string };
|
|
8
|
+
|
|
9
|
+
export type TStorageConfig = { baseDir: string; compression: boolean };
|
|
10
|
+
|
|
11
|
+
export type TFetchConfig = {
|
|
12
|
+
endpoints: { profile: string; list: string; wikipedia: string; commons: string; wikidata: string; wayback: string };
|
|
13
|
+
headers: Record<string, string>;
|
|
14
|
+
apiAgent: string;
|
|
15
|
+
agentPool: string[];
|
|
16
|
+
rateLimit: {
|
|
17
|
+
batchSize: number;
|
|
18
|
+
timeout: number;
|
|
19
|
+
retries: number;
|
|
20
|
+
idle: number;
|
|
21
|
+
requestDelay: { max: number; min: number };
|
|
22
|
+
};
|
|
23
|
+
requests: { list: { chunkSize: number; maxRequests: number } };
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type TQueueConfig = { tsThreshold: number; maxSize: number; defaultPrio: number };
|
|
27
|
+
|
|
28
|
+
export type TConfigObject = {
|
|
29
|
+
logging: TLoggingConfig;
|
|
30
|
+
job: TJobConfig;
|
|
31
|
+
cron: TCronConfig;
|
|
32
|
+
storage: TStorageConfig;
|
|
33
|
+
fetch: TFetchConfig;
|
|
34
|
+
queue: TQueueConfig;
|
|
35
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TIndustry, TMaritalStatus } from '@rtbnext/schema/src/base/const';
|
|
2
|
+
|
|
3
|
+
export type TAggregator = 'all' | 'first' | 'last' | 'sum' | 'min' | 'max' | 'mean' | ((values: readonly T[K][]) => R);
|
|
4
|
+
|
|
5
|
+
export type TObjOperator = 'set' | 'inc' | 'max' | 'min' | 'append' | 'prepend' | ((curr: any, p: string) => any);
|
|
6
|
+
|
|
7
|
+
export type TMeasuredResult<R> = { result: R; ms: number };
|
|
8
|
+
|
|
9
|
+
export type TMaritalStatusResolver = Record<string, TMaritalStatus>;
|
|
10
|
+
export type TIndustryResolver = Record<string, TIndustry>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { Expand } from 'devtypes/types/util';
|
|
2
|
+
|
|
3
|
+
import type { IJob } from '../interfaces/job';
|
|
4
|
+
import type { TQueueType } from './queue';
|
|
5
|
+
|
|
6
|
+
export type TJobClsOptions<T extends object = {}> = Expand<{ silent?: boolean; safeMode?: boolean } & T>;
|
|
7
|
+
|
|
8
|
+
export type TAliasJobOptions = TJobClsOptions<{
|
|
9
|
+
removeGlobal?: string[];
|
|
10
|
+
profile?: string;
|
|
11
|
+
add?: string[];
|
|
12
|
+
remove?: string[];
|
|
13
|
+
}>;
|
|
14
|
+
|
|
15
|
+
export type TAnnualJobOptions = TJobClsOptions<{ year: number; profiles?: string[] }>;
|
|
16
|
+
|
|
17
|
+
export type TListJobOptions = TJobClsOptions<{
|
|
18
|
+
override?: boolean;
|
|
19
|
+
profileUpdate?: boolean;
|
|
20
|
+
list?: string;
|
|
21
|
+
year?: string;
|
|
22
|
+
name?: string;
|
|
23
|
+
desc?: string;
|
|
24
|
+
}>;
|
|
25
|
+
|
|
26
|
+
export type TMergeJobOptions = TJobClsOptions<{
|
|
27
|
+
list?: string[];
|
|
28
|
+
source?: string;
|
|
29
|
+
target?: string;
|
|
30
|
+
dryRun?: boolean;
|
|
31
|
+
force?: boolean;
|
|
32
|
+
makeAlias?: boolean;
|
|
33
|
+
}>;
|
|
34
|
+
|
|
35
|
+
export type TMoveJobOptions = TJobClsOptions<{ source: string; target: string; makeAlias?: boolean }>;
|
|
36
|
+
|
|
37
|
+
export type TProfileJobOptions = TJobClsOptions<{
|
|
38
|
+
profiles?: string[];
|
|
39
|
+
replace?: boolean;
|
|
40
|
+
skipRanking?: boolean;
|
|
41
|
+
skipWiki?: boolean;
|
|
42
|
+
}>;
|
|
43
|
+
|
|
44
|
+
export type TQueueJobOptions = TJobClsOptions<{
|
|
45
|
+
type: TQueueType;
|
|
46
|
+
add?: string[];
|
|
47
|
+
remove?: string[];
|
|
48
|
+
prio?: number;
|
|
49
|
+
args?: any;
|
|
50
|
+
clear?: boolean;
|
|
51
|
+
}>;
|
|
52
|
+
|
|
53
|
+
export type TTop10JobOptions = TJobClsOptions<{ date?: readonly [year: number, month: number] }>;
|
|
54
|
+
|
|
55
|
+
export type TWikiJobOptions = TJobClsOptions<{ profile: string; assign?: string }>;
|
|
56
|
+
|
|
57
|
+
export type TJobOption = { name: string; desc: string; parser?: (value: string) => any; required?: boolean };
|
|
58
|
+
|
|
59
|
+
export type TJobOptions = ReadonlyArray<TJobOption>;
|
|
60
|
+
|
|
61
|
+
export type TCommandJob = { readonly id: string; desc: string; options?: TJobOptions };
|
|
62
|
+
|
|
63
|
+
export type TCronJob<T extends object = {}> = ReadonlyArray<{ cronexpr: string; options?: (date: Date) => T }>;
|
|
64
|
+
|
|
65
|
+
export interface TJobCls<T extends TJobClsOptions = TJobClsOptions> {
|
|
66
|
+
readonly command: TCommandJob;
|
|
67
|
+
readonly cron?: TCronJob<T>;
|
|
68
|
+
new (options: T): IJob;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type TJobRegistry = ReadonlyArray<TJobCls<any>>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { TRealtime } from '@rtbnext/schema/src/base/assets';
|
|
2
|
+
import type { TChangeFlag } from '@rtbnext/schema/src/base/const';
|
|
3
|
+
import type {
|
|
4
|
+
TBillionairesListItem,
|
|
5
|
+
TListIndexItem,
|
|
6
|
+
TPersonListItem,
|
|
7
|
+
TRTBListItem
|
|
8
|
+
} from '@rtbnext/schema/src/model/list';
|
|
9
|
+
import type { TProfileData } from '@rtbnext/schema/src/model/profile';
|
|
10
|
+
import type { Expand } from 'devtypes/types/util';
|
|
11
|
+
|
|
12
|
+
import type { IBillionairesListParser, IListParser, IPersonListParser, IRTBListParser } from '../interfaces/parser';
|
|
13
|
+
import type { TListResponse } from './response';
|
|
14
|
+
|
|
15
|
+
export type TListTypes = 'rtb' | 'billionaires' | 'person';
|
|
16
|
+
|
|
17
|
+
export type TListParserCls<T extends IListParser> = new (...args: any[]) => T;
|
|
18
|
+
|
|
19
|
+
export type TListIndexItemCtx = { name: string; desc: string };
|
|
20
|
+
|
|
21
|
+
export type TPersonListItemCtx = { parsed: IPersonListParser; profileData: Partial<TProfileData> };
|
|
22
|
+
|
|
23
|
+
export type TRTBListItemCtx = Expand<
|
|
24
|
+
TPersonListItemCtx & { parsed: IRTBListParser; flag: TChangeFlag; rankDiff?: number; realtime?: TRealtime }
|
|
25
|
+
>;
|
|
26
|
+
|
|
27
|
+
export type TRTBListConfig = {
|
|
28
|
+
lists: readonly ['rtb'];
|
|
29
|
+
parser: TListParserCls<IRTBListParser>;
|
|
30
|
+
indexItem(): TListIndexItem;
|
|
31
|
+
listItem(ctx: TRTBListItemCtx): TRTBListItem;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type TBillionairesListConfig = {
|
|
35
|
+
lists: readonly ['billionaires', 'forbes-400'];
|
|
36
|
+
parser: TListParserCls<IBillionairesListParser>;
|
|
37
|
+
indexItem(uri: string, ctx: TListIndexItemCtx): TListIndexItem;
|
|
38
|
+
listItem(ctx: TPersonListItemCtx): TBillionairesListItem;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type TPersonListConfig = {
|
|
42
|
+
parser: TListParserCls<IPersonListParser>;
|
|
43
|
+
indexItem(uri: string, ctx: TListIndexItemCtx): TListIndexItem;
|
|
44
|
+
listItem(ctx: TPersonListItemCtx): TPersonListItem;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type TListConfig = { rtb: TRTBListConfig; billionaires: TBillionairesListConfig; person: TPersonListConfig };
|
|
48
|
+
|
|
49
|
+
export type TPreparedList<T extends object> = { rawData: TListResponse<T>; rawList: T[]; entries: T[] };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Parser } from '../parser/Parser.js';
|
|
2
|
+
|
|
3
|
+
export type TParserDateType = 'iso' | 'ymd' | 'ym' | 'y';
|
|
4
|
+
|
|
5
|
+
export type TParserMethod = Exclude<keyof typeof Parser, 'prototype'>;
|
|
6
|
+
|
|
7
|
+
export type TParserContainer = { value: unknown; type: TParserMethod; strict?: boolean; args?: unknown[] };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { IProfile } from '../interfaces/profile';
|
|
2
|
+
|
|
3
|
+
export type TProfileOperation = 'create' | 'update' | 'move';
|
|
4
|
+
|
|
5
|
+
export type TProfileUpdateMode = 'setData' | 'updateData' | 'createOnly';
|
|
6
|
+
|
|
7
|
+
export type TProfileLookupResult = { profile: IProfile | false; isExisting: boolean; isSimilar: boolean };
|
|
8
|
+
|
|
9
|
+
export type TProfileProcessResult = { profile: IProfile | false; action: TProfileOperation; success: boolean };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type TQueueType = 'profile' | 'list';
|
|
2
|
+
|
|
3
|
+
export type TQueueOptions = { uriLike: string; args?: Record<string, unknown>; prio?: number };
|
|
4
|
+
|
|
5
|
+
export type TQueueItem = {
|
|
6
|
+
readonly key: string;
|
|
7
|
+
readonly uri: string;
|
|
8
|
+
ts: string;
|
|
9
|
+
args?: Record<string, unknown>;
|
|
10
|
+
prio?: number;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type TQueue = Map<string, TQueueItem>;
|
|
14
|
+
|
|
15
|
+
export type TQueueStorage = TQueueItem[];
|