@accounter/scraper-app 0.0.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/README.md +90 -0
- package/docs/plan.md +76 -0
- package/index.html +12 -0
- package/package.json +40 -0
- package/src/env.template +2 -0
- package/src/server/__tests__/accounts-routes.test.ts +133 -0
- package/src/server/__tests__/check-accounts.test.ts +305 -0
- package/src/server/__tests__/filter-payload.test.ts +193 -0
- package/src/server/__tests__/graphql-client.integration.test.ts +98 -0
- package/src/server/__tests__/graphql-client.test.ts +508 -0
- package/src/server/__tests__/healthz.test.ts +22 -0
- package/src/server/__tests__/history.test.ts +111 -0
- package/src/server/__tests__/otp-manager.test.ts +132 -0
- package/src/server/__tests__/scrape-runner.test.ts +144 -0
- package/src/server/__tests__/settings-routes.test.ts +117 -0
- package/src/server/__tests__/sources-routes.test.ts +149 -0
- package/src/server/__tests__/validate-payload.test.ts +193 -0
- package/src/server/__tests__/vault-routes.test.ts +174 -0
- package/src/server/__tests__/vault.test.ts +33 -0
- package/src/server/__tests__/websocket.test.ts +151 -0
- package/src/server/account-discovery.ts +49 -0
- package/src/server/accounts-routes.ts +74 -0
- package/src/server/check-accounts.ts +79 -0
- package/src/server/filter-payload.ts +145 -0
- package/src/server/graphql/client.ts +103 -0
- package/src/server/graphql/mutations.ts +518 -0
- package/src/server/history-routes.ts +11 -0
- package/src/server/history.ts +53 -0
- package/src/server/index.ts +40 -0
- package/src/server/otp-manager.ts +63 -0
- package/src/server/payload-schemas/amex.schema.ts +2 -0
- package/src/server/payload-schemas/cal.schema.ts +27 -0
- package/src/server/payload-schemas/currency-rates.schema.ts +11 -0
- package/src/server/payload-schemas/discount.schema.ts +26 -0
- package/src/server/payload-schemas/isracard.schema.ts +58 -0
- package/src/server/payload-schemas/max.schema.ts +27 -0
- package/src/server/payload-schemas/poalim-foreign.schema.ts +30 -0
- package/src/server/payload-schemas/poalim-ils.schema.ts +31 -0
- package/src/server/payload-schemas/poalim-swift.schema.ts +21 -0
- package/src/server/scrape-runner.ts +165 -0
- package/src/server/scrapers/__tests__/amex.test.ts +142 -0
- package/src/server/scrapers/__tests__/cal.test.ts +135 -0
- package/src/server/scrapers/__tests__/currency-rates.test.ts +105 -0
- package/src/server/scrapers/__tests__/discount.test.ts +160 -0
- package/src/server/scrapers/__tests__/isracard.test.ts +142 -0
- package/src/server/scrapers/__tests__/max.test.ts +115 -0
- package/src/server/scrapers/__tests__/poalim.test.ts +154 -0
- package/src/server/scrapers/amex.ts +63 -0
- package/src/server/scrapers/cal.ts +56 -0
- package/src/server/scrapers/currency-rates.ts +64 -0
- package/src/server/scrapers/discount.ts +62 -0
- package/src/server/scrapers/isracard.ts +68 -0
- package/src/server/scrapers/max.ts +32 -0
- package/src/server/scrapers/poalim.ts +103 -0
- package/src/server/settings-routes.ts +27 -0
- package/src/server/sources-routes.ts +182 -0
- package/src/server/validate-payload.ts +74 -0
- package/src/server/vault-routes.ts +99 -0
- package/src/server/vault-store.ts +42 -0
- package/src/server/vault.ts +216 -0
- package/src/server/websocket.ts +454 -0
- package/src/shared/source-types.ts +10 -0
- package/src/shared/types.ts +20 -0
- package/src/shared/ws-protocol.ts +177 -0
- package/src/test-setup.ts +6 -0
- package/src/ui/__tests__/accounts-tab.test.tsx +134 -0
- package/src/ui/__tests__/config.test.tsx +99 -0
- package/src/ui/__tests__/history.test.tsx +94 -0
- package/src/ui/__tests__/run.test.tsx +195 -0
- package/src/ui/__tests__/settings-tab.test.tsx +79 -0
- package/src/ui/__tests__/sources-tab.test.tsx +139 -0
- package/src/ui/__tests__/vault-setup.test.tsx +105 -0
- package/src/ui/__tests__/vault-unlock.test.tsx +78 -0
- package/src/ui/app.tsx +109 -0
- package/src/ui/components/error-boundary.tsx +54 -0
- package/src/ui/components/otp-modal.tsx +82 -0
- package/src/ui/components/skeleton.tsx +58 -0
- package/src/ui/components/task-row.tsx +241 -0
- package/src/ui/contexts/vault-context.tsx +77 -0
- package/src/ui/lib/api.ts +117 -0
- package/src/ui/lib/ws.ts +137 -0
- package/src/ui/main.tsx +9 -0
- package/src/ui/screens/config/accounts-tab.tsx +185 -0
- package/src/ui/screens/config/config.tsx +163 -0
- package/src/ui/screens/config/settings-tab.tsx +167 -0
- package/src/ui/screens/config/source-forms.tsx +518 -0
- package/src/ui/screens/config/source-types.ts +91 -0
- package/src/ui/screens/config/sources-tab.tsx +176 -0
- package/src/ui/screens/history.tsx +234 -0
- package/src/ui/screens/run.tsx +266 -0
- package/src/ui/screens/vault-setup.tsx +120 -0
- package/src/ui/screens/vault-unlock.tsx +38 -0
- package/tsconfig.json +15 -0
- package/tsup.config.ts +10 -0
- package/vite.config.ts +24 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { IsracardCardsTransactionsList } from '@accounter/modern-poalim-scraper';
|
|
2
|
+
import type { CalPayload } from './payload-schemas/cal.schema.js';
|
|
3
|
+
import type { DiscountPayload } from './payload-schemas/discount.schema.js';
|
|
4
|
+
import type { MaxPayload } from './payload-schemas/max.schema.js';
|
|
5
|
+
import type { PoalimForeignPayload } from './payload-schemas/poalim-foreign.schema.js';
|
|
6
|
+
import type { PoalimIlsPayload } from './payload-schemas/poalim-ils.schema.js';
|
|
7
|
+
import type { PoalimSwiftPayload } from './payload-schemas/poalim-swift.schema.js';
|
|
8
|
+
import type { AccountRecord } from './vault.js';
|
|
9
|
+
|
|
10
|
+
export type SourceType = 'poalim' | 'discount' | 'isracard' | 'amex' | 'cal' | 'max';
|
|
11
|
+
|
|
12
|
+
export type ValidatedPayload =
|
|
13
|
+
| PoalimIlsPayload
|
|
14
|
+
| PoalimForeignPayload
|
|
15
|
+
| PoalimSwiftPayload
|
|
16
|
+
| DiscountPayload
|
|
17
|
+
| IsracardCardsTransactionsList
|
|
18
|
+
| CalPayload
|
|
19
|
+
| MaxPayload;
|
|
20
|
+
|
|
21
|
+
export type { AccountRecord } from './vault.js';
|
|
22
|
+
|
|
23
|
+
export type AccountCheckResult = { accepted: string[]; ignored: string[]; unknown: string[] };
|
|
24
|
+
|
|
25
|
+
export function extractAccountIdentifiers(type: SourceType, payload: ValidatedPayload): string[] {
|
|
26
|
+
switch (type) {
|
|
27
|
+
case 'poalim': {
|
|
28
|
+
if (!('retrievalTransactionData' in payload)) return [];
|
|
29
|
+
const p = payload as PoalimIlsPayload;
|
|
30
|
+
return [String(p.retrievalTransactionData.accountNumber)];
|
|
31
|
+
}
|
|
32
|
+
case 'discount': {
|
|
33
|
+
const p = payload as DiscountPayload;
|
|
34
|
+
return [...new Set(p.map(entry => entry.accountNumber))];
|
|
35
|
+
}
|
|
36
|
+
case 'isracard':
|
|
37
|
+
case 'amex': {
|
|
38
|
+
const p = payload as IsracardCardsTransactionsList;
|
|
39
|
+
return p.CardsTransactionsListBean.cardNumberList
|
|
40
|
+
.map(c => c.match(/\d{4}/)?.[0])
|
|
41
|
+
.filter((c): c is string => c !== undefined);
|
|
42
|
+
}
|
|
43
|
+
case 'cal': {
|
|
44
|
+
const p = payload as CalPayload;
|
|
45
|
+
return [...new Set(p.map(entry => entry.card))];
|
|
46
|
+
}
|
|
47
|
+
case 'max': {
|
|
48
|
+
const p = payload as MaxPayload;
|
|
49
|
+
return [...new Set(p.map(account => account.accountNumber))];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function checkAccounts(
|
|
55
|
+
type: SourceType,
|
|
56
|
+
payload: ValidatedPayload,
|
|
57
|
+
known: AccountRecord[],
|
|
58
|
+
): AccountCheckResult {
|
|
59
|
+
const identifiers = extractAccountIdentifiers(type, payload);
|
|
60
|
+
const accepted: string[] = [];
|
|
61
|
+
const ignored: string[] = [];
|
|
62
|
+
const unknown: string[] = [];
|
|
63
|
+
|
|
64
|
+
for (const id of identifiers) {
|
|
65
|
+
const record = known.find(a => a.sourceType === type && a.accountNumber === id);
|
|
66
|
+
if (!record) {
|
|
67
|
+
unknown.push(id);
|
|
68
|
+
} else if (record.status === 'ignored') {
|
|
69
|
+
ignored.push(id);
|
|
70
|
+
} else if (record.status === 'accepted') {
|
|
71
|
+
accepted.push(id);
|
|
72
|
+
} else {
|
|
73
|
+
// 'pending' and any future unrecognized status blocks the run
|
|
74
|
+
unknown.push(id);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return { accepted, ignored, unknown };
|
|
79
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
import type { IsracardCardsTransactionsList } from '@accounter/modern-poalim-scraper';
|
|
3
|
+
import type { SourceType, ValidatedPayload } from './check-accounts.js';
|
|
4
|
+
import type { CalPayload } from './payload-schemas/cal.schema.js';
|
|
5
|
+
import type { DiscountPayload } from './payload-schemas/discount.schema.js';
|
|
6
|
+
import type { MaxPayload } from './payload-schemas/max.schema.js';
|
|
7
|
+
import type { PoalimForeignPayload } from './payload-schemas/poalim-foreign.schema.js';
|
|
8
|
+
import type { PoalimIlsPayload } from './payload-schemas/poalim-ils.schema.js';
|
|
9
|
+
import type { PoalimSwiftPayload } from './payload-schemas/poalim-swift.schema.js';
|
|
10
|
+
import type {
|
|
11
|
+
CalAccountSchema,
|
|
12
|
+
DiscountAccountSchema,
|
|
13
|
+
IsracardAmexAccountSchema,
|
|
14
|
+
MaxAccountSchema,
|
|
15
|
+
PoalimAccountSchema,
|
|
16
|
+
} from './vault.js';
|
|
17
|
+
|
|
18
|
+
export type FilterableCreds =
|
|
19
|
+
| z.infer<typeof PoalimAccountSchema>
|
|
20
|
+
| z.infer<typeof IsracardAmexAccountSchema>
|
|
21
|
+
| z.infer<typeof CalAccountSchema>
|
|
22
|
+
| z.infer<typeof MaxAccountSchema>
|
|
23
|
+
| z.infer<typeof DiscountAccountSchema>;
|
|
24
|
+
|
|
25
|
+
function effectiveSet(
|
|
26
|
+
accepted: string[] | undefined,
|
|
27
|
+
ignored: string[] | undefined,
|
|
28
|
+
all: string[],
|
|
29
|
+
): Set<string> {
|
|
30
|
+
const base = accepted?.length ? accepted : all;
|
|
31
|
+
const ignoredSet = new Set(ignored ?? []);
|
|
32
|
+
return new Set(base.filter(c => !ignoredSet.has(c)));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function filterIsracardAmex(
|
|
36
|
+
payload: IsracardCardsTransactionsList,
|
|
37
|
+
accepted: string[] | undefined,
|
|
38
|
+
ignored: string[] | undefined,
|
|
39
|
+
): IsracardCardsTransactionsList {
|
|
40
|
+
const bean = payload.CardsTransactionsListBean;
|
|
41
|
+
const allCards = bean.cardNumberList.map(c => c.match(/\d{4}/)?.[0]);
|
|
42
|
+
|
|
43
|
+
const allowed = effectiveSet(accepted, ignored, allCards.filter(Boolean) as string[]);
|
|
44
|
+
|
|
45
|
+
const filteredBean: Record<string, unknown> = {};
|
|
46
|
+
for (const [key, val] of Object.entries(bean)) {
|
|
47
|
+
if (/^Index\d+$/.test(key)) {
|
|
48
|
+
const card = allCards[Number(key.slice(5))]; // 'Index0' → 0 → cardNumbers[0]
|
|
49
|
+
if (!card || !allowed.has(card)) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
filteredBean[key] = val;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
...payload,
|
|
58
|
+
CardsTransactionsListBean:
|
|
59
|
+
filteredBean as IsracardCardsTransactionsList['CardsTransactionsListBean'],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function filterPayload(
|
|
64
|
+
type: SourceType,
|
|
65
|
+
payload: ValidatedPayload,
|
|
66
|
+
creds: FilterableCreds,
|
|
67
|
+
): ValidatedPayload {
|
|
68
|
+
switch (type) {
|
|
69
|
+
case 'isracard':
|
|
70
|
+
case 'amex': {
|
|
71
|
+
const p = payload as IsracardCardsTransactionsList;
|
|
72
|
+
const opts = (creds as z.infer<typeof IsracardAmexAccountSchema>).options;
|
|
73
|
+
return filterIsracardAmex(
|
|
74
|
+
p,
|
|
75
|
+
opts?.acceptedCardNumbers,
|
|
76
|
+
opts?.ignoredCardNumbers,
|
|
77
|
+
) as IsracardCardsTransactionsList;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
case 'cal': {
|
|
81
|
+
const p = payload as CalPayload;
|
|
82
|
+
const opts = (creds as z.infer<typeof CalAccountSchema>).options;
|
|
83
|
+
const allCards = [...new Set(p.map(e => e.card))];
|
|
84
|
+
const allowed = effectiveSet(opts?.acceptedCardNumbers, opts?.ignoredCardNumbers, allCards);
|
|
85
|
+
return p.filter(e => allowed.has(e.card));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
case 'max': {
|
|
89
|
+
const p = payload as MaxPayload;
|
|
90
|
+
const opts = (creds as z.infer<typeof MaxAccountSchema>).options;
|
|
91
|
+
const allAccounts = [...new Set(p.map(e => e.accountNumber))];
|
|
92
|
+
const allowed = effectiveSet(
|
|
93
|
+
opts?.acceptedCardNumbers,
|
|
94
|
+
opts?.ignoredCardNumbers,
|
|
95
|
+
allAccounts,
|
|
96
|
+
);
|
|
97
|
+
return p.filter(e => allowed.has(e.accountNumber));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
case 'poalim': {
|
|
101
|
+
const opts = (creds as z.infer<typeof PoalimAccountSchema>).options;
|
|
102
|
+
// PoalimIls has retrievalTransactionData; Foreign has balancesAndLimitsDataList; Swift has swiftsList
|
|
103
|
+
if ('retrievalTransactionData' in payload) {
|
|
104
|
+
const p = payload as PoalimIlsPayload;
|
|
105
|
+
const { accountNumber, branchNumber } = p.retrievalTransactionData;
|
|
106
|
+
const allAccounts = [String(accountNumber)];
|
|
107
|
+
const allBranches = [String(branchNumber)];
|
|
108
|
+
const allowedAccounts = effectiveSet(
|
|
109
|
+
opts?.acceptedAccountNumbers,
|
|
110
|
+
opts?.ignoredAccountNumbers,
|
|
111
|
+
allAccounts,
|
|
112
|
+
);
|
|
113
|
+
const allowedBranches = effectiveSet(
|
|
114
|
+
opts?.acceptedBranchNumbers,
|
|
115
|
+
opts?.ignoredBranchNumbers,
|
|
116
|
+
allBranches,
|
|
117
|
+
);
|
|
118
|
+
if (
|
|
119
|
+
!allowedAccounts.has(String(accountNumber)) ||
|
|
120
|
+
!allowedBranches.has(String(branchNumber))
|
|
121
|
+
) {
|
|
122
|
+
return { ...p, transactions: [] };
|
|
123
|
+
}
|
|
124
|
+
return p;
|
|
125
|
+
}
|
|
126
|
+
if ('balancesAndLimitsDataList' in payload) {
|
|
127
|
+
// Foreign payloads carry no account/branch identifiers — account-level filtering
|
|
128
|
+
// is handled by the caller, which correlates each Foreign payload with its
|
|
129
|
+
// positionally-matched ILS payload (which does carry the identifiers).
|
|
130
|
+
return payload as PoalimForeignPayload;
|
|
131
|
+
}
|
|
132
|
+
if ('swiftsList' in payload) {
|
|
133
|
+
// Swift payloads carry no account/branch identifiers — same as Foreign above.
|
|
134
|
+
return payload as PoalimSwiftPayload;
|
|
135
|
+
}
|
|
136
|
+
return payload;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
case 'discount': {
|
|
140
|
+
// No filter options for discount
|
|
141
|
+
const p = payload as DiscountPayload;
|
|
142
|
+
return p;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { GraphQLClient } from 'graphql-request';
|
|
2
|
+
import type { IsracardCardsTransactionsList } from '@accounter/modern-poalim-scraper';
|
|
3
|
+
import type { ScraperUploadResult } from '../gql/index.js';
|
|
4
|
+
import type { CalPayload } from '../payload-schemas/cal.schema.js';
|
|
5
|
+
import type { CurrencyRatesPayload } from '../payload-schemas/currency-rates.schema.js';
|
|
6
|
+
import type { DiscountPayload } from '../payload-schemas/discount.schema.js';
|
|
7
|
+
import type { MaxPayload } from '../payload-schemas/max.schema.js';
|
|
8
|
+
import type { PoalimForeignPayload } from '../payload-schemas/poalim-foreign.schema.js';
|
|
9
|
+
import type { PoalimIlsPayload } from '../payload-schemas/poalim-ils.schema.js';
|
|
10
|
+
import type { PoalimSwiftPayload } from '../payload-schemas/poalim-swift.schema.js';
|
|
11
|
+
import {
|
|
12
|
+
amexVars,
|
|
13
|
+
calVars,
|
|
14
|
+
currencyRatesVars,
|
|
15
|
+
discountVars,
|
|
16
|
+
isracardVars,
|
|
17
|
+
maxVars,
|
|
18
|
+
poalimForeignVars,
|
|
19
|
+
poalimIlsVars,
|
|
20
|
+
poalimSwiftVars,
|
|
21
|
+
UPLOAD_AMEX,
|
|
22
|
+
UPLOAD_CAL,
|
|
23
|
+
UPLOAD_CURRENCY_RATES,
|
|
24
|
+
UPLOAD_DISCOUNT,
|
|
25
|
+
UPLOAD_ISRACARD,
|
|
26
|
+
UPLOAD_MAX,
|
|
27
|
+
UPLOAD_POALIM_FOREIGN,
|
|
28
|
+
UPLOAD_POALIM_ILS,
|
|
29
|
+
UPLOAD_POALIM_SWIFT,
|
|
30
|
+
} from './mutations.js';
|
|
31
|
+
|
|
32
|
+
type GqlResponse<K extends string> = Record<K, ScraperUploadResult>;
|
|
33
|
+
|
|
34
|
+
function extractResult<K extends string>(data: GqlResponse<K>, key: K): ScraperUploadResult {
|
|
35
|
+
const result = data[key];
|
|
36
|
+
if (!result) throw new Error(`GraphQL response missing field: ${key}`);
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type { ScraperUploadResult };
|
|
41
|
+
|
|
42
|
+
export function createUploadClient(serverUrl: string, apiKey: string) {
|
|
43
|
+
const gql = new GraphQLClient(serverUrl, {
|
|
44
|
+
headers: { 'X-API-Key': apiKey },
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
async function request<K extends string>(
|
|
48
|
+
doc: string,
|
|
49
|
+
vars: Record<string, unknown>,
|
|
50
|
+
key: K,
|
|
51
|
+
): Promise<ScraperUploadResult> {
|
|
52
|
+
const data = await gql.request<GqlResponse<K>>(doc, vars);
|
|
53
|
+
return extractResult(data, key);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
async uploadPoalimIls(payload: PoalimIlsPayload): Promise<ScraperUploadResult> {
|
|
58
|
+
return request(UPLOAD_POALIM_ILS, poalimIlsVars(payload), 'uploadPoalimIlsTransactions');
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
async uploadPoalimForeign(payload: PoalimForeignPayload): Promise<ScraperUploadResult> {
|
|
62
|
+
return request(
|
|
63
|
+
UPLOAD_POALIM_FOREIGN,
|
|
64
|
+
poalimForeignVars(payload),
|
|
65
|
+
'uploadPoalimForeignTransactions',
|
|
66
|
+
);
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
async uploadPoalimSwift(payload: PoalimSwiftPayload): Promise<ScraperUploadResult> {
|
|
70
|
+
return request(
|
|
71
|
+
UPLOAD_POALIM_SWIFT,
|
|
72
|
+
poalimSwiftVars(payload),
|
|
73
|
+
'uploadPoalimSwiftTransactions',
|
|
74
|
+
);
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
async uploadIsracard(payloads: IsracardCardsTransactionsList[]): Promise<ScraperUploadResult> {
|
|
78
|
+
return request(UPLOAD_ISRACARD, isracardVars(payloads), 'uploadIsracardTransactions');
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
async uploadAmex(payloads: IsracardCardsTransactionsList[]): Promise<ScraperUploadResult> {
|
|
82
|
+
return request(UPLOAD_AMEX, amexVars(payloads), 'uploadAmexTransactions');
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
async uploadCal(payload: CalPayload): Promise<ScraperUploadResult> {
|
|
86
|
+
return request(UPLOAD_CAL, calVars(payload), 'uploadCalTransactions');
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
async uploadDiscount(payload: DiscountPayload): Promise<ScraperUploadResult> {
|
|
90
|
+
return request(UPLOAD_DISCOUNT, discountVars(payload), 'uploadDiscountTransactions');
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
async uploadMax(payload: MaxPayload): Promise<ScraperUploadResult> {
|
|
94
|
+
return request(UPLOAD_MAX, maxVars(payload), 'uploadMaxTransactions');
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
async uploadCurrencyRates(payload: CurrencyRatesPayload): Promise<ScraperUploadResult> {
|
|
98
|
+
return request(UPLOAD_CURRENCY_RATES, currencyRatesVars(payload), 'uploadCurrencyRates');
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export type UploadClient = ReturnType<typeof createUploadClient>;
|