@paypal/checkout-components 5.0.295-alpha.2 → 5.0.295
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/button.js +1 -1
- package/dist/test/button.js +1 -1
- package/package.json +2 -3
- package/src/api/shopper-insights/component.js +196 -0
- package/src/api/shopper-insights/component.test.js +343 -0
- package/src/api/shopper-insights/interface.js +5 -62
- package/src/api/shopper-insights/validation.js +74 -1
- package/src/api/shopper-insights/validation.test.js +42 -1
- package/src/api/shopper-insights/fingerprint.js +0 -52
- package/src/api/shopper-insights/shopperSession.js +0 -304
- package/src/api/shopper-insights/shopperSession.test.js +0 -320
|
@@ -1,7 +1,80 @@
|
|
|
1
1
|
/* @flow */
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
import { sendCountMetric } from "@paypal/sdk-client/src";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
SHOPPER_INSIGHTS_METRIC_NAME,
|
|
7
|
+
type MerchantPayloadData,
|
|
8
|
+
} from "../../constants/api";
|
|
3
9
|
import { ValidationError } from "../../lib";
|
|
4
10
|
|
|
11
|
+
type MerchantConfigParams = {|
|
|
12
|
+
sdkToken: ?string,
|
|
13
|
+
pageType: ?string,
|
|
14
|
+
userIDToken: ?string,
|
|
15
|
+
clientToken: ?string,
|
|
16
|
+
|};
|
|
17
|
+
|
|
18
|
+
export function validateMerchantConfig({
|
|
19
|
+
sdkToken,
|
|
20
|
+
pageType,
|
|
21
|
+
userIDToken,
|
|
22
|
+
clientToken,
|
|
23
|
+
}: MerchantConfigParams) {
|
|
24
|
+
if (!sdkToken) {
|
|
25
|
+
sendCountMetric({
|
|
26
|
+
name: SHOPPER_INSIGHTS_METRIC_NAME,
|
|
27
|
+
event: "error",
|
|
28
|
+
dimensions: {
|
|
29
|
+
errorType: "merchant_configuration_validation_error",
|
|
30
|
+
validationDetails: "sdk_token_not_present",
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
throw new ValidationError(
|
|
35
|
+
`script data attribute sdk-client-token is required but was not passed`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!pageType) {
|
|
40
|
+
sendCountMetric({
|
|
41
|
+
name: SHOPPER_INSIGHTS_METRIC_NAME,
|
|
42
|
+
event: "error",
|
|
43
|
+
dimensions: {
|
|
44
|
+
errorType: "merchant_configuration_validation_error",
|
|
45
|
+
validationDetails: "page_type_not_present",
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
throw new ValidationError(
|
|
50
|
+
`script data attribute page-type is required but was not passed`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (userIDToken) {
|
|
55
|
+
sendCountMetric({
|
|
56
|
+
name: SHOPPER_INSIGHTS_METRIC_NAME,
|
|
57
|
+
event: "error",
|
|
58
|
+
dimensions: {
|
|
59
|
+
errorType: "merchant_configuration_validation_error",
|
|
60
|
+
validationDetails: "sdk_token_and_id_token_present",
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
throw new ValidationError(
|
|
65
|
+
`use script data attribute sdk-client-token instead of user-id-token`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Client token has widely adopted integrations in the SDK that we do not want
|
|
70
|
+
// to support anymore. For now, we will be only enforcing a warning. We should
|
|
71
|
+
// expand on this warning with upgrade guides when we have them.
|
|
72
|
+
if (clientToken) {
|
|
73
|
+
// eslint-disable-next-line no-console
|
|
74
|
+
console.warn(`script data attribute client-token is not recommended`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
5
78
|
export const hasEmail = (merchantPayload: MerchantPayloadData): boolean =>
|
|
6
79
|
Boolean(merchantPayload?.email);
|
|
7
80
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* @flow */
|
|
2
2
|
import { vi, describe, expect } from "vitest";
|
|
3
3
|
|
|
4
|
-
import { validateMerchantPayload } from "./validation";
|
|
4
|
+
import { validateMerchantConfig, validateMerchantPayload } from "./validation";
|
|
5
5
|
|
|
6
6
|
vi.mock("@paypal/sdk-client/src", () => {
|
|
7
7
|
return {
|
|
@@ -9,6 +9,47 @@ vi.mock("@paypal/sdk-client/src", () => {
|
|
|
9
9
|
};
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
+
describe("shopper insights merchant SDK config validation", () => {
|
|
13
|
+
test("should throw if sdk token is not passed", () => {
|
|
14
|
+
expect(() =>
|
|
15
|
+
validateMerchantConfig({
|
|
16
|
+
sdkToken: "",
|
|
17
|
+
pageType: "",
|
|
18
|
+
userIDToken: "",
|
|
19
|
+
clientToken: "",
|
|
20
|
+
})
|
|
21
|
+
).toThrowError(
|
|
22
|
+
"script data attribute sdk-client-token is required but was not passed"
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("should throw if page type is not passed", () => {
|
|
27
|
+
expect(() =>
|
|
28
|
+
validateMerchantConfig({
|
|
29
|
+
sdkToken: "sdk-token",
|
|
30
|
+
pageType: "",
|
|
31
|
+
userIDToken: "",
|
|
32
|
+
clientToken: "",
|
|
33
|
+
})
|
|
34
|
+
).toThrowError(
|
|
35
|
+
"script data attribute page-type is required but was not passed"
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("should throw if ID token is passed", () => {
|
|
40
|
+
expect(() =>
|
|
41
|
+
validateMerchantConfig({
|
|
42
|
+
sdkToken: "sdk-token",
|
|
43
|
+
pageType: "product-listing",
|
|
44
|
+
userIDToken: "id-token",
|
|
45
|
+
clientToken: "",
|
|
46
|
+
})
|
|
47
|
+
).toThrowError(
|
|
48
|
+
"use script data attribute sdk-client-token instead of user-id-token"
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
12
53
|
describe("shopper insights merchant payload validation", () => {
|
|
13
54
|
test("should have successful validation if email is only passed", () => {
|
|
14
55
|
expect(() =>
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
/* @flow */
|
|
2
|
-
/* eslint-disable eslint-comments/disable-enable-pair */
|
|
3
|
-
/* eslint-disable no-restricted-globals, promise/no-native */
|
|
4
|
-
import { load as loadFingerprintJS } from "@fingerprintjs/fingerprintjs-pro";
|
|
5
|
-
|
|
6
|
-
const __FINGERPRINT_JS_API_KEY__ = "Eh4QKkI51U0rVUUPeQT8";
|
|
7
|
-
|
|
8
|
-
type FingerprintResults = {|
|
|
9
|
-
requestId: string,
|
|
10
|
-
|};
|
|
11
|
-
|
|
12
|
-
type Fingerprinter = {|
|
|
13
|
-
get: () => Promise<FingerprintResults>,
|
|
14
|
-
|};
|
|
15
|
-
|
|
16
|
-
export class Fingerprint {
|
|
17
|
-
fingerprinter: ?Fingerprinter;
|
|
18
|
-
results: ?FingerprintResults;
|
|
19
|
-
|
|
20
|
-
async load(): Promise<void> {
|
|
21
|
-
if (!this.fingerprinter) {
|
|
22
|
-
this.fingerprinter = await loadFingerprintJS({
|
|
23
|
-
apiKey: __FINGERPRINT_JS_API_KEY__,
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async collect(): Promise<FingerprintResults> {
|
|
29
|
-
if (this.results && this.results.requestId) {
|
|
30
|
-
return this.results;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (!this.fingerprinter) {
|
|
34
|
-
await this.load();
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (!this.fingerprinter) {
|
|
38
|
-
throw new Error("fingerprint library failed to load");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
this.results = await this.fingerprinter.get();
|
|
42
|
-
|
|
43
|
-
return this.results;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
get(): Promise<FingerprintResults> {
|
|
47
|
-
return this.collect();
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// $FlowIssue flow is bad with classes
|
|
52
|
-
export const fingerprint = new Fingerprint();
|
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
/* @flow */
|
|
2
|
-
/* eslint-disable eslint-comments/disable-enable-pair */
|
|
3
|
-
/* eslint-disable no-restricted-globals, promise/no-native */
|
|
4
|
-
|
|
5
|
-
import { type LoggerType } from "@krakenjs/beaver-logger/src";
|
|
6
|
-
import { stringifyError } from "@krakenjs/belter/src";
|
|
7
|
-
import { sendCountMetric } from "@paypal/sdk-client/src";
|
|
8
|
-
import { FPTI_KEY } from "@paypal/sdk-constants/src";
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
ELIGIBLE_PAYMENT_METHODS,
|
|
12
|
-
FPTI_TRANSITION,
|
|
13
|
-
SHOPPER_INSIGHTS_METRIC_NAME,
|
|
14
|
-
} from "../../constants/api";
|
|
15
|
-
import { ValidationError } from "../../lib";
|
|
16
|
-
|
|
17
|
-
import {
|
|
18
|
-
validateMerchantPayload,
|
|
19
|
-
hasEmail,
|
|
20
|
-
hasPhoneNumber,
|
|
21
|
-
} from "./validation";
|
|
22
|
-
import { Fingerprint } from "./fingerprint";
|
|
23
|
-
|
|
24
|
-
export type MerchantPayloadData = {|
|
|
25
|
-
email?: string,
|
|
26
|
-
phone?: {|
|
|
27
|
-
countryCode?: string,
|
|
28
|
-
nationalNumber?: string,
|
|
29
|
-
|},
|
|
30
|
-
|};
|
|
31
|
-
|
|
32
|
-
type RecommendedPaymentMethods = {|
|
|
33
|
-
isPayPalRecommended: boolean,
|
|
34
|
-
isVenmoRecommended: boolean,
|
|
35
|
-
|};
|
|
36
|
-
|
|
37
|
-
type RecommendedPaymentMethodsRequestData = {|
|
|
38
|
-
customer: {|
|
|
39
|
-
country_code?: string,
|
|
40
|
-
email?: string,
|
|
41
|
-
phone?: {|
|
|
42
|
-
country_code: string,
|
|
43
|
-
national_number: string,
|
|
44
|
-
|},
|
|
45
|
-
|},
|
|
46
|
-
purchase_units: $ReadOnlyArray<{|
|
|
47
|
-
amount: {|
|
|
48
|
-
currency_code: string,
|
|
49
|
-
|},
|
|
50
|
-
|}>,
|
|
51
|
-
preferences: {|
|
|
52
|
-
include_account_details: boolean,
|
|
53
|
-
|},
|
|
54
|
-
|};
|
|
55
|
-
|
|
56
|
-
type RecommendedPaymentMethodsResponse = {|
|
|
57
|
-
body: {|
|
|
58
|
-
eligible_methods: {
|
|
59
|
-
[paymentMethod: "paypal" | "venmo"]: {|
|
|
60
|
-
can_be_vaulted: boolean,
|
|
61
|
-
eligible_in_paypal_network?: boolean,
|
|
62
|
-
recommended?: boolean,
|
|
63
|
-
recommended_priority?: number,
|
|
64
|
-
|},
|
|
65
|
-
},
|
|
66
|
-
|},
|
|
67
|
-
|};
|
|
68
|
-
|
|
69
|
-
type SdkConfig = {|
|
|
70
|
-
sdkToken: ?string,
|
|
71
|
-
pageType: ?string,
|
|
72
|
-
userIDToken: ?string,
|
|
73
|
-
clientToken: ?string,
|
|
74
|
-
paypalApiDomain: string,
|
|
75
|
-
environment: ?string,
|
|
76
|
-
buyerCountry: string,
|
|
77
|
-
currency: string,
|
|
78
|
-
|};
|
|
79
|
-
|
|
80
|
-
// eslint's flow integration is very out of date
|
|
81
|
-
// it doesn't recognize the generics here as used
|
|
82
|
-
// eslint-disable-next-line no-undef
|
|
83
|
-
type Request = <TRequestData, TResponse>({|
|
|
84
|
-
method?: string,
|
|
85
|
-
url: string,
|
|
86
|
-
// eslint-disable-next-line no-undef
|
|
87
|
-
data: TRequestData,
|
|
88
|
-
accessToken: ?string,
|
|
89
|
-
// eslint-disable-next-line no-undef
|
|
90
|
-
|}) => Promise<TResponse>;
|
|
91
|
-
|
|
92
|
-
type Storage = {|
|
|
93
|
-
// eslint's flow integration is very out of date
|
|
94
|
-
// it doesn't recognize the generics here as used
|
|
95
|
-
// eslint-disable-next-line no-undef
|
|
96
|
-
get: <TValue>(key: string) => ?TValue,
|
|
97
|
-
// eslint-disable-next-line flowtype/no-weak-types
|
|
98
|
-
set: (key: string, value: any) => void,
|
|
99
|
-
|};
|
|
100
|
-
|
|
101
|
-
export const createRecommendedPaymentMethodsRequestPayload = ({
|
|
102
|
-
merchantPayload,
|
|
103
|
-
sdkConfig,
|
|
104
|
-
}: {|
|
|
105
|
-
merchantPayload: MerchantPayloadData,
|
|
106
|
-
sdkConfig: SdkConfig,
|
|
107
|
-
|}): RecommendedPaymentMethodsRequestData => ({
|
|
108
|
-
customer: {
|
|
109
|
-
...(sdkConfig.environment !== "production" && {
|
|
110
|
-
country_code: sdkConfig.buyerCountry,
|
|
111
|
-
}),
|
|
112
|
-
// $FlowIssue
|
|
113
|
-
...(hasEmail(merchantPayload) && {
|
|
114
|
-
email: merchantPayload?.email,
|
|
115
|
-
}),
|
|
116
|
-
...(hasPhoneNumber(merchantPayload) && {
|
|
117
|
-
phone: {
|
|
118
|
-
country_code: merchantPayload?.phone?.countryCode,
|
|
119
|
-
national_number: merchantPayload?.phone?.nationalNumber,
|
|
120
|
-
},
|
|
121
|
-
}),
|
|
122
|
-
},
|
|
123
|
-
purchase_units: [
|
|
124
|
-
{
|
|
125
|
-
amount: {
|
|
126
|
-
currency_code: sdkConfig.currency,
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
],
|
|
130
|
-
// getRecommendedPaymentMethods maps to include_account_details in the API
|
|
131
|
-
preferences: {
|
|
132
|
-
include_account_details: true,
|
|
133
|
-
},
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
export interface ShopperInsightsInterface {
|
|
137
|
-
getRecommendedPaymentMethods: (
|
|
138
|
-
payload: MerchantPayloadData
|
|
139
|
-
) => Promise<RecommendedPaymentMethods>;
|
|
140
|
-
identify: () => Promise<{||}>;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export class ShopperSession {
|
|
144
|
-
fingerprint: Fingerprint;
|
|
145
|
-
logger: LoggerType;
|
|
146
|
-
request: Request;
|
|
147
|
-
requestId: string = "";
|
|
148
|
-
sdkConfig: SdkConfig;
|
|
149
|
-
sessionState: Storage;
|
|
150
|
-
|
|
151
|
-
constructor({
|
|
152
|
-
fingerprint,
|
|
153
|
-
logger,
|
|
154
|
-
request,
|
|
155
|
-
sdkConfig,
|
|
156
|
-
sessionState,
|
|
157
|
-
}: {|
|
|
158
|
-
fingerprint: Fingerprint,
|
|
159
|
-
logger: LoggerType,
|
|
160
|
-
request: Request,
|
|
161
|
-
sdkConfig: SdkConfig,
|
|
162
|
-
sessionState: Storage,
|
|
163
|
-
|}) {
|
|
164
|
-
this.fingerprint = fingerprint;
|
|
165
|
-
this.logger = logger;
|
|
166
|
-
this.request = request;
|
|
167
|
-
this.sdkConfig = sdkConfig;
|
|
168
|
-
this.sessionState = sessionState;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
validateSdkConfig() {
|
|
172
|
-
if (!this.sdkConfig.sdkToken) {
|
|
173
|
-
sendCountMetric({
|
|
174
|
-
name: SHOPPER_INSIGHTS_METRIC_NAME,
|
|
175
|
-
event: "error",
|
|
176
|
-
dimensions: {
|
|
177
|
-
errorType: "merchant_configuration_validation_error",
|
|
178
|
-
validationDetails: "sdk_token_not_present",
|
|
179
|
-
},
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
throw new ValidationError(
|
|
183
|
-
`script data attribute sdk-client-token is required but was not passed`
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (!this.sdkConfig.pageType) {
|
|
188
|
-
sendCountMetric({
|
|
189
|
-
name: SHOPPER_INSIGHTS_METRIC_NAME,
|
|
190
|
-
event: "error",
|
|
191
|
-
dimensions: {
|
|
192
|
-
errorType: "merchant_configuration_validation_error",
|
|
193
|
-
validationDetails: "page_type_not_present",
|
|
194
|
-
},
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
throw new ValidationError(
|
|
198
|
-
`script data attribute page-type is required but was not passed`
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (this.sdkConfig.userIDToken) {
|
|
203
|
-
sendCountMetric({
|
|
204
|
-
name: SHOPPER_INSIGHTS_METRIC_NAME,
|
|
205
|
-
event: "error",
|
|
206
|
-
dimensions: {
|
|
207
|
-
errorType: "merchant_configuration_validation_error",
|
|
208
|
-
validationDetails: "sdk_token_and_id_token_present",
|
|
209
|
-
},
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
throw new ValidationError(
|
|
213
|
-
`use script data attribute sdk-client-token instead of user-id-token`
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Client token has widely adopted integrations in the SDK that we do not want
|
|
218
|
-
// to support anymore. For now, we will be only enforcing a warning. We should
|
|
219
|
-
// expand on this warning with upgrade guides when we have them.
|
|
220
|
-
if (this.sdkConfig.clientToken) {
|
|
221
|
-
// eslint-disable-next-line no-console
|
|
222
|
-
console.warn(`script data attribute client-token is not recommended`);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
async getRecommendedPaymentMethods(
|
|
227
|
-
merchantPayload: MerchantPayloadData
|
|
228
|
-
): Promise<RecommendedPaymentMethods> {
|
|
229
|
-
validateMerchantPayload(merchantPayload);
|
|
230
|
-
|
|
231
|
-
const startTime = Date.now();
|
|
232
|
-
try {
|
|
233
|
-
const { body } = await this.request<
|
|
234
|
-
RecommendedPaymentMethodsRequestData,
|
|
235
|
-
RecommendedPaymentMethodsResponse
|
|
236
|
-
>({
|
|
237
|
-
method: "POST",
|
|
238
|
-
url: `${this.sdkConfig.paypalApiDomain}/${ELIGIBLE_PAYMENT_METHODS}`,
|
|
239
|
-
data: createRecommendedPaymentMethodsRequestPayload({
|
|
240
|
-
merchantPayload,
|
|
241
|
-
sdkConfig: this.sdkConfig,
|
|
242
|
-
}),
|
|
243
|
-
accessToken: this.sdkConfig.sdkToken,
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
this.sessionState.set("shopperInsights", {
|
|
247
|
-
getRecommendedPaymentMethodsUsed: true,
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
const { paypal, venmo } = body?.eligible_methods;
|
|
251
|
-
|
|
252
|
-
const isPayPalRecommended =
|
|
253
|
-
(paypal?.eligible_in_paypal_network && paypal?.recommended) || false;
|
|
254
|
-
const isVenmoRecommended =
|
|
255
|
-
(venmo?.eligible_in_paypal_network && venmo?.recommended) || false;
|
|
256
|
-
|
|
257
|
-
this.logger.track({
|
|
258
|
-
[FPTI_KEY.TRANSITION]: FPTI_TRANSITION.SHOPPER_INSIGHTS_API_SUCCESS,
|
|
259
|
-
[FPTI_KEY.EVENT_NAME]: FPTI_TRANSITION.SHOPPER_INSIGHTS_API_SUCCESS,
|
|
260
|
-
[FPTI_KEY.RESPONSE_DURATION]: (Date.now() - startTime).toString(),
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
sendCountMetric({
|
|
264
|
-
name: SHOPPER_INSIGHTS_METRIC_NAME,
|
|
265
|
-
event: "success",
|
|
266
|
-
dimensions: {
|
|
267
|
-
isPayPalRecommended: String(isPayPalRecommended),
|
|
268
|
-
isVenmoRecommended: String(isVenmoRecommended),
|
|
269
|
-
},
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
return { isPayPalRecommended, isVenmoRecommended };
|
|
273
|
-
} catch (error) {
|
|
274
|
-
sendCountMetric({
|
|
275
|
-
name: SHOPPER_INSIGHTS_METRIC_NAME,
|
|
276
|
-
event: "error",
|
|
277
|
-
dimensions: {
|
|
278
|
-
errorType: "api_error",
|
|
279
|
-
},
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
this.logger.track({
|
|
283
|
-
[FPTI_KEY.TRANSITION]: FPTI_TRANSITION.SHOPPER_INSIGHTS_API_ERROR,
|
|
284
|
-
[FPTI_KEY.EVENT_NAME]: FPTI_TRANSITION.SHOPPER_INSIGHTS_API_ERROR,
|
|
285
|
-
[FPTI_KEY.RESPONSE_DURATION]: (Date.now() - startTime).toString(),
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
this.logger.error("shopper_insights_api_error", {
|
|
289
|
-
err: stringifyError(error),
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
throw error;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
async identify(): Promise<{||}> {
|
|
297
|
-
const { requestId } = await this.fingerprint.get();
|
|
298
|
-
|
|
299
|
-
this.requestId = requestId;
|
|
300
|
-
|
|
301
|
-
// $FlowIssue
|
|
302
|
-
return {};
|
|
303
|
-
}
|
|
304
|
-
}
|