@grafana/faro-web-sdk 2.7.1 → 2.8.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/dist/bundle/faro-web-sdk.iife.js +1 -1
- package/dist/bundle/types/config/types.d.ts +1 -0
- package/dist/bundle/types/instrumentations/console/instrumentation.d.ts +1 -1
- package/dist/bundle/types/instrumentations/csp/instrumentation.d.ts +1 -1
- package/dist/bundle/types/instrumentations/errors/instrumentation.d.ts +1 -1
- package/dist/bundle/types/instrumentations/navigation/instrumentation.d.ts +1 -1
- package/dist/bundle/types/instrumentations/performance/instrumentation.d.ts +1 -1
- package/dist/bundle/types/instrumentations/session/instrumentation.d.ts +1 -1
- package/dist/bundle/types/instrumentations/userActions/instrumentation.d.ts +1 -1
- package/dist/bundle/types/instrumentations/view/instrumentation.d.ts +1 -1
- package/dist/bundle/types/instrumentations/webVitals/instrumentation.d.ts +1 -1
- package/dist/bundle/types/transports/console/transport.d.ts +1 -1
- package/dist/bundle/types/transports/fetch/transport.d.ts +7 -1
- package/dist/bundle/types/transports/fetch/types.d.ts +1 -0
- package/dist/cjs/config/makeCoreConfig.js +1 -0
- package/dist/cjs/config/makeCoreConfig.js.map +1 -1
- package/dist/cjs/config/types.js.map +1 -1
- package/dist/cjs/instrumentations/performance/performanceUtils.js +4 -1
- package/dist/cjs/instrumentations/performance/performanceUtils.js.map +1 -1
- package/dist/cjs/transports/fetch/transport.js +118 -22
- package/dist/cjs/transports/fetch/transport.js.map +1 -1
- package/dist/cjs/transports/fetch/types.js.map +1 -1
- package/dist/esm/config/makeCoreConfig.js +1 -0
- package/dist/esm/config/makeCoreConfig.js.map +1 -1
- package/dist/esm/config/types.js.map +1 -1
- package/dist/esm/instrumentations/performance/performanceUtils.js +4 -1
- package/dist/esm/instrumentations/performance/performanceUtils.js.map +1 -1
- package/dist/esm/transports/fetch/transport.js +118 -22
- package/dist/esm/transports/fetch/transport.js.map +1 -1
- package/dist/esm/transports/fetch/types.js.map +1 -1
- package/dist/types/config/types.d.ts +1 -0
- package/dist/types/instrumentations/console/instrumentation.d.ts +1 -1
- package/dist/types/instrumentations/csp/instrumentation.d.ts +1 -1
- package/dist/types/instrumentations/errors/instrumentation.d.ts +1 -1
- package/dist/types/instrumentations/navigation/instrumentation.d.ts +1 -1
- package/dist/types/instrumentations/performance/instrumentation.d.ts +1 -1
- package/dist/types/instrumentations/session/instrumentation.d.ts +1 -1
- package/dist/types/instrumentations/userActions/instrumentation.d.ts +1 -1
- package/dist/types/instrumentations/view/instrumentation.d.ts +1 -1
- package/dist/types/instrumentations/webVitals/instrumentation.d.ts +1 -1
- package/dist/types/transports/console/transport.d.ts +1 -1
- package/dist/types/transports/fetch/transport.d.ts +7 -1
- package/dist/types/transports/fetch/types.d.ts +1 -0
- package/package.json +5 -5
|
@@ -25,11 +25,14 @@ const DEFAULT_BUFFER_SIZE = 30;
|
|
|
25
25
|
const DEFAULT_CONCURRENCY = 5; // chrome supports 10 total, firefox 17
|
|
26
26
|
const DEFAULT_RATE_LIMIT_BACKOFF_MS = 5000;
|
|
27
27
|
const BEACON_BODY_SIZE_LIMIT = 60000;
|
|
28
|
+
const MAX_KEEPALIVE_REQUESTS = 9;
|
|
28
29
|
const TOO_MANY_REQUESTS = 429;
|
|
29
30
|
const ACCEPTED = 202;
|
|
31
|
+
let pendingKeepaliveBodySize = 0;
|
|
32
|
+
let pendingKeepaliveRequests = 0;
|
|
30
33
|
export class FetchTransport extends BaseTransport {
|
|
31
34
|
constructor(options) {
|
|
32
|
-
var _a, _b, _c, _d;
|
|
35
|
+
var _a, _b, _c, _d, _e;
|
|
33
36
|
super();
|
|
34
37
|
this.options = options;
|
|
35
38
|
this.name = '@grafana/faro-web-sdk:transport-fetch';
|
|
@@ -37,9 +40,17 @@ export class FetchTransport extends BaseTransport {
|
|
|
37
40
|
this.disabledUntil = new Date(0);
|
|
38
41
|
this.rateLimitBackoffMs = (_a = options.defaultRateLimitBackoffMs) !== null && _a !== void 0 ? _a : DEFAULT_RATE_LIMIT_BACKOFF_MS;
|
|
39
42
|
this.getNow = (_b = options.getNow) !== null && _b !== void 0 ? _b : (() => Date.now());
|
|
43
|
+
const requestCompression = (_c = options.requestCompression) !== null && _c !== void 0 ? _c : false;
|
|
44
|
+
if (requestCompression && typeof CompressionStream === 'undefined') {
|
|
45
|
+
this.compressionEnabled = false;
|
|
46
|
+
this.logWarn('requestCompression is enabled but CompressionStream is not available. Falling back to uncompressed.');
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
this.compressionEnabled = requestCompression;
|
|
50
|
+
}
|
|
40
51
|
this.promiseBuffer = createPromiseBuffer({
|
|
41
|
-
size: (
|
|
42
|
-
concurrency: (
|
|
52
|
+
size: (_d = options.bufferSize) !== null && _d !== void 0 ? _d : DEFAULT_BUFFER_SIZE,
|
|
53
|
+
concurrency: (_e = options.concurrency) !== null && _e !== void 0 ? _e : DEFAULT_CONCURRENCY,
|
|
43
54
|
});
|
|
44
55
|
}
|
|
45
56
|
send(items) {
|
|
@@ -50,9 +61,10 @@ export class FetchTransport extends BaseTransport {
|
|
|
50
61
|
return Promise.resolve();
|
|
51
62
|
}
|
|
52
63
|
yield this.promiseBuffer.add(() => __awaiter(this, void 0, void 0, function* () {
|
|
53
|
-
const
|
|
64
|
+
const jsonBody = JSON.stringify(getTransportBody(items));
|
|
54
65
|
const { url, requestOptions, apiKey } = this.options;
|
|
55
66
|
const _a = requestOptions !== null && requestOptions !== void 0 ? requestOptions : {}, { headers = {} } = _a, restOfRequestOptions = __rest(_a, ["headers"]);
|
|
67
|
+
const { keepalive: configuredKeepalive } = restOfRequestOptions, requestOptionsWithoutKeepalive = __rest(restOfRequestOptions, ["keepalive"]);
|
|
56
68
|
let sessionId;
|
|
57
69
|
const sessionMeta = this.metas.value.session;
|
|
58
70
|
if (sessionMeta != null) {
|
|
@@ -62,24 +74,17 @@ export class FetchTransport extends BaseTransport {
|
|
|
62
74
|
for (const [key, value] of Object.entries(headers)) {
|
|
63
75
|
resolvedHeaders[key] = typeof value === 'function' ? yield Promise.resolve(value()) : value;
|
|
64
76
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
// read the body so the connection can be closed
|
|
78
|
-
response.text().catch(noop);
|
|
79
|
-
return response;
|
|
80
|
-
}))
|
|
81
|
-
.catch((err) => {
|
|
82
|
-
this.logError('Failed sending payload to the receiver\n', JSON.parse(body), err);
|
|
77
|
+
let body = jsonBody;
|
|
78
|
+
let bodySize = jsonBody.length;
|
|
79
|
+
const compressionHeaders = {};
|
|
80
|
+
if (this.compressionEnabled) {
|
|
81
|
+
body = yield this.compress(jsonBody);
|
|
82
|
+
bodySize = body.size;
|
|
83
|
+
compressionHeaders['Content-Encoding'] = 'gzip';
|
|
84
|
+
}
|
|
85
|
+
const requestInit = Object.assign({ method: 'POST', headers: Object.assign(Object.assign(Object.assign(Object.assign({ 'Content-Type': 'application/json' }, compressionHeaders), resolvedHeaders), (apiKey ? { 'x-api-key': apiKey } : {})), (sessionId ? { 'x-faro-session-id': sessionId } : {})), body }, (requestOptionsWithoutKeepalive !== null && requestOptionsWithoutKeepalive !== void 0 ? requestOptionsWithoutKeepalive : {}));
|
|
86
|
+
return this.fetchWithKeepaliveRetry(url, requestInit, bodySize, configuredKeepalive).catch((err) => {
|
|
87
|
+
this.logError('Failed sending payload to the receiver\n', JSON.parse(jsonBody), err);
|
|
83
88
|
});
|
|
84
89
|
}));
|
|
85
90
|
}
|
|
@@ -110,6 +115,97 @@ export class FetchTransport extends BaseTransport {
|
|
|
110
115
|
}
|
|
111
116
|
return new Date(now + this.rateLimitBackoffMs);
|
|
112
117
|
}
|
|
118
|
+
reserveKeepalive(bodySize, configuredKeepalive) {
|
|
119
|
+
if (configuredKeepalive === false) {
|
|
120
|
+
return {
|
|
121
|
+
keepalive: false,
|
|
122
|
+
release: noop,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (bodySize > BEACON_BODY_SIZE_LIMIT ||
|
|
126
|
+
pendingKeepaliveBodySize + bodySize > BEACON_BODY_SIZE_LIMIT ||
|
|
127
|
+
pendingKeepaliveRequests >= MAX_KEEPALIVE_REQUESTS) {
|
|
128
|
+
this.logDebug('Disabling keepalive because the pending keepalive request budget would be exceeded.');
|
|
129
|
+
return {
|
|
130
|
+
keepalive: false,
|
|
131
|
+
release: noop,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
pendingKeepaliveBodySize += bodySize;
|
|
135
|
+
pendingKeepaliveRequests++;
|
|
136
|
+
let released = false;
|
|
137
|
+
return {
|
|
138
|
+
keepalive: true,
|
|
139
|
+
release: () => {
|
|
140
|
+
if (released) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
released = true;
|
|
144
|
+
pendingKeepaliveBodySize = Math.max(0, pendingKeepaliveBodySize - bodySize);
|
|
145
|
+
pendingKeepaliveRequests = Math.max(0, pendingKeepaliveRequests - 1);
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
fetchWithKeepaliveRetry(url, requestInit, bodySize, configuredKeepalive) {
|
|
150
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
151
|
+
const keepaliveReservation = this.reserveKeepalive(bodySize, configuredKeepalive);
|
|
152
|
+
try {
|
|
153
|
+
const response = yield fetch(url, Object.assign(Object.assign({}, requestInit), { keepalive: keepaliveReservation.keepalive }));
|
|
154
|
+
return this.handleResponse(response);
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
if (keepaliveReservation.keepalive && this.isFetchNetworkError(err)) {
|
|
158
|
+
this.logDebug('Retrying failed keepalive request with keepalive disabled.');
|
|
159
|
+
const response = yield fetch(url, Object.assign(Object.assign({}, requestInit), { keepalive: false }));
|
|
160
|
+
return this.handleResponse(response);
|
|
161
|
+
}
|
|
162
|
+
throw err;
|
|
163
|
+
}
|
|
164
|
+
finally {
|
|
165
|
+
keepaliveReservation.release();
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
handleResponse(response) {
|
|
170
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
171
|
+
if (response.status === ACCEPTED) {
|
|
172
|
+
const sessionExpired = response.headers.get('X-Faro-Session-Status') === 'invalid';
|
|
173
|
+
if (sessionExpired) {
|
|
174
|
+
this.extendFaroSession(this.config, this.logDebug);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (response.status === TOO_MANY_REQUESTS) {
|
|
178
|
+
this.disabledUntil = this.getRetryAfterDate(response);
|
|
179
|
+
this.logWarn(`Too many requests, backing off until ${this.disabledUntil}`);
|
|
180
|
+
}
|
|
181
|
+
// read the body so the connection can be closed
|
|
182
|
+
response.text().catch(noop);
|
|
183
|
+
return response;
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
isFetchNetworkError(err) {
|
|
187
|
+
return err instanceof TypeError;
|
|
188
|
+
}
|
|
189
|
+
compress(body) {
|
|
190
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
191
|
+
const stream = new ReadableStream({
|
|
192
|
+
start(controller) {
|
|
193
|
+
controller.enqueue(new TextEncoder().encode(body));
|
|
194
|
+
controller.close();
|
|
195
|
+
},
|
|
196
|
+
}).pipeThrough(new CompressionStream('gzip'));
|
|
197
|
+
const reader = stream.getReader();
|
|
198
|
+
const chunks = [];
|
|
199
|
+
for (;;) {
|
|
200
|
+
const { done, value } = yield reader.read();
|
|
201
|
+
if (done) {
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
chunks.push(value);
|
|
205
|
+
}
|
|
206
|
+
return new Blob(chunks);
|
|
207
|
+
});
|
|
208
|
+
}
|
|
113
209
|
extendFaroSession(config, logDebug) {
|
|
114
210
|
const SessionExpiredString = `Session expired`;
|
|
115
211
|
const sessionTrackingConfig = config.sessionTracking;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../../../src/transports/fetch/transport.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseExtension, BaseTransport, createPromiseBuffer, getTransportBody, noop, VERSION } from '@grafana/faro-core';\nimport type { Config, Patterns, PromiseBuffer, TransportItem } from '@grafana/faro-core';\n\nimport { getSessionManagerByConfig } from '../../instrumentations/session/sessionManager';\nimport { getUserSessionUpdater } from '../../instrumentations/session/sessionManager/sessionManagerUtils';\n\nimport type { FetchTransportOptions } from './types';\n\nconst DEFAULT_BUFFER_SIZE = 30;\nconst DEFAULT_CONCURRENCY = 5; // chrome supports 10 total, firefox 17\nconst DEFAULT_RATE_LIMIT_BACKOFF_MS = 5000;\n\nconst BEACON_BODY_SIZE_LIMIT = 60000;\nconst TOO_MANY_REQUESTS = 429;\nconst ACCEPTED = 202;\n\nexport class FetchTransport extends BaseTransport {\n readonly name = '@grafana/faro-web-sdk:transport-fetch';\n readonly version = VERSION;\n\n promiseBuffer: PromiseBuffer<Response | void>;\n\n private readonly rateLimitBackoffMs: number;\n private readonly getNow: () => number;\n private disabledUntil: Date = new Date(0);\n\n constructor(private options: FetchTransportOptions) {\n super();\n\n this.rateLimitBackoffMs = options.defaultRateLimitBackoffMs ?? DEFAULT_RATE_LIMIT_BACKOFF_MS;\n this.getNow = options.getNow ?? (() => Date.now());\n\n this.promiseBuffer = createPromiseBuffer({\n size: options.bufferSize ?? DEFAULT_BUFFER_SIZE,\n concurrency: options.concurrency ?? DEFAULT_CONCURRENCY,\n });\n }\n\n async send(items: TransportItem[]): Promise<void> {\n try {\n if (this.disabledUntil > new Date(this.getNow())) {\n this.logWarn(`Dropping transport item due to too many requests. Backoff until ${this.disabledUntil}`);\n\n return Promise.resolve();\n }\n\n await this.promiseBuffer.add(async () => {\n const body = JSON.stringify(getTransportBody(items));\n\n const { url, requestOptions, apiKey } = this.options;\n\n const { headers = {}, ...restOfRequestOptions } = requestOptions ?? {};\n\n let sessionId;\n const sessionMeta = this.metas.value.session;\n if (sessionMeta != null) {\n sessionId = sessionMeta.id;\n }\n\n const resolvedHeaders: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n resolvedHeaders[key] = typeof value === 'function' ? await Promise.resolve(value()) : value;\n }\n\n return fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...resolvedHeaders,\n ...(apiKey ? { 'x-api-key': apiKey } : {}),\n ...(sessionId ? { 'x-faro-session-id': sessionId } : {}),\n },\n body,\n keepalive: body.length <= BEACON_BODY_SIZE_LIMIT,\n ...(restOfRequestOptions ?? {}),\n })\n .then(async (response) => {\n if (response.status === ACCEPTED) {\n const sessionExpired = response.headers.get('X-Faro-Session-Status') === 'invalid';\n\n if (sessionExpired) {\n this.extendFaroSession(this.config, this.logDebug);\n }\n }\n\n if (response.status === TOO_MANY_REQUESTS) {\n this.disabledUntil = this.getRetryAfterDate(response);\n this.logWarn(`Too many requests, backing off until ${this.disabledUntil}`);\n }\n\n // read the body so the connection can be closed\n response.text().catch(noop);\n return response;\n })\n .catch((err) => {\n this.logError('Failed sending payload to the receiver\\n', JSON.parse(body), err);\n });\n });\n } catch (err) {\n this.logError(err);\n }\n }\n\n override getIgnoreUrls(): Patterns {\n return ([this.options.url] as Patterns).concat(this.config.ignoreUrls ?? []);\n }\n\n override isBatched(): boolean {\n return true;\n }\n\n private getRetryAfterDate(response: Response): Date {\n const now = this.getNow();\n const retryAfterHeader = response.headers.get('Retry-After');\n\n if (retryAfterHeader) {\n const delay = Number(retryAfterHeader);\n\n if (!isNaN(delay)) {\n return new Date(delay * 1000 + now);\n }\n\n const date = Date.parse(retryAfterHeader);\n\n if (!isNaN(date)) {\n return new Date(date);\n }\n }\n\n return new Date(now + this.rateLimitBackoffMs);\n }\n\n private extendFaroSession(config: Config, logDebug: BaseExtension['logDebug']) {\n const SessionExpiredString = `Session expired`;\n\n const sessionTrackingConfig = config.sessionTracking;\n\n if (sessionTrackingConfig?.enabled) {\n const { fetchUserSession, storeUserSession } = getSessionManagerByConfig(sessionTrackingConfig);\n\n getUserSessionUpdater({ fetchUserSession, storeUserSession })({ forceSessionExtend: true });\n\n logDebug(`${SessionExpiredString} created new session.`);\n } else {\n logDebug(`${SessionExpiredString}.`);\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../../../src/transports/fetch/transport.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseExtension, BaseTransport, createPromiseBuffer, getTransportBody, noop, VERSION } from '@grafana/faro-core';\nimport type { Config, Patterns, PromiseBuffer, TransportItem } from '@grafana/faro-core';\n\nimport { getSessionManagerByConfig } from '../../instrumentations/session/sessionManager';\nimport { getUserSessionUpdater } from '../../instrumentations/session/sessionManager/sessionManagerUtils';\n\nimport type { FetchTransportOptions } from './types';\n\nconst DEFAULT_BUFFER_SIZE = 30;\nconst DEFAULT_CONCURRENCY = 5; // chrome supports 10 total, firefox 17\nconst DEFAULT_RATE_LIMIT_BACKOFF_MS = 5000;\n\nconst BEACON_BODY_SIZE_LIMIT = 60000;\nconst MAX_KEEPALIVE_REQUESTS = 9;\nconst TOO_MANY_REQUESTS = 429;\nconst ACCEPTED = 202;\n\nlet pendingKeepaliveBodySize = 0;\nlet pendingKeepaliveRequests = 0;\n\ninterface KeepaliveReservation {\n keepalive: boolean;\n release: () => void;\n}\n\nexport class FetchTransport extends BaseTransport {\n readonly name = '@grafana/faro-web-sdk:transport-fetch';\n readonly version = VERSION;\n\n promiseBuffer: PromiseBuffer<Response | void>;\n\n private readonly rateLimitBackoffMs: number;\n private readonly getNow: () => number;\n private readonly compressionEnabled: boolean;\n private disabledUntil: Date = new Date(0);\n\n constructor(private options: FetchTransportOptions) {\n super();\n\n this.rateLimitBackoffMs = options.defaultRateLimitBackoffMs ?? DEFAULT_RATE_LIMIT_BACKOFF_MS;\n this.getNow = options.getNow ?? (() => Date.now());\n\n const requestCompression = options.requestCompression ?? false;\n\n if (requestCompression && typeof CompressionStream === 'undefined') {\n this.compressionEnabled = false;\n this.logWarn(\n 'requestCompression is enabled but CompressionStream is not available. Falling back to uncompressed.'\n );\n } else {\n this.compressionEnabled = requestCompression;\n }\n\n this.promiseBuffer = createPromiseBuffer({\n size: options.bufferSize ?? DEFAULT_BUFFER_SIZE,\n concurrency: options.concurrency ?? DEFAULT_CONCURRENCY,\n });\n }\n\n async send(items: TransportItem[]): Promise<void> {\n try {\n if (this.disabledUntil > new Date(this.getNow())) {\n this.logWarn(`Dropping transport item due to too many requests. Backoff until ${this.disabledUntil}`);\n\n return Promise.resolve();\n }\n\n await this.promiseBuffer.add(async () => {\n const jsonBody = JSON.stringify(getTransportBody(items));\n\n const { url, requestOptions, apiKey } = this.options;\n\n const { headers = {}, ...restOfRequestOptions } = requestOptions ?? {};\n const { keepalive: configuredKeepalive, ...requestOptionsWithoutKeepalive } = restOfRequestOptions;\n\n let sessionId;\n const sessionMeta = this.metas.value.session;\n if (sessionMeta != null) {\n sessionId = sessionMeta.id;\n }\n\n const resolvedHeaders: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n resolvedHeaders[key] = typeof value === 'function' ? await Promise.resolve(value()) : value;\n }\n\n let body: string | Blob = jsonBody;\n let bodySize = jsonBody.length;\n const compressionHeaders: Record<string, string> = {};\n\n if (this.compressionEnabled) {\n body = await this.compress(jsonBody);\n bodySize = body.size;\n compressionHeaders['Content-Encoding'] = 'gzip';\n }\n\n const requestInit: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...compressionHeaders,\n ...resolvedHeaders,\n ...(apiKey ? { 'x-api-key': apiKey } : {}),\n ...(sessionId ? { 'x-faro-session-id': sessionId } : {}),\n },\n body,\n ...(requestOptionsWithoutKeepalive ?? {}),\n };\n\n return this.fetchWithKeepaliveRetry(url, requestInit, bodySize, configuredKeepalive).catch((err) => {\n this.logError('Failed sending payload to the receiver\\n', JSON.parse(jsonBody), err);\n });\n });\n } catch (err) {\n this.logError(err);\n }\n }\n\n override getIgnoreUrls(): Patterns {\n return ([this.options.url] as Patterns).concat(this.config.ignoreUrls ?? []);\n }\n\n override isBatched(): boolean {\n return true;\n }\n\n private getRetryAfterDate(response: Response): Date {\n const now = this.getNow();\n const retryAfterHeader = response.headers.get('Retry-After');\n\n if (retryAfterHeader) {\n const delay = Number(retryAfterHeader);\n\n if (!isNaN(delay)) {\n return new Date(delay * 1000 + now);\n }\n\n const date = Date.parse(retryAfterHeader);\n\n if (!isNaN(date)) {\n return new Date(date);\n }\n }\n\n return new Date(now + this.rateLimitBackoffMs);\n }\n\n private reserveKeepalive(bodySize: number, configuredKeepalive?: boolean): KeepaliveReservation {\n if (configuredKeepalive === false) {\n return {\n keepalive: false,\n release: noop,\n };\n }\n\n if (\n bodySize > BEACON_BODY_SIZE_LIMIT ||\n pendingKeepaliveBodySize + bodySize > BEACON_BODY_SIZE_LIMIT ||\n pendingKeepaliveRequests >= MAX_KEEPALIVE_REQUESTS\n ) {\n this.logDebug('Disabling keepalive because the pending keepalive request budget would be exceeded.');\n\n return {\n keepalive: false,\n release: noop,\n };\n }\n\n pendingKeepaliveBodySize += bodySize;\n pendingKeepaliveRequests++;\n\n let released = false;\n\n return {\n keepalive: true,\n release: () => {\n if (released) {\n return;\n }\n\n released = true;\n pendingKeepaliveBodySize = Math.max(0, pendingKeepaliveBodySize - bodySize);\n pendingKeepaliveRequests = Math.max(0, pendingKeepaliveRequests - 1);\n },\n };\n }\n\n private async fetchWithKeepaliveRetry(\n url: string,\n requestInit: RequestInit,\n bodySize: number,\n configuredKeepalive?: boolean\n ): Promise<Response> {\n const keepaliveReservation = this.reserveKeepalive(bodySize, configuredKeepalive);\n\n try {\n const response = await fetch(url, {\n ...requestInit,\n keepalive: keepaliveReservation.keepalive,\n });\n\n return this.handleResponse(response);\n } catch (err) {\n if (keepaliveReservation.keepalive && this.isFetchNetworkError(err)) {\n this.logDebug('Retrying failed keepalive request with keepalive disabled.');\n\n const response = await fetch(url, {\n ...requestInit,\n keepalive: false,\n });\n\n return this.handleResponse(response);\n }\n\n throw err;\n } finally {\n keepaliveReservation.release();\n }\n }\n\n private async handleResponse(response: Response): Promise<Response> {\n if (response.status === ACCEPTED) {\n const sessionExpired = response.headers.get('X-Faro-Session-Status') === 'invalid';\n\n if (sessionExpired) {\n this.extendFaroSession(this.config, this.logDebug);\n }\n }\n\n if (response.status === TOO_MANY_REQUESTS) {\n this.disabledUntil = this.getRetryAfterDate(response);\n this.logWarn(`Too many requests, backing off until ${this.disabledUntil}`);\n }\n\n // read the body so the connection can be closed\n response.text().catch(noop);\n return response;\n }\n\n private isFetchNetworkError(err: unknown): boolean {\n return err instanceof TypeError;\n }\n\n private async compress(body: string): Promise<Blob> {\n const stream = new ReadableStream({\n start(controller) {\n controller.enqueue(new TextEncoder().encode(body));\n controller.close();\n },\n }).pipeThrough(new CompressionStream('gzip'));\n\n const reader = stream.getReader();\n const chunks: BlobPart[] = [];\n for (;;) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n chunks.push(value);\n }\n return new Blob(chunks);\n }\n\n private extendFaroSession(config: Config, logDebug: BaseExtension['logDebug']) {\n const SessionExpiredString = `Session expired`;\n\n const sessionTrackingConfig = config.sessionTracking;\n\n if (sessionTrackingConfig?.enabled) {\n const { fetchUserSession, storeUserSession } = getSessionManagerByConfig(sessionTrackingConfig);\n\n getUserSessionUpdater({ fetchUserSession, storeUserSession })({ forceSessionExtend: true });\n\n logDebug(`${SessionExpiredString} created new session.`);\n } else {\n logDebug(`${SessionExpiredString}.`);\n }\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/transports/fetch/types.ts"],"names":[],"mappings":"","sourcesContent":["export interface FetchTransportRequestOptions extends Omit<RequestInit, 'body' | 'headers'> {\n /**\n * Headers to include in every request.\n * Each value can be:\n * - a string (static value)\n * - a function returning a string (dynamic value, sync)\n * - a function returning a Promise of string (dynamic value, async)\n */\n headers?: Record<string, string | (() => string | Promise<string>)>;\n}\n\nexport interface FetchTransportOptions {\n // url of the collector endpoint\n url: string;\n\n // will be added as `x-api-key` header\n apiKey?: string;\n // how many requests to buffer in total\n bufferSize?: number;\n // how many requests to execute concurrently\n concurrency?: number;\n // if rate limit response does not include a Retry-After header,\n // how many milliseconds to back off before attempting a request.\n // intermediate events will be dropped, not buffered\n defaultRateLimitBackoffMs?: number;\n // get current date. for mocking purposes in tests\n getNow?: ClockFn;\n // addition options for global.Fetch\n requestOptions?: FetchTransportRequestOptions;\n}\n\nexport type ClockFn = () => number;\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/transports/fetch/types.ts"],"names":[],"mappings":"","sourcesContent":["export interface FetchTransportRequestOptions extends Omit<RequestInit, 'body' | 'headers'> {\n /**\n * Headers to include in every request.\n * Each value can be:\n * - a string (static value)\n * - a function returning a string (dynamic value, sync)\n * - a function returning a Promise of string (dynamic value, async)\n */\n headers?: Record<string, string | (() => string | Promise<string>)>;\n}\n\nexport interface FetchTransportOptions {\n // url of the collector endpoint\n url: string;\n\n // will be added as `x-api-key` header\n apiKey?: string;\n // how many requests to buffer in total\n bufferSize?: number;\n // how many requests to execute concurrently\n concurrency?: number;\n // if rate limit response does not include a Retry-After header,\n // how many milliseconds to back off before attempting a request.\n // intermediate events will be dropped, not buffered\n defaultRateLimitBackoffMs?: number;\n // get current date. for mocking purposes in tests\n getNow?: ClockFn;\n // addition options for global.Fetch\n requestOptions?: FetchTransportRequestOptions;\n // compress request bodies with gzip using the native CompressionStream API.\n // falls back to uncompressed if CompressionStream is not available.\n requestCompression?: boolean;\n}\n\nexport type ClockFn = () => number;\n"]}
|
|
@@ -2,6 +2,7 @@ import type { Config } from '@grafana/faro-core';
|
|
|
2
2
|
export interface BrowserConfig extends Partial<Omit<Config, 'app' | 'parseStacktrace'>>, Pick<Config, 'app'> {
|
|
3
3
|
url?: string;
|
|
4
4
|
apiKey?: string;
|
|
5
|
+
requestCompression?: boolean;
|
|
5
6
|
}
|
|
6
7
|
export interface GetWebInstrumentationsOptions {
|
|
7
8
|
captureConsole?: boolean;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseInstrumentation, LogLevel } from '@grafana/faro-core';
|
|
2
2
|
export declare class ConsoleInstrumentation extends BaseInstrumentation {
|
|
3
3
|
readonly name = "@grafana/faro-web-sdk:instrumentation-console";
|
|
4
|
-
readonly version = "2.
|
|
4
|
+
readonly version = "2.8.1";
|
|
5
5
|
static defaultDisabledLevels: LogLevel[];
|
|
6
6
|
static consoleErrorPrefix: string;
|
|
7
7
|
private errorSerializer;
|
|
@@ -2,7 +2,7 @@ import { BaseInstrumentation } from '@grafana/faro-core';
|
|
|
2
2
|
import type { Instrumentation } from '@grafana/faro-core';
|
|
3
3
|
export declare class CSPInstrumentation extends BaseInstrumentation implements Instrumentation {
|
|
4
4
|
readonly name = "@grafana/faro-web-sdk:instrumentation-csp";
|
|
5
|
-
readonly version = "2.
|
|
5
|
+
readonly version = "2.8.1";
|
|
6
6
|
constructor();
|
|
7
7
|
initialize(): void;
|
|
8
8
|
destroy(): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseInstrumentation } from '@grafana/faro-core';
|
|
2
2
|
export declare class ErrorsInstrumentation extends BaseInstrumentation {
|
|
3
3
|
readonly name = "@grafana/faro-web-sdk:instrumentation-errors";
|
|
4
|
-
readonly version = "2.
|
|
4
|
+
readonly version = "2.8.1";
|
|
5
5
|
initialize(): void;
|
|
6
6
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseInstrumentation } from '@grafana/faro-core';
|
|
2
2
|
export declare class NavigationInstrumentation extends BaseInstrumentation {
|
|
3
3
|
readonly name = "@grafana/faro-web-sdk:instrumentation-navigation";
|
|
4
|
-
readonly version = "2.
|
|
4
|
+
readonly version = "2.8.1";
|
|
5
5
|
initialize(): void;
|
|
6
6
|
}
|
|
@@ -3,6 +3,6 @@ import type { ResourceEntryMessage } from './types';
|
|
|
3
3
|
export declare const performanceEntriesSubscription: Observable<ResourceEntryMessage>;
|
|
4
4
|
export declare class PerformanceInstrumentation extends BaseInstrumentation {
|
|
5
5
|
readonly name = "@grafana/faro-web-sdk:instrumentation-performance";
|
|
6
|
-
readonly version = "2.
|
|
6
|
+
readonly version = "2.8.1";
|
|
7
7
|
initialize(): void;
|
|
8
8
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseInstrumentation } from '@grafana/faro-core';
|
|
2
2
|
export declare class SessionInstrumentation extends BaseInstrumentation {
|
|
3
3
|
readonly name = "@grafana/faro-web-sdk:instrumentation-session";
|
|
4
|
-
readonly version = "2.
|
|
4
|
+
readonly version = "2.8.1";
|
|
5
5
|
private notifiedSession;
|
|
6
6
|
private sendSessionStartEvent;
|
|
7
7
|
private createInitialSession;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseInstrumentation } from '@grafana/faro-core';
|
|
2
2
|
export declare class UserActionInstrumentation extends BaseInstrumentation {
|
|
3
3
|
readonly name = "@grafana/faro-web-sdk:instrumentation-user-action";
|
|
4
|
-
readonly version = "2.
|
|
4
|
+
readonly version = "2.8.1";
|
|
5
5
|
private _userActionSub?;
|
|
6
6
|
initialize(): void;
|
|
7
7
|
destroy(): void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseInstrumentation } from '@grafana/faro-core';
|
|
2
2
|
export declare class ViewInstrumentation extends BaseInstrumentation {
|
|
3
3
|
readonly name = "@grafana/faro-web-sdk:instrumentation-view";
|
|
4
|
-
readonly version = "2.
|
|
4
|
+
readonly version = "2.8.1";
|
|
5
5
|
private notifiedView;
|
|
6
6
|
private sendViewChangedEvent;
|
|
7
7
|
initialize(): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseInstrumentation } from '@grafana/faro-core';
|
|
2
2
|
export declare class WebVitalsInstrumentation extends BaseInstrumentation {
|
|
3
3
|
readonly name = "@grafana/faro-web-sdk:instrumentation-web-vitals";
|
|
4
|
-
readonly version = "2.
|
|
4
|
+
readonly version = "2.8.1";
|
|
5
5
|
initialize(): void;
|
|
6
6
|
}
|
|
@@ -4,7 +4,7 @@ import type { ConsoleTransportOptions } from './types';
|
|
|
4
4
|
export declare class ConsoleTransport extends BaseTransport {
|
|
5
5
|
private options;
|
|
6
6
|
readonly name = "@grafana/faro-web-sdk:transport-console";
|
|
7
|
-
readonly version = "2.
|
|
7
|
+
readonly version = "2.8.1";
|
|
8
8
|
constructor(options?: ConsoleTransportOptions);
|
|
9
9
|
send(item: TransportItem): void;
|
|
10
10
|
}
|
|
@@ -4,15 +4,21 @@ import type { FetchTransportOptions } from './types';
|
|
|
4
4
|
export declare class FetchTransport extends BaseTransport {
|
|
5
5
|
private options;
|
|
6
6
|
readonly name = "@grafana/faro-web-sdk:transport-fetch";
|
|
7
|
-
readonly version = "2.
|
|
7
|
+
readonly version = "2.8.1";
|
|
8
8
|
promiseBuffer: PromiseBuffer<Response | void>;
|
|
9
9
|
private readonly rateLimitBackoffMs;
|
|
10
10
|
private readonly getNow;
|
|
11
|
+
private readonly compressionEnabled;
|
|
11
12
|
private disabledUntil;
|
|
12
13
|
constructor(options: FetchTransportOptions);
|
|
13
14
|
send(items: TransportItem[]): Promise<void>;
|
|
14
15
|
getIgnoreUrls(): Patterns;
|
|
15
16
|
isBatched(): boolean;
|
|
16
17
|
private getRetryAfterDate;
|
|
18
|
+
private reserveKeepalive;
|
|
19
|
+
private fetchWithKeepaliveRetry;
|
|
20
|
+
private handleResponse;
|
|
21
|
+
private isFetchNetworkError;
|
|
22
|
+
private compress;
|
|
17
23
|
private extendFaroSession;
|
|
18
24
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grafana/faro-web-sdk",
|
|
3
|
-
"packageManager": "yarn@4.
|
|
4
|
-
"version": "2.
|
|
3
|
+
"packageManager": "yarn@4.17.0",
|
|
4
|
+
"version": "2.8.1",
|
|
5
5
|
"description": "Faro instrumentations, metas, transports for web.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"observability",
|
|
@@ -57,17 +57,17 @@
|
|
|
57
57
|
"quality:circular-deps": "madge --circular ."
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@grafana/faro-core": "^2.
|
|
60
|
+
"@grafana/faro-core": "^2.8.1",
|
|
61
61
|
"ua-parser-js": "1.0.41",
|
|
62
62
|
"web-vitals": "^5.1.0"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
|
-
"@types/node": "24.
|
|
65
|
+
"@types/node": "24.13.2",
|
|
66
66
|
"@types/ua-parser-js": "0.7.39",
|
|
67
67
|
"user-agent-data-types": "0.4.3"
|
|
68
68
|
},
|
|
69
69
|
"publishConfig": {
|
|
70
70
|
"access": "public"
|
|
71
71
|
},
|
|
72
|
-
"gitHead": "
|
|
72
|
+
"gitHead": "8efae53b855f9924cc730a7e198796f886875ca0"
|
|
73
73
|
}
|