@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.
Files changed (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +11 -0
  3. package/dist/abstract/Cache.d.ts +9 -0
  4. package/dist/abstract/Cache.js +19 -0
  5. package/dist/abstract/Index.d.ts +22 -0
  6. package/dist/abstract/Index.js +81 -0
  7. package/dist/abstract/Job.d.ts +19 -0
  8. package/dist/abstract/Job.js +49 -0
  9. package/dist/abstract/Snapshot.d.ts +22 -0
  10. package/dist/abstract/Snapshot.js +78 -0
  11. package/dist/bin/cli.d.ts +2 -0
  12. package/dist/bin/cli.js +25 -0
  13. package/dist/bin/cron.d.ts +2 -0
  14. package/dist/bin/cron.js +4 -0
  15. package/dist/core/Config.d.ts +30 -0
  16. package/dist/core/Config.js +66 -0
  17. package/dist/core/Cron.d.ts +12 -0
  18. package/dist/core/Cron.js +52 -0
  19. package/dist/core/Fetch.d.ts +28 -0
  20. package/dist/core/Fetch.js +172 -0
  21. package/dist/core/Logger.d.ts +30 -0
  22. package/dist/core/Logger.js +92 -0
  23. package/dist/core/Queue.d.ts +37 -0
  24. package/dist/core/Queue.js +136 -0
  25. package/dist/core/Storage.d.ts +28 -0
  26. package/dist/core/Storage.js +166 -0
  27. package/dist/core/Utils.d.ts +33 -0
  28. package/dist/core/Utils.js +167 -0
  29. package/dist/interfaces/cache.d.ts +6 -0
  30. package/dist/interfaces/config.d.ts +21 -0
  31. package/dist/interfaces/cron.d.ts +3 -0
  32. package/dist/interfaces/fetch.d.ts +13 -0
  33. package/dist/interfaces/filter.d.ts +12 -0
  34. package/dist/interfaces/index.d.ts +30 -0
  35. package/dist/interfaces/job.d.ts +9 -0
  36. package/dist/interfaces/list.d.ts +9 -0
  37. package/dist/interfaces/logger.d.ts +20 -0
  38. package/dist/interfaces/mover.d.ts +7 -0
  39. package/dist/interfaces/parser.d.ts +68 -0
  40. package/dist/interfaces/profile.d.ts +30 -0
  41. package/dist/interfaces/queue.d.ts +17 -0
  42. package/dist/interfaces/snapshot.d.ts +16 -0
  43. package/dist/interfaces/stats.d.ts +45 -0
  44. package/dist/interfaces/storage.d.ts +16 -0
  45. package/dist/job/Alias.d.ts +8 -0
  46. package/dist/job/Alias.js +42 -0
  47. package/dist/job/Annual.d.ts +8 -0
  48. package/dist/job/Annual.js +41 -0
  49. package/dist/job/List.d.ts +11 -0
  50. package/dist/job/List.js +101 -0
  51. package/dist/job/Merge.d.ts +10 -0
  52. package/dist/job/Merge.js +59 -0
  53. package/dist/job/Move.d.ts +7 -0
  54. package/dist/job/Move.js +33 -0
  55. package/dist/job/Performance.d.ts +8 -0
  56. package/dist/job/Performance.js +27 -0
  57. package/dist/job/Profile.d.ts +11 -0
  58. package/dist/job/Profile.js +76 -0
  59. package/dist/job/Queue.d.ts +8 -0
  60. package/dist/job/Queue.js +54 -0
  61. package/dist/job/RTB.d.ts +12 -0
  62. package/dist/job/RTB.js +121 -0
  63. package/dist/job/Stats.d.ts +11 -0
  64. package/dist/job/Stats.js +46 -0
  65. package/dist/job/Top10.d.ts +9 -0
  66. package/dist/job/Top10.js +48 -0
  67. package/dist/job/Wiki.d.ts +9 -0
  68. package/dist/job/Wiki.js +40 -0
  69. package/dist/job/index.d.ts +26 -0
  70. package/dist/job/index.js +26 -0
  71. package/dist/lib/const.d.ts +31 -0
  72. package/dist/lib/const.js +74 -0
  73. package/dist/lib/list.d.ts +90 -0
  74. package/dist/lib/list.js +72 -0
  75. package/dist/lib/regex.d.ts +7 -0
  76. package/dist/lib/regex.js +7 -0
  77. package/dist/model/Filter.d.ts +28 -0
  78. package/dist/model/Filter.js +122 -0
  79. package/dist/model/List.d.ts +12 -0
  80. package/dist/model/List.js +43 -0
  81. package/dist/model/ListIndex.d.ts +8 -0
  82. package/dist/model/ListIndex.js +10 -0
  83. package/dist/model/Mover.d.ts +15 -0
  84. package/dist/model/Mover.js +74 -0
  85. package/dist/model/Profile.d.ts +49 -0
  86. package/dist/model/Profile.js +181 -0
  87. package/dist/model/ProfileIndex.d.ts +20 -0
  88. package/dist/model/ProfileIndex.js +140 -0
  89. package/dist/model/Stats.d.ts +56 -0
  90. package/dist/model/Stats.js +435 -0
  91. package/dist/parser/BillionairesListParser.d.ts +3 -0
  92. package/dist/parser/BillionairesListParser.js +2 -0
  93. package/dist/parser/ListParser.d.ts +7 -0
  94. package/dist/parser/ListParser.js +11 -0
  95. package/dist/parser/Parser.d.ts +43 -0
  96. package/dist/parser/Parser.js +146 -0
  97. package/dist/parser/PersonListParser.d.ts +29 -0
  98. package/dist/parser/PersonListParser.js +111 -0
  99. package/dist/parser/ProfileParser.d.ts +44 -0
  100. package/dist/parser/ProfileParser.js +193 -0
  101. package/dist/parser/RTBListParser.d.ts +15 -0
  102. package/dist/parser/RTBListParser.js +91 -0
  103. package/dist/types/annual.d.ts +7 -0
  104. package/dist/types/config.d.ts +35 -0
  105. package/dist/types/fetch.d.ts +3 -0
  106. package/dist/types/generic.d.ts +10 -0
  107. package/dist/types/job.d.ts +71 -0
  108. package/dist/types/list.d.ts +49 -0
  109. package/dist/types/parser.d.ts +7 -0
  110. package/dist/types/profile.d.ts +9 -0
  111. package/dist/types/queue.d.ts +15 -0
  112. package/dist/types/response.d.ts +183 -0
  113. package/dist/types/storage.d.ts +3 -0
  114. package/dist/types/wiki.d.ts +1 -0
  115. package/dist/utils/Annual.d.ts +7 -0
  116. package/dist/utils/Annual.js +99 -0
  117. package/dist/utils/Performance.d.ts +8 -0
  118. package/dist/utils/Performance.js +39 -0
  119. package/dist/utils/ProfileManager.d.ts +24 -0
  120. package/dist/utils/ProfileManager.js +60 -0
  121. package/dist/utils/ProfileMerger.d.ts +11 -0
  122. package/dist/utils/ProfileMerger.js +67 -0
  123. package/dist/utils/Ranking.d.ts +11 -0
  124. package/dist/utils/Ranking.js +77 -0
  125. package/dist/utils/Wiki.d.ts +11 -0
  126. package/dist/utils/Wiki.js +168 -0
  127. 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,7 @@
1
+ export type TAnnualRawData = {
2
+ networth: number[];
3
+ prevNetworth?: number;
4
+ rank: number[];
5
+ hadBefore: boolean;
6
+ prevRank?: number;
7
+ };
@@ -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,3 @@
1
+ export type TFetchMethod = 'get' | 'post';
2
+
3
+ export type THeader = Record<string, string>;
@@ -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[];