radar-sdk-js 5.1.0-beta.1 → 5.1.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/errors.d.ts +1 -51
- package/dist/index.d.ts +0 -1
- package/dist/logger.d.ts +2 -2
- package/dist/radar.js +59 -301
- package/dist/radar.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/api.ts +1 -1
- package/src/errors.ts +2 -65
- package/src/http.ts +11 -188
- package/src/index.ts +0 -1
- package/src/logger.ts +3 -8
- package/src/version.ts +1 -1
- package/dist/dns-over-https.d.ts +0 -29
- package/src/dns-over-https.ts +0 -166
package/README.md
CHANGED
|
@@ -55,7 +55,7 @@ Radar.initialize({ publishableKey: 'prj_test_pk_...' });
|
|
|
55
55
|
Add the following to your HTML:
|
|
56
56
|
|
|
57
57
|
```html
|
|
58
|
-
<script src="https://js.radar.com/v5.1.
|
|
58
|
+
<script src="https://js.radar.com/v5.1.1-beta.0/radar.min.js"></script>
|
|
59
59
|
|
|
60
60
|
<script>
|
|
61
61
|
Radar.initialize({ publishableKey: 'prj_test_pk_...' });
|
|
@@ -134,7 +134,7 @@ the core SDK first, then any plugins you need:
|
|
|
134
134
|
<link href="https://js.radar.com/maps/v1.0.0/radar-maps.css" rel="stylesheet" />
|
|
135
135
|
<link href="https://js.radar.com/autocomplete/v1.1.0/radar-autocomplete.css" rel="stylesheet" />
|
|
136
136
|
|
|
137
|
-
<script src="https://js.radar.com/v5.1.
|
|
137
|
+
<script src="https://js.radar.com/v5.1.1-beta.0/radar.min.js"></script>
|
|
138
138
|
<script src="https://js.radar.com/maps/v1.0.0/radar-maps.min.js"></script>
|
|
139
139
|
<script src="https://js.radar.com/autocomplete/v1.1.0/radar-autocomplete.min.js"></script>
|
|
140
140
|
<script src="https://js.radar.com/fraud/v1.0.0/radar-fraud.min.js"></script>
|
|
@@ -151,7 +151,7 @@ by ID or element reference.
|
|
|
151
151
|
<html>
|
|
152
152
|
<head>
|
|
153
153
|
<link href="https://js.radar.com/maps/v1.0.0/radar-maps.css" rel="stylesheet" />
|
|
154
|
-
<script src="https://js.radar.com/v5.1.
|
|
154
|
+
<script src="https://js.radar.com/v5.1.1-beta.0/radar.min.js"></script>
|
|
155
155
|
<script src="https://js.radar.com/maps/v1.0.0/radar-maps.min.js"></script>
|
|
156
156
|
</head>
|
|
157
157
|
|
|
@@ -178,7 +178,7 @@ by ID or element reference.
|
|
|
178
178
|
<html>
|
|
179
179
|
<head>
|
|
180
180
|
<link href="https://js.radar.com/autocomplete/v1.1.0/radar-autocomplete.css" rel="stylesheet" />
|
|
181
|
-
<script src="https://js.radar.com/v5.1.
|
|
181
|
+
<script src="https://js.radar.com/v5.1.1-beta.0/radar.min.js"></script>
|
|
182
182
|
<script src="https://js.radar.com/autocomplete/v1.1.0/radar-autocomplete.min.js"></script>
|
|
183
183
|
</head>
|
|
184
184
|
|
|
@@ -211,7 +211,7 @@ are needed for geofencing.
|
|
|
211
211
|
```html
|
|
212
212
|
<html>
|
|
213
213
|
<head>
|
|
214
|
-
<script src="https://js.radar.com/v5.1.
|
|
214
|
+
<script src="https://js.radar.com/v5.1.1-beta.0/radar.min.js"></script>
|
|
215
215
|
</head>
|
|
216
216
|
|
|
217
217
|
<body>
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import type { DnsProbeResult } from './dns-over-https';
|
|
2
1
|
import type { RadarResponse } from './http';
|
|
3
|
-
export type { DnsProbeResult };
|
|
4
2
|
/** base error class for all Radar SDK errors */
|
|
5
3
|
export declare abstract class RadarError extends Error {
|
|
6
4
|
/** legacy status code string (e.g. `'ERROR_PUBLISHABLE_KEY'`) */
|
|
@@ -83,59 +81,11 @@ export declare class RadarServerError extends RadarError {
|
|
|
83
81
|
readonly response?: RadarResponse;
|
|
84
82
|
constructor(response?: RadarResponse);
|
|
85
83
|
}
|
|
86
|
-
/**
|
|
87
|
-
* Diagnostics captured when fetch() rejects before any HTTP response is received.
|
|
88
|
-
* Safe to stringify for logs — does not include request headers or bodies.
|
|
89
|
-
*/
|
|
90
|
-
export interface RadarNetworkFailureDetails {
|
|
91
|
-
readonly phase: 'fetch';
|
|
92
|
-
readonly method: string;
|
|
93
|
-
readonly url: string;
|
|
94
|
-
readonly pathname: string;
|
|
95
|
-
readonly search: string;
|
|
96
|
-
/** hostname parsed from the request URL */
|
|
97
|
-
readonly apiHostname?: string;
|
|
98
|
-
/** document origin when in a browser */
|
|
99
|
-
readonly pageOrigin?: string;
|
|
100
|
-
/** document pathname when in a browser */
|
|
101
|
-
readonly pagePath?: string;
|
|
102
|
-
/** `navigator.userAgent` when available */
|
|
103
|
-
readonly userAgent?: string;
|
|
104
|
-
/** true when API hostname differs from the document hostname */
|
|
105
|
-
readonly crossSiteApiCall?: boolean;
|
|
106
|
-
/**
|
|
107
|
-
* Wall-clock ms from immediately before `fetch()` until it rejected (rounded integer).
|
|
108
|
-
* Does not include JSON parse time when a response was received.
|
|
109
|
-
*/
|
|
110
|
-
readonly durationMs: number;
|
|
111
|
-
/** whether `navigator.onLine` was false at failure time */
|
|
112
|
-
readonly online: boolean;
|
|
113
|
-
/** true when failure was triggered by AbortController / user abort */
|
|
114
|
-
readonly aborted: boolean;
|
|
115
|
-
readonly errorName: string;
|
|
116
|
-
readonly errorMessage: string;
|
|
117
|
-
/** present when the environment threw an Error with a stack */
|
|
118
|
-
readonly errorStack?: string;
|
|
119
|
-
readonly connectionEffectiveType?: string;
|
|
120
|
-
readonly connectionDownlink?: number;
|
|
121
|
-
readonly connectionRtt?: number;
|
|
122
|
-
readonly connectionSaveData?: boolean;
|
|
123
|
-
readonly requestId?: string;
|
|
124
|
-
}
|
|
125
84
|
/** thrown when a request times out or the network is unavailable */
|
|
126
85
|
export declare class RadarNetworkError extends RadarError {
|
|
127
86
|
readonly name = "RadarNetworkError";
|
|
128
87
|
readonly status = "ERROR_NETWORK";
|
|
129
|
-
|
|
130
|
-
readonly details?: RadarNetworkFailureDetails;
|
|
131
|
-
/** original rejection from fetch; often a `TypeError` or `DOMException` */
|
|
132
|
-
readonly fetchError?: unknown;
|
|
133
|
-
/**
|
|
134
|
-
* Resolves when the optional Cloudflare DNS-over-HTTPS (A record) probe finishes.
|
|
135
|
-
* Only set for non-aborted fetch failures where a probe was scheduled (`null` if skipped in tests, no hostname, or a probe already ran for this host).
|
|
136
|
-
*/
|
|
137
|
-
readonly dnsProbe?: Promise<DnsProbeResult>;
|
|
138
|
-
constructor(message?: string, details?: RadarNetworkFailureDetails, fetchError?: unknown, dnsProbe?: Promise<DnsProbeResult> | null);
|
|
88
|
+
constructor();
|
|
139
89
|
}
|
|
140
90
|
/** thrown for unexpected/unclassified errors */
|
|
141
91
|
export declare class RadarUnknownError extends RadarError {
|
package/dist/index.d.ts
CHANGED
package/dist/logger.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ declare class Logger {
|
|
|
6
6
|
static info(message: string): void;
|
|
7
7
|
/** log a warning-level message */
|
|
8
8
|
static warn(message: string): void;
|
|
9
|
-
/** log an error-level message
|
|
10
|
-
static error(message: string
|
|
9
|
+
/** log an error-level message */
|
|
10
|
+
static error(message: string): void;
|
|
11
11
|
}
|
|
12
12
|
export default Logger;
|
package/dist/radar.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var SDK_VERSION = '5.1.
|
|
1
|
+
var SDK_VERSION = '5.1.1-beta.0';
|
|
2
2
|
|
|
3
3
|
/** global SDK configuration singleton */
|
|
4
4
|
class Config {
|
|
@@ -57,167 +57,6 @@ Config.defaultOptions = {
|
|
|
57
57
|
debug: false,
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
-
const LOG_LEVELS = {
|
|
61
|
-
none: 0,
|
|
62
|
-
error: 1,
|
|
63
|
-
warn: 2,
|
|
64
|
-
info: 3,
|
|
65
|
-
debug: 4,
|
|
66
|
-
};
|
|
67
|
-
// get the numeric level for logLevel option
|
|
68
|
-
const getLevel = () => {
|
|
69
|
-
// disable logging in tests
|
|
70
|
-
if (typeof window !== 'undefined' && window.RADAR_TEST_ENV) {
|
|
71
|
-
return LOG_LEVELS.none;
|
|
72
|
-
}
|
|
73
|
-
const { logLevel } = Config.get();
|
|
74
|
-
return logLevel ? LOG_LEVELS[logLevel] : LOG_LEVELS.error;
|
|
75
|
-
};
|
|
76
|
-
/** leveled console logger controlled by SDK config */
|
|
77
|
-
class Logger {
|
|
78
|
-
/** log a debug-level message (only when debug mode is enabled) */
|
|
79
|
-
static debug(message, options) {
|
|
80
|
-
if (getLevel() === LOG_LEVELS.debug) {
|
|
81
|
-
console.log(`Radar SDK (debug): ${message.trim()}`, options);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
/** log an info-level message */
|
|
85
|
-
static info(message) {
|
|
86
|
-
if (getLevel() >= LOG_LEVELS.info) {
|
|
87
|
-
console.log(`Radar SDK: ${message.trim()}`);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
/** log a warning-level message */
|
|
91
|
-
static warn(message) {
|
|
92
|
-
if (getLevel() >= LOG_LEVELS.warn) {
|
|
93
|
-
console.warn(`Radar SDK: ${message.trim()}`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
/** log an error-level message; optional second argument for structured diagnostics (printed as a separate console.error arg) */
|
|
97
|
-
static error(message, data) {
|
|
98
|
-
if (getLevel() >= LOG_LEVELS.error) {
|
|
99
|
-
const trimmed = message.trim();
|
|
100
|
-
if (data !== undefined) {
|
|
101
|
-
console.error(`Radar SDK: ${trimmed}`, data);
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
console.error(`Radar SDK: ${trimmed}`);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const CLOUDFLARE_DOH_JSON = 'https://cloudflare-dns.com/dns-query';
|
|
111
|
-
/** hostname -> already ran a probe this session */
|
|
112
|
-
const dnsProbeScheduledForHostname = new Set();
|
|
113
|
-
function getDnsProbeAbortSignal() {
|
|
114
|
-
if (typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function') {
|
|
115
|
-
return AbortSignal.timeout(8000);
|
|
116
|
-
}
|
|
117
|
-
return undefined;
|
|
118
|
-
}
|
|
119
|
-
async function dnsLookupAJson(hostname) {
|
|
120
|
-
const query = new URL(CLOUDFLARE_DOH_JSON);
|
|
121
|
-
query.searchParams.set('name', hostname);
|
|
122
|
-
query.searchParams.set('type', 'A');
|
|
123
|
-
const res = await fetch(query.toString(), {
|
|
124
|
-
headers: { Accept: 'application/dns-json' },
|
|
125
|
-
signal: getDnsProbeAbortSignal(),
|
|
126
|
-
});
|
|
127
|
-
if (!res.ok) {
|
|
128
|
-
return { kind: 'http_error', httpStatus: res.status };
|
|
129
|
-
}
|
|
130
|
-
return { kind: 'ok', data: (await res.json()) };
|
|
131
|
-
}
|
|
132
|
-
async function runDnsOverHttpsProbe(hostname, correlation) {
|
|
133
|
-
try {
|
|
134
|
-
const outcome = await dnsLookupAJson(hostname);
|
|
135
|
-
if (outcome.kind === 'http_error') {
|
|
136
|
-
const result = {
|
|
137
|
-
status: 'http_error',
|
|
138
|
-
hostname,
|
|
139
|
-
resolver: 'cloudflare-dns.com',
|
|
140
|
-
httpStatus: outcome.httpStatus,
|
|
141
|
-
};
|
|
142
|
-
Logger.error(`DNS-over-HTTPS HTTP error (${hostname})`, {
|
|
143
|
-
resolver: 'cloudflare-dns.com',
|
|
144
|
-
httpStatus: outcome.httpStatus,
|
|
145
|
-
correlation,
|
|
146
|
-
});
|
|
147
|
-
return result;
|
|
148
|
-
}
|
|
149
|
-
const data = outcome.data;
|
|
150
|
-
const ips = data.Status === 0 && Array.isArray(data.Answer) ? data.Answer.map((a) => a.data).filter(Boolean) : [];
|
|
151
|
-
if (ips.length > 0) {
|
|
152
|
-
const result = {
|
|
153
|
-
status: 'success',
|
|
154
|
-
hostname,
|
|
155
|
-
resolver: 'cloudflare-dns.com',
|
|
156
|
-
dnsStatus: data.Status,
|
|
157
|
-
ipv4Answers: ips,
|
|
158
|
-
};
|
|
159
|
-
Logger.error(`DNS-over-HTTPS (${hostname})`, {
|
|
160
|
-
resolver: 'cloudflare-dns.com',
|
|
161
|
-
dnsStatus: data.Status,
|
|
162
|
-
ipv4Answers: ips,
|
|
163
|
-
correlation,
|
|
164
|
-
});
|
|
165
|
-
return result;
|
|
166
|
-
}
|
|
167
|
-
const note = data.Status !== 0
|
|
168
|
-
? `Resolver returned DNS status ${String(data.Status)} (non-zero)`
|
|
169
|
-
: 'No IPv4 Answer records returned';
|
|
170
|
-
const result = {
|
|
171
|
-
status: 'no_answers',
|
|
172
|
-
hostname,
|
|
173
|
-
resolver: 'cloudflare-dns.com',
|
|
174
|
-
dnsStatus: data.Status,
|
|
175
|
-
answerCount: Array.isArray(data.Answer) ? data.Answer.length : 0,
|
|
176
|
-
note,
|
|
177
|
-
};
|
|
178
|
-
Logger.error(`DNS-over-HTTPS (${hostname})`, {
|
|
179
|
-
resolver: 'cloudflare-dns.com',
|
|
180
|
-
dnsStatus: data.Status,
|
|
181
|
-
answerCount: result.answerCount,
|
|
182
|
-
correlation,
|
|
183
|
-
note,
|
|
184
|
-
});
|
|
185
|
-
return result;
|
|
186
|
-
}
|
|
187
|
-
catch (err) {
|
|
188
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
189
|
-
const result = {
|
|
190
|
-
status: 'fetch_error',
|
|
191
|
-
hostname,
|
|
192
|
-
resolver: 'cloudflare-dns.com',
|
|
193
|
-
errorMessage: message,
|
|
194
|
-
};
|
|
195
|
-
Logger.error(`DNS-over-HTTPS probe failed (${hostname})`, {
|
|
196
|
-
resolver: 'cloudflare-dns.com',
|
|
197
|
-
errorMessage: message,
|
|
198
|
-
correlation,
|
|
199
|
-
});
|
|
200
|
-
return result;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Runs at most one DoH probe per hostname per page load. Returns a promise you can await
|
|
205
|
-
* from `RadarNetworkError.dnsProbe` — does not block the throwing error path.
|
|
206
|
-
*/
|
|
207
|
-
function scheduleDnsOverHttpsProbe(hostname, correlation) {
|
|
208
|
-
if (!hostname || hostname.length === 0) {
|
|
209
|
-
return null;
|
|
210
|
-
}
|
|
211
|
-
if (typeof window !== 'undefined' && window.RADAR_TEST_ENV === true) {
|
|
212
|
-
return null;
|
|
213
|
-
}
|
|
214
|
-
if (dnsProbeScheduledForHostname.has(hostname)) {
|
|
215
|
-
return null;
|
|
216
|
-
}
|
|
217
|
-
dnsProbeScheduledForHostname.add(hostname);
|
|
218
|
-
return runDnsOverHttpsProbe(hostname, correlation);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
60
|
/** base error class for all Radar SDK errors */
|
|
222
61
|
class RadarError extends Error {
|
|
223
62
|
constructor(message) {
|
|
@@ -320,13 +159,10 @@ class RadarServerError extends RadarError {
|
|
|
320
159
|
}
|
|
321
160
|
/** thrown when a request times out or the network is unavailable */
|
|
322
161
|
class RadarNetworkError extends RadarError {
|
|
323
|
-
constructor(
|
|
324
|
-
super(
|
|
162
|
+
constructor() {
|
|
163
|
+
super('Request timed out.');
|
|
325
164
|
this.name = 'RadarNetworkError';
|
|
326
165
|
this.status = 'ERROR_NETWORK';
|
|
327
|
-
this.details = details;
|
|
328
|
-
this.fetchError = fetchError;
|
|
329
|
-
this.dnsProbe = dnsProbe ?? undefined;
|
|
330
166
|
}
|
|
331
167
|
}
|
|
332
168
|
/** thrown for unexpected/unclassified errors */
|
|
@@ -356,6 +192,50 @@ var errors = /*#__PURE__*/Object.freeze({
|
|
|
356
192
|
RadarUnknownError: RadarUnknownError
|
|
357
193
|
});
|
|
358
194
|
|
|
195
|
+
const LOG_LEVELS = {
|
|
196
|
+
none: 0,
|
|
197
|
+
error: 1,
|
|
198
|
+
warn: 2,
|
|
199
|
+
info: 3,
|
|
200
|
+
debug: 4,
|
|
201
|
+
};
|
|
202
|
+
// get the numeric level for logLevel option
|
|
203
|
+
const getLevel = () => {
|
|
204
|
+
// disable logging in tests
|
|
205
|
+
if (typeof window !== 'undefined' && window.RADAR_TEST_ENV) {
|
|
206
|
+
return LOG_LEVELS.none;
|
|
207
|
+
}
|
|
208
|
+
const { logLevel } = Config.get();
|
|
209
|
+
return logLevel ? LOG_LEVELS[logLevel] : LOG_LEVELS.error;
|
|
210
|
+
};
|
|
211
|
+
/** leveled console logger controlled by SDK config */
|
|
212
|
+
class Logger {
|
|
213
|
+
/** log a debug-level message (only when debug mode is enabled) */
|
|
214
|
+
static debug(message, options) {
|
|
215
|
+
if (getLevel() === LOG_LEVELS.debug) {
|
|
216
|
+
console.log(`Radar SDK (debug): ${message.trim()}`, options);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/** log an info-level message */
|
|
220
|
+
static info(message) {
|
|
221
|
+
if (getLevel() >= LOG_LEVELS.info) {
|
|
222
|
+
console.log(`Radar SDK: ${message.trim()}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/** log a warning-level message */
|
|
226
|
+
static warn(message) {
|
|
227
|
+
if (getLevel() >= LOG_LEVELS.warn) {
|
|
228
|
+
console.warn(`Radar SDK: ${message.trim()}`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/** log an error-level message */
|
|
232
|
+
static error(message) {
|
|
233
|
+
if (getLevel() >= LOG_LEVELS.error) {
|
|
234
|
+
console.error(`Radar SDK: ${message.trim()}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
359
239
|
/** typed localStorage wrapper with `radar-*` namespaced keys */
|
|
360
240
|
class Storage {
|
|
361
241
|
/** localStorage key for user ID */
|
|
@@ -588,108 +468,6 @@ class Navigator {
|
|
|
588
468
|
}
|
|
589
469
|
|
|
590
470
|
const inFlightRequests = new Map();
|
|
591
|
-
/** gather optional Network Information API snapshot (best-effort) */
|
|
592
|
-
function getConnectionSnapshot(nav) {
|
|
593
|
-
if (!nav) {
|
|
594
|
-
return {};
|
|
595
|
-
}
|
|
596
|
-
const c = nav.connection;
|
|
597
|
-
if (!c) {
|
|
598
|
-
return {};
|
|
599
|
-
}
|
|
600
|
-
return {
|
|
601
|
-
...(c.effectiveType !== undefined ? { connectionEffectiveType: String(c.effectiveType) } : {}),
|
|
602
|
-
...(typeof c.downlink === 'number' ? { connectionDownlink: c.downlink } : {}),
|
|
603
|
-
...(typeof c.rtt === 'number' ? { connectionRtt: c.rtt } : {}),
|
|
604
|
-
...(typeof c.saveData === 'boolean' ? { connectionSaveData: c.saveData } : {}),
|
|
605
|
-
};
|
|
606
|
-
}
|
|
607
|
-
function isAbortError(err, signal) {
|
|
608
|
-
if (signal.aborted) {
|
|
609
|
-
return true;
|
|
610
|
-
}
|
|
611
|
-
if (err instanceof DOMException && err.name === 'AbortError') {
|
|
612
|
-
return true;
|
|
613
|
-
}
|
|
614
|
-
return err instanceof Error && err.name === 'AbortError';
|
|
615
|
-
}
|
|
616
|
-
function parseThrowable(err) {
|
|
617
|
-
if (err instanceof Error) {
|
|
618
|
-
return { name: err.name, message: err.message, stack: err.stack };
|
|
619
|
-
}
|
|
620
|
-
if (typeof err === 'string') {
|
|
621
|
-
return { name: 'string', message: err };
|
|
622
|
-
}
|
|
623
|
-
return { name: 'non_error', message: String(err) };
|
|
624
|
-
}
|
|
625
|
-
/** monotonic-ish timestamp in ms (`performance.now` when available; else `Date.now`) */
|
|
626
|
-
function getHighResTime() {
|
|
627
|
-
return typeof performance !== 'undefined' && typeof performance.now === 'function' ? performance.now() : Date.now();
|
|
628
|
-
}
|
|
629
|
-
function buildFetchFailureDetails(method, url, requestId, err, signal,
|
|
630
|
-
/** elapsed ms from immediately before `fetch()` until rejection */
|
|
631
|
-
durationMs) {
|
|
632
|
-
let pathname = '';
|
|
633
|
-
let search = '';
|
|
634
|
-
let apiHostname;
|
|
635
|
-
try {
|
|
636
|
-
const parsed = new URL(url);
|
|
637
|
-
pathname = parsed.pathname;
|
|
638
|
-
search = parsed.search;
|
|
639
|
-
apiHostname = parsed.hostname;
|
|
640
|
-
}
|
|
641
|
-
catch {
|
|
642
|
-
pathname = url;
|
|
643
|
-
}
|
|
644
|
-
const { name, message, stack } = parseThrowable(err);
|
|
645
|
-
const aborted = isAbortError(err, signal);
|
|
646
|
-
const nav = typeof navigator !== 'undefined' ? navigator : undefined;
|
|
647
|
-
let pageOrigin;
|
|
648
|
-
let pagePath;
|
|
649
|
-
let userAgent;
|
|
650
|
-
let crossSiteApiCall;
|
|
651
|
-
if (typeof window !== 'undefined' && window.location && nav) {
|
|
652
|
-
pageOrigin = window.location.origin;
|
|
653
|
-
pagePath = window.location.pathname === '' ? '/' : window.location.pathname;
|
|
654
|
-
userAgent = nav.userAgent;
|
|
655
|
-
if (apiHostname !== undefined && window.location.hostname !== '') {
|
|
656
|
-
crossSiteApiCall = window.location.hostname !== apiHostname;
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
const online = Boolean(nav && nav.onLine);
|
|
660
|
-
return {
|
|
661
|
-
phase: 'fetch',
|
|
662
|
-
method,
|
|
663
|
-
url,
|
|
664
|
-
pathname,
|
|
665
|
-
search,
|
|
666
|
-
durationMs: Math.max(0, Math.round(durationMs)),
|
|
667
|
-
...(apiHostname !== undefined ? { apiHostname } : {}),
|
|
668
|
-
...(pageOrigin !== undefined ? { pageOrigin } : {}),
|
|
669
|
-
...(pagePath !== undefined ? { pagePath } : {}),
|
|
670
|
-
...(userAgent !== undefined ? { userAgent } : {}),
|
|
671
|
-
...(crossSiteApiCall !== undefined ? { crossSiteApiCall } : {}),
|
|
672
|
-
online,
|
|
673
|
-
aborted,
|
|
674
|
-
errorName: name,
|
|
675
|
-
errorMessage: message,
|
|
676
|
-
...(stack ? { errorStack: stack } : {}),
|
|
677
|
-
...getConnectionSnapshot(nav),
|
|
678
|
-
...(requestId ? { requestId } : {}),
|
|
679
|
-
};
|
|
680
|
-
}
|
|
681
|
-
function fetchFailureUserMessage(details) {
|
|
682
|
-
if (details.aborted) {
|
|
683
|
-
return 'Request aborted.';
|
|
684
|
-
}
|
|
685
|
-
if (details.errorMessage) {
|
|
686
|
-
return details.errorMessage;
|
|
687
|
-
}
|
|
688
|
-
if (!details.online) {
|
|
689
|
-
return 'Network unavailable (browser offline).';
|
|
690
|
-
}
|
|
691
|
-
return 'Network request failed.';
|
|
692
|
-
}
|
|
693
471
|
/** fetch-based HTTP client for Radar API requests */
|
|
694
472
|
class Http {
|
|
695
473
|
/**
|
|
@@ -714,7 +492,10 @@ class Http {
|
|
|
714
492
|
let body;
|
|
715
493
|
if (method === 'GET') {
|
|
716
494
|
const params = new URLSearchParams(Object.entries(filtered).map(([k, v]) => [k, String(v)]));
|
|
717
|
-
|
|
495
|
+
// URLSearchParams serializes spaces as '+' (application/x-www-form-urlencoded)
|
|
496
|
+
// a query string is part of a URI, so encode per RFC 3986 (%20)
|
|
497
|
+
// the only difference is really how spaces are handled between the two protocols
|
|
498
|
+
const qs = params.toString().replace(/\+/g, '%20');
|
|
718
499
|
if (qs) {
|
|
719
500
|
url = `${url}?${qs}`;
|
|
720
501
|
}
|
|
@@ -736,7 +517,6 @@ class Http {
|
|
|
736
517
|
...headers,
|
|
737
518
|
};
|
|
738
519
|
let response;
|
|
739
|
-
const fetchStartedAt = getHighResTime();
|
|
740
520
|
try {
|
|
741
521
|
response = await fetch(url, {
|
|
742
522
|
method,
|
|
@@ -745,35 +525,19 @@ class Http {
|
|
|
745
525
|
signal: abortController.signal,
|
|
746
526
|
});
|
|
747
527
|
}
|
|
748
|
-
catch
|
|
749
|
-
let dnsProbePromise = null;
|
|
750
|
-
const durationMs = getHighResTime() - fetchStartedAt;
|
|
528
|
+
catch {
|
|
751
529
|
// Delete abort controller instance for this request ID if it hasn't yet been replaced with a different one
|
|
752
530
|
if (requestId && inFlightRequests.get(requestId) === abortController) {
|
|
753
531
|
inFlightRequests.delete(requestId);
|
|
754
532
|
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
method: fetchFailureDetails.method,
|
|
761
|
-
url: fetchFailureDetails.url,
|
|
762
|
-
durationMs: fetchFailureDetails.durationMs,
|
|
763
|
-
phase: fetchFailureDetails.phase,
|
|
764
|
-
errorName: fetchFailureDetails.errorName,
|
|
765
|
-
errorMessage: fetchFailureDetails.errorMessage,
|
|
766
|
-
crossSiteApiCall: fetchFailureDetails.crossSiteApiCall,
|
|
767
|
-
},
|
|
768
|
-
});
|
|
769
|
-
}
|
|
770
|
-
for (const [pattern, interceptorHandler] of Http.errorInterceptors) {
|
|
771
|
-
if ((urlHost ?? '').includes(pattern)) {
|
|
772
|
-
throw interceptorHandler(!!Navigator.online());
|
|
533
|
+
if (host) {
|
|
534
|
+
for (const [pattern, handler] of Http.errorInterceptors) {
|
|
535
|
+
if (host.includes(pattern)) {
|
|
536
|
+
throw handler(!!Navigator.online());
|
|
537
|
+
}
|
|
773
538
|
}
|
|
774
539
|
}
|
|
775
|
-
|
|
776
|
-
throw new RadarNetworkError(userMsg, fetchFailureDetails, fetchErr, dnsProbePromise);
|
|
540
|
+
throw new RadarNetworkError();
|
|
777
541
|
}
|
|
778
542
|
if (requestId && inFlightRequests.get(requestId) === abortController) {
|
|
779
543
|
inFlightRequests.delete(requestId);
|
|
@@ -808,12 +572,6 @@ class Http {
|
|
|
808
572
|
throw new RadarLocationError('Could not determine location.');
|
|
809
573
|
}
|
|
810
574
|
else if (error === 'ERROR_NETWORK') {
|
|
811
|
-
Logger.error('Radar API returned ERROR_NETWORK in response meta', {
|
|
812
|
-
httpStatus: response.status,
|
|
813
|
-
method,
|
|
814
|
-
url,
|
|
815
|
-
meta: parsed.meta ?? null,
|
|
816
|
-
});
|
|
817
575
|
throw new RadarNetworkError();
|
|
818
576
|
}
|
|
819
577
|
}
|
|
@@ -1664,7 +1422,7 @@ class Radar {
|
|
|
1664
1422
|
static initialize(publishableKeyOrOptions, extraOptions = {}) {
|
|
1665
1423
|
// NOTE(jasonl): shim the string signature into the options object to handle both cases
|
|
1666
1424
|
const options = typeof publishableKeyOrOptions === 'string'
|
|
1667
|
-
? {
|
|
1425
|
+
? { publishableKey: publishableKeyOrOptions, ...extraOptions }
|
|
1668
1426
|
: { ...publishableKeyOrOptions };
|
|
1669
1427
|
if (options.publishableKey && options.authToken) {
|
|
1670
1428
|
throw new RadarPublishableKeyError('Token and publishableKey are mutually exclusive.');
|