@djangocfg/monitor 2.1.216
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 +341 -0
- package/dist/client.cjs +1273 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +123 -0
- package/dist/client.d.ts +123 -0
- package/dist/client.mjs +1243 -0
- package/dist/client.mjs.map +1 -0
- package/dist/index.cjs +18 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +101 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.mjs +1 -0
- package/dist/index.mjs.map +1 -0
- package/dist/server.cjs +947 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +117 -0
- package/dist/server.d.ts +117 -0
- package/dist/server.mjs +917 -0
- package/dist/server.mjs.map +1 -0
- package/package.json +82 -0
- package/src/.claude/.sidecar/activity.jsonl +1 -0
- package/src/.claude/.sidecar/map_cache.json +38 -0
- package/src/.claude/.sidecar/usage.json +5 -0
- package/src/.claude/project-map.md +29 -0
- package/src/_api/BaseClient.ts +18 -0
- package/src/_api/generated/cfg_monitor/CLAUDE.md +60 -0
- package/src/_api/generated/cfg_monitor/_utils/fetchers/index.ts +30 -0
- package/src/_api/generated/cfg_monitor/_utils/fetchers/monitor.ts +51 -0
- package/src/_api/generated/cfg_monitor/_utils/hooks/index.ts +30 -0
- package/src/_api/generated/cfg_monitor/_utils/hooks/monitor.ts +43 -0
- package/src/_api/generated/cfg_monitor/_utils/schemas/FrontendEventIngestRequest.schema.ts +34 -0
- package/src/_api/generated/cfg_monitor/_utils/schemas/IngestBatchRequest.schema.ts +20 -0
- package/src/_api/generated/cfg_monitor/_utils/schemas/index.ts +22 -0
- package/src/_api/generated/cfg_monitor/api-instance.ts +181 -0
- package/src/_api/generated/cfg_monitor/client.ts +322 -0
- package/src/_api/generated/cfg_monitor/enums.ts +36 -0
- package/src/_api/generated/cfg_monitor/errors.ts +118 -0
- package/src/_api/generated/cfg_monitor/http.ts +137 -0
- package/src/_api/generated/cfg_monitor/index.ts +317 -0
- package/src/_api/generated/cfg_monitor/logger.ts +261 -0
- package/src/_api/generated/cfg_monitor/monitor/client.ts +25 -0
- package/src/_api/generated/cfg_monitor/monitor/index.ts +4 -0
- package/src/_api/generated/cfg_monitor/monitor/models.ts +48 -0
- package/src/_api/generated/cfg_monitor/retry.ts +177 -0
- package/src/_api/generated/cfg_monitor/schema.json +184 -0
- package/src/_api/generated/cfg_monitor/storage.ts +163 -0
- package/src/_api/generated/cfg_monitor/validation-events.ts +135 -0
- package/src/_api/index.ts +6 -0
- package/src/client/capture/console.ts +72 -0
- package/src/client/capture/fingerprint.ts +27 -0
- package/src/client/capture/js-errors.ts +70 -0
- package/src/client/capture/network.ts +47 -0
- package/src/client/capture/session.ts +33 -0
- package/src/client/capture/validation.ts +38 -0
- package/src/client/index.ts +72 -0
- package/src/client/store/index.ts +41 -0
- package/src/client/transport/ingest.ts +31 -0
- package/src/index.ts +12 -0
- package/src/server/index.ts +85 -0
- package/src/types/config.ts +33 -0
- package/src/types/events.ts +5 -0
- package/src/types/index.ts +6 -0
package/dist/client.mjs
ADDED
|
@@ -0,0 +1,1243 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
4
|
+
|
|
5
|
+
// src/client/capture/fingerprint.ts
|
|
6
|
+
function simpleHash(str) {
|
|
7
|
+
let hash = 0;
|
|
8
|
+
for (let i = 0; i < str.length; i++) {
|
|
9
|
+
hash = (hash << 5) - hash + str.charCodeAt(i);
|
|
10
|
+
hash = hash & hash;
|
|
11
|
+
}
|
|
12
|
+
return Math.abs(hash).toString(16).padStart(8, "0");
|
|
13
|
+
}
|
|
14
|
+
__name(simpleHash, "simpleHash");
|
|
15
|
+
async function computeFingerprint(message, stack, url) {
|
|
16
|
+
const raw = `${message}|${stack}|${url}`;
|
|
17
|
+
if (typeof crypto !== "undefined" && crypto.subtle) {
|
|
18
|
+
try {
|
|
19
|
+
const data = new TextEncoder().encode(raw);
|
|
20
|
+
const buf = await crypto.subtle.digest("SHA-256", data);
|
|
21
|
+
return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("").slice(0, 64);
|
|
22
|
+
} catch {
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return simpleHash(raw);
|
|
26
|
+
}
|
|
27
|
+
__name(computeFingerprint, "computeFingerprint");
|
|
28
|
+
|
|
29
|
+
// src/client/capture/session.ts
|
|
30
|
+
var SESSION_KEY = "fm_session_id";
|
|
31
|
+
var COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
|
|
32
|
+
function generateUUID() {
|
|
33
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
34
|
+
return crypto.randomUUID();
|
|
35
|
+
}
|
|
36
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
37
|
+
const r = Math.random() * 16 | 0;
|
|
38
|
+
return (c === "x" ? r : r & 3 | 8).toString(16);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
__name(generateUUID, "generateUUID");
|
|
42
|
+
function setCookie(name, value) {
|
|
43
|
+
if (typeof document === "undefined") return;
|
|
44
|
+
document.cookie = `${name}=${value}; path=/; SameSite=Lax; max-age=${COOKIE_MAX_AGE}`;
|
|
45
|
+
}
|
|
46
|
+
__name(setCookie, "setCookie");
|
|
47
|
+
function getSessionId() {
|
|
48
|
+
if (typeof localStorage === "undefined") return "";
|
|
49
|
+
let id = localStorage.getItem(SESSION_KEY);
|
|
50
|
+
if (!id) {
|
|
51
|
+
id = generateUUID();
|
|
52
|
+
localStorage.setItem(SESSION_KEY, id);
|
|
53
|
+
setCookie(SESSION_KEY, id);
|
|
54
|
+
}
|
|
55
|
+
return id;
|
|
56
|
+
}
|
|
57
|
+
__name(getSessionId, "getSessionId");
|
|
58
|
+
function ensureSessionCookie() {
|
|
59
|
+
const id = getSessionId();
|
|
60
|
+
if (id) setCookie(SESSION_KEY, id);
|
|
61
|
+
}
|
|
62
|
+
__name(ensureSessionCookie, "ensureSessionCookie");
|
|
63
|
+
|
|
64
|
+
// src/client/store/index.ts
|
|
65
|
+
import { createStore } from "zustand/vanilla";
|
|
66
|
+
|
|
67
|
+
// src/_api/generated/cfg_monitor/monitor/client.ts
|
|
68
|
+
var _Monitor = class _Monitor {
|
|
69
|
+
constructor(client) {
|
|
70
|
+
this.client = client;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Ingest browser events
|
|
74
|
+
*
|
|
75
|
+
* Accepts a batch of up to 50 frontend events. No authentication required.
|
|
76
|
+
*/
|
|
77
|
+
async ingestCreate(data) {
|
|
78
|
+
const response = await this.client.request("POST", "/cfg/monitor/ingest/", { body: data });
|
|
79
|
+
return response;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
__name(_Monitor, "Monitor");
|
|
83
|
+
var Monitor = _Monitor;
|
|
84
|
+
|
|
85
|
+
// src/_api/generated/cfg_monitor/http.ts
|
|
86
|
+
var _FetchAdapter = class _FetchAdapter {
|
|
87
|
+
async request(request) {
|
|
88
|
+
const { method, url, headers, body, params, formData, binaryBody } = request;
|
|
89
|
+
let finalUrl = url;
|
|
90
|
+
if (params) {
|
|
91
|
+
const searchParams = new URLSearchParams();
|
|
92
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
93
|
+
if (value !== null && value !== void 0) {
|
|
94
|
+
searchParams.append(key, String(value));
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
const queryString = searchParams.toString();
|
|
98
|
+
if (queryString) {
|
|
99
|
+
finalUrl = url.includes("?") ? `${url}&${queryString}` : `${url}?${queryString}`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const finalHeaders = { ...headers };
|
|
103
|
+
let requestBody;
|
|
104
|
+
if (formData) {
|
|
105
|
+
requestBody = formData;
|
|
106
|
+
} else if (binaryBody) {
|
|
107
|
+
finalHeaders["Content-Type"] = "application/octet-stream";
|
|
108
|
+
requestBody = binaryBody;
|
|
109
|
+
} else if (body) {
|
|
110
|
+
finalHeaders["Content-Type"] = "application/json";
|
|
111
|
+
requestBody = JSON.stringify(body);
|
|
112
|
+
}
|
|
113
|
+
const response = await fetch(finalUrl, {
|
|
114
|
+
method,
|
|
115
|
+
headers: finalHeaders,
|
|
116
|
+
body: requestBody,
|
|
117
|
+
credentials: "include"
|
|
118
|
+
// Include Django session cookies
|
|
119
|
+
});
|
|
120
|
+
let data = null;
|
|
121
|
+
const contentType = response.headers.get("content-type");
|
|
122
|
+
if (response.status !== 204 && contentType?.includes("application/json")) {
|
|
123
|
+
data = await response.json();
|
|
124
|
+
} else if (response.status !== 204) {
|
|
125
|
+
data = await response.text();
|
|
126
|
+
}
|
|
127
|
+
const responseHeaders = {};
|
|
128
|
+
response.headers.forEach((value, key) => {
|
|
129
|
+
responseHeaders[key] = value;
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
data,
|
|
133
|
+
status: response.status,
|
|
134
|
+
statusText: response.statusText,
|
|
135
|
+
headers: responseHeaders
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
__name(_FetchAdapter, "FetchAdapter");
|
|
140
|
+
var FetchAdapter = _FetchAdapter;
|
|
141
|
+
var _KeepAliveFetchAdapter = class _KeepAliveFetchAdapter extends FetchAdapter {
|
|
142
|
+
async request(request) {
|
|
143
|
+
const origFetch = globalThis.fetch;
|
|
144
|
+
globalThis.fetch = (input, init) => origFetch(input, { ...init, keepalive: true });
|
|
145
|
+
try {
|
|
146
|
+
return await super.request(request);
|
|
147
|
+
} finally {
|
|
148
|
+
globalThis.fetch = origFetch;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
__name(_KeepAliveFetchAdapter, "KeepAliveFetchAdapter");
|
|
153
|
+
var KeepAliveFetchAdapter = _KeepAliveFetchAdapter;
|
|
154
|
+
|
|
155
|
+
// src/_api/generated/cfg_monitor/errors.ts
|
|
156
|
+
var _APIError = class _APIError extends Error {
|
|
157
|
+
constructor(statusCode, statusText, response, url, message) {
|
|
158
|
+
super(message || `HTTP ${statusCode}: ${statusText}`);
|
|
159
|
+
this.statusCode = statusCode;
|
|
160
|
+
this.statusText = statusText;
|
|
161
|
+
this.response = response;
|
|
162
|
+
this.url = url;
|
|
163
|
+
this.name = "APIError";
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Get error details from response.
|
|
167
|
+
* DRF typically returns: { "detail": "Error message" } or { "field": ["error1", "error2"] }
|
|
168
|
+
*/
|
|
169
|
+
get details() {
|
|
170
|
+
if (typeof this.response === "object" && this.response !== null) {
|
|
171
|
+
return this.response;
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Get field-specific validation errors from DRF.
|
|
177
|
+
* Returns: { "field_name": ["error1", "error2"], ... }
|
|
178
|
+
*/
|
|
179
|
+
get fieldErrors() {
|
|
180
|
+
const details = this.details;
|
|
181
|
+
if (!details) return null;
|
|
182
|
+
const fieldErrors = {};
|
|
183
|
+
for (const [key, value] of Object.entries(details)) {
|
|
184
|
+
if (Array.isArray(value)) {
|
|
185
|
+
fieldErrors[key] = value;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return Object.keys(fieldErrors).length > 0 ? fieldErrors : null;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Get single error message from DRF.
|
|
192
|
+
* Checks for "detail", "message", or first field error.
|
|
193
|
+
*/
|
|
194
|
+
get errorMessage() {
|
|
195
|
+
const details = this.details;
|
|
196
|
+
if (!details) return this.message;
|
|
197
|
+
if (details.detail) {
|
|
198
|
+
return Array.isArray(details.detail) ? details.detail.join(", ") : String(details.detail);
|
|
199
|
+
}
|
|
200
|
+
if (details.message) {
|
|
201
|
+
return String(details.message);
|
|
202
|
+
}
|
|
203
|
+
const fieldErrors = this.fieldErrors;
|
|
204
|
+
if (fieldErrors) {
|
|
205
|
+
const firstField = Object.keys(fieldErrors)[0];
|
|
206
|
+
if (firstField) {
|
|
207
|
+
return `${firstField}: ${fieldErrors[firstField]?.join(", ")}`;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return this.message;
|
|
211
|
+
}
|
|
212
|
+
// Helper methods for common HTTP status codes
|
|
213
|
+
get isValidationError() {
|
|
214
|
+
return this.statusCode === 400;
|
|
215
|
+
}
|
|
216
|
+
get isAuthError() {
|
|
217
|
+
return this.statusCode === 401;
|
|
218
|
+
}
|
|
219
|
+
get isPermissionError() {
|
|
220
|
+
return this.statusCode === 403;
|
|
221
|
+
}
|
|
222
|
+
get isNotFoundError() {
|
|
223
|
+
return this.statusCode === 404;
|
|
224
|
+
}
|
|
225
|
+
get isServerError() {
|
|
226
|
+
return this.statusCode >= 500 && this.statusCode < 600;
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
__name(_APIError, "APIError");
|
|
230
|
+
var APIError = _APIError;
|
|
231
|
+
var _NetworkError = class _NetworkError extends Error {
|
|
232
|
+
constructor(message, url, originalError) {
|
|
233
|
+
super(message);
|
|
234
|
+
this.url = url;
|
|
235
|
+
this.originalError = originalError;
|
|
236
|
+
this.name = "NetworkError";
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
__name(_NetworkError, "NetworkError");
|
|
240
|
+
var NetworkError = _NetworkError;
|
|
241
|
+
|
|
242
|
+
// src/_api/generated/cfg_monitor/logger.ts
|
|
243
|
+
import { createConsola } from "consola";
|
|
244
|
+
var DEFAULT_CONFIG = {
|
|
245
|
+
enabled: true,
|
|
246
|
+
logRequests: true,
|
|
247
|
+
logResponses: true,
|
|
248
|
+
logErrors: true,
|
|
249
|
+
logBodies: true,
|
|
250
|
+
logHeaders: false
|
|
251
|
+
};
|
|
252
|
+
var SENSITIVE_HEADERS = [
|
|
253
|
+
"authorization",
|
|
254
|
+
"cookie",
|
|
255
|
+
"set-cookie",
|
|
256
|
+
"x-api-key",
|
|
257
|
+
"x-csrf-token"
|
|
258
|
+
];
|
|
259
|
+
var _APILogger = class _APILogger {
|
|
260
|
+
constructor(config = {}) {
|
|
261
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
262
|
+
this.consola = config.consola || createConsola({
|
|
263
|
+
level: this.config.enabled ? 4 : 0
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Enable logging
|
|
268
|
+
*/
|
|
269
|
+
enable() {
|
|
270
|
+
this.config.enabled = true;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Disable logging
|
|
274
|
+
*/
|
|
275
|
+
disable() {
|
|
276
|
+
this.config.enabled = false;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Update configuration
|
|
280
|
+
*/
|
|
281
|
+
setConfig(config) {
|
|
282
|
+
this.config = { ...this.config, ...config };
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Filter sensitive headers
|
|
286
|
+
*/
|
|
287
|
+
filterHeaders(headers) {
|
|
288
|
+
if (!headers) return {};
|
|
289
|
+
const filtered = {};
|
|
290
|
+
Object.keys(headers).forEach((key) => {
|
|
291
|
+
const lowerKey = key.toLowerCase();
|
|
292
|
+
if (SENSITIVE_HEADERS.includes(lowerKey)) {
|
|
293
|
+
filtered[key] = "***";
|
|
294
|
+
} else {
|
|
295
|
+
filtered[key] = headers[key] || "";
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
return filtered;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Log request
|
|
302
|
+
*/
|
|
303
|
+
logRequest(request) {
|
|
304
|
+
if (!this.config.enabled || !this.config.logRequests) return;
|
|
305
|
+
const { method, url, headers, body } = request;
|
|
306
|
+
this.consola.start(`${method} ${url}`);
|
|
307
|
+
if (this.config.logHeaders && headers) {
|
|
308
|
+
this.consola.debug("Headers:", this.filterHeaders(headers));
|
|
309
|
+
}
|
|
310
|
+
if (this.config.logBodies && body) {
|
|
311
|
+
this.consola.debug("Body:", body);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Log response
|
|
316
|
+
*/
|
|
317
|
+
logResponse(request, response) {
|
|
318
|
+
if (!this.config.enabled || !this.config.logResponses) return;
|
|
319
|
+
const { method, url } = request;
|
|
320
|
+
const { status, statusText, data, duration } = response;
|
|
321
|
+
const statusColor = status >= 500 ? "red" : status >= 400 ? "yellow" : status >= 300 ? "cyan" : "green";
|
|
322
|
+
this.consola.success(
|
|
323
|
+
`${method} ${url} ${status} ${statusText} (${duration}ms)`
|
|
324
|
+
);
|
|
325
|
+
if (this.config.logBodies && data) {
|
|
326
|
+
this.consola.debug("Response:", data);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Log error
|
|
331
|
+
*/
|
|
332
|
+
logError(request, error) {
|
|
333
|
+
if (!this.config.enabled || !this.config.logErrors) return;
|
|
334
|
+
const { method, url } = request;
|
|
335
|
+
const { message, statusCode, fieldErrors, duration } = error;
|
|
336
|
+
this.consola.error(
|
|
337
|
+
`${method} ${url} ${statusCode || "Network"} Error (${duration}ms)`
|
|
338
|
+
);
|
|
339
|
+
this.consola.error("Message:", message);
|
|
340
|
+
if (fieldErrors && Object.keys(fieldErrors).length > 0) {
|
|
341
|
+
this.consola.error("Field Errors:");
|
|
342
|
+
Object.entries(fieldErrors).forEach(([field, errors]) => {
|
|
343
|
+
errors.forEach((err) => {
|
|
344
|
+
this.consola.error(` \u2022 ${field}: ${err}`);
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Log general info
|
|
351
|
+
*/
|
|
352
|
+
info(message, ...args) {
|
|
353
|
+
if (!this.config.enabled) return;
|
|
354
|
+
this.consola.info(message, ...args);
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Log warning
|
|
358
|
+
*/
|
|
359
|
+
warn(message, ...args) {
|
|
360
|
+
if (!this.config.enabled) return;
|
|
361
|
+
this.consola.warn(message, ...args);
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Log error
|
|
365
|
+
*/
|
|
366
|
+
error(message, ...args) {
|
|
367
|
+
if (!this.config.enabled) return;
|
|
368
|
+
this.consola.error(message, ...args);
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Log debug
|
|
372
|
+
*/
|
|
373
|
+
debug(message, ...args) {
|
|
374
|
+
if (!this.config.enabled) return;
|
|
375
|
+
this.consola.debug(message, ...args);
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Log success
|
|
379
|
+
*/
|
|
380
|
+
success(message, ...args) {
|
|
381
|
+
if (!this.config.enabled) return;
|
|
382
|
+
this.consola.success(message, ...args);
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Create a sub-logger with prefix
|
|
386
|
+
*/
|
|
387
|
+
withTag(tag) {
|
|
388
|
+
return this.consola.withTag(tag);
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
__name(_APILogger, "APILogger");
|
|
392
|
+
var APILogger = _APILogger;
|
|
393
|
+
var defaultLogger = new APILogger();
|
|
394
|
+
|
|
395
|
+
// src/_api/generated/cfg_monitor/retry.ts
|
|
396
|
+
import pRetry, { AbortError } from "p-retry";
|
|
397
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
398
|
+
retries: 3,
|
|
399
|
+
factor: 2,
|
|
400
|
+
minTimeout: 1e3,
|
|
401
|
+
maxTimeout: 6e4,
|
|
402
|
+
randomize: true,
|
|
403
|
+
onFailedAttempt: /* @__PURE__ */ __name(() => {
|
|
404
|
+
}, "onFailedAttempt")
|
|
405
|
+
};
|
|
406
|
+
function shouldRetry(error) {
|
|
407
|
+
if (error instanceof NetworkError) {
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
if (error instanceof APIError) {
|
|
411
|
+
const status = error.statusCode;
|
|
412
|
+
if (status >= 500 && status < 600) {
|
|
413
|
+
return true;
|
|
414
|
+
}
|
|
415
|
+
if (status === 429) {
|
|
416
|
+
return true;
|
|
417
|
+
}
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
return true;
|
|
421
|
+
}
|
|
422
|
+
__name(shouldRetry, "shouldRetry");
|
|
423
|
+
async function withRetry(fn, config) {
|
|
424
|
+
const finalConfig = { ...DEFAULT_RETRY_CONFIG, ...config };
|
|
425
|
+
return pRetry(
|
|
426
|
+
async () => {
|
|
427
|
+
try {
|
|
428
|
+
return await fn();
|
|
429
|
+
} catch (error) {
|
|
430
|
+
if (!shouldRetry(error)) {
|
|
431
|
+
throw new AbortError(error);
|
|
432
|
+
}
|
|
433
|
+
throw error;
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
retries: finalConfig.retries,
|
|
438
|
+
factor: finalConfig.factor,
|
|
439
|
+
minTimeout: finalConfig.minTimeout,
|
|
440
|
+
maxTimeout: finalConfig.maxTimeout,
|
|
441
|
+
randomize: finalConfig.randomize,
|
|
442
|
+
onFailedAttempt: finalConfig.onFailedAttempt ? (error) => {
|
|
443
|
+
const pRetryError = error;
|
|
444
|
+
finalConfig.onFailedAttempt({
|
|
445
|
+
error: pRetryError,
|
|
446
|
+
attemptNumber: pRetryError.attemptNumber,
|
|
447
|
+
retriesLeft: pRetryError.retriesLeft
|
|
448
|
+
});
|
|
449
|
+
} : void 0
|
|
450
|
+
}
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
__name(withRetry, "withRetry");
|
|
454
|
+
|
|
455
|
+
// src/_api/generated/cfg_monitor/client.ts
|
|
456
|
+
var _APIClient = class _APIClient {
|
|
457
|
+
constructor(baseUrl, options) {
|
|
458
|
+
this.logger = null;
|
|
459
|
+
this.retryConfig = null;
|
|
460
|
+
this.tokenGetter = null;
|
|
461
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
462
|
+
this.httpClient = options?.httpClient || new FetchAdapter();
|
|
463
|
+
this.tokenGetter = options?.tokenGetter || null;
|
|
464
|
+
if (options?.loggerConfig !== void 0) {
|
|
465
|
+
this.logger = new APILogger(options.loggerConfig);
|
|
466
|
+
}
|
|
467
|
+
if (options?.retryConfig !== void 0) {
|
|
468
|
+
this.retryConfig = options.retryConfig;
|
|
469
|
+
}
|
|
470
|
+
this.monitor = new Monitor(this);
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Get CSRF token from cookies (for SessionAuthentication).
|
|
474
|
+
*
|
|
475
|
+
* Returns null if cookie doesn't exist (JWT-only auth).
|
|
476
|
+
*/
|
|
477
|
+
getCsrfToken() {
|
|
478
|
+
const name = "csrftoken";
|
|
479
|
+
const value = `; ${document.cookie}`;
|
|
480
|
+
const parts = value.split(`; ${name}=`);
|
|
481
|
+
if (parts.length === 2) {
|
|
482
|
+
return parts.pop()?.split(";").shift() || null;
|
|
483
|
+
}
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Get the base URL for building streaming/download URLs.
|
|
488
|
+
*/
|
|
489
|
+
getBaseUrl() {
|
|
490
|
+
return this.baseUrl;
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Get JWT token for URL authentication (used in streaming endpoints).
|
|
494
|
+
* Returns null if no token getter is configured or no token is available.
|
|
495
|
+
*/
|
|
496
|
+
getToken() {
|
|
497
|
+
return this.tokenGetter ? this.tokenGetter() : null;
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Make HTTP request with Django CSRF and session handling.
|
|
501
|
+
* Automatically retries on network errors and 5xx server errors.
|
|
502
|
+
*/
|
|
503
|
+
async request(method, path, options) {
|
|
504
|
+
if (this.retryConfig) {
|
|
505
|
+
return withRetry(() => this._makeRequest(method, path, options), {
|
|
506
|
+
...this.retryConfig,
|
|
507
|
+
onFailedAttempt: /* @__PURE__ */ __name((info) => {
|
|
508
|
+
if (this.logger) {
|
|
509
|
+
this.logger.warn(
|
|
510
|
+
`Retry attempt ${info.attemptNumber}/${info.retriesLeft + info.attemptNumber} for ${method} ${path}: ${info.error.message}`
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
this.retryConfig?.onFailedAttempt?.(info);
|
|
514
|
+
}, "onFailedAttempt")
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
return this._makeRequest(method, path, options);
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Internal request method (without retry wrapper).
|
|
521
|
+
* Used by request() method with optional retry logic.
|
|
522
|
+
*/
|
|
523
|
+
async _makeRequest(method, path, options) {
|
|
524
|
+
const url = this.baseUrl ? `${this.baseUrl}${path}` : path;
|
|
525
|
+
const startTime = Date.now();
|
|
526
|
+
const headers = {
|
|
527
|
+
...options?.headers || {}
|
|
528
|
+
};
|
|
529
|
+
if (!options?.formData && !options?.binaryBody && !headers["Content-Type"]) {
|
|
530
|
+
headers["Content-Type"] = "application/json";
|
|
531
|
+
}
|
|
532
|
+
if (this.logger) {
|
|
533
|
+
this.logger.logRequest({
|
|
534
|
+
method,
|
|
535
|
+
url,
|
|
536
|
+
headers,
|
|
537
|
+
body: options?.formData || options?.body,
|
|
538
|
+
timestamp: startTime
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
try {
|
|
542
|
+
const response = await this.httpClient.request({
|
|
543
|
+
method,
|
|
544
|
+
url,
|
|
545
|
+
headers,
|
|
546
|
+
params: options?.params,
|
|
547
|
+
body: options?.body,
|
|
548
|
+
formData: options?.formData,
|
|
549
|
+
binaryBody: options?.binaryBody
|
|
550
|
+
});
|
|
551
|
+
const duration = Date.now() - startTime;
|
|
552
|
+
if (response.status >= 400) {
|
|
553
|
+
const error = new APIError(
|
|
554
|
+
response.status,
|
|
555
|
+
response.statusText,
|
|
556
|
+
response.data,
|
|
557
|
+
url
|
|
558
|
+
);
|
|
559
|
+
if (this.logger) {
|
|
560
|
+
this.logger.logError(
|
|
561
|
+
{
|
|
562
|
+
method,
|
|
563
|
+
url,
|
|
564
|
+
headers,
|
|
565
|
+
body: options?.formData || options?.body,
|
|
566
|
+
timestamp: startTime
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
message: error.message,
|
|
570
|
+
statusCode: response.status,
|
|
571
|
+
duration,
|
|
572
|
+
timestamp: Date.now()
|
|
573
|
+
}
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
throw error;
|
|
577
|
+
}
|
|
578
|
+
if (this.logger) {
|
|
579
|
+
this.logger.logResponse(
|
|
580
|
+
{
|
|
581
|
+
method,
|
|
582
|
+
url,
|
|
583
|
+
headers,
|
|
584
|
+
body: options?.formData || options?.body,
|
|
585
|
+
timestamp: startTime
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
status: response.status,
|
|
589
|
+
statusText: response.statusText,
|
|
590
|
+
data: response.data,
|
|
591
|
+
duration,
|
|
592
|
+
timestamp: Date.now()
|
|
593
|
+
}
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
return response.data;
|
|
597
|
+
} catch (error) {
|
|
598
|
+
const duration = Date.now() - startTime;
|
|
599
|
+
if (error instanceof APIError) {
|
|
600
|
+
throw error;
|
|
601
|
+
}
|
|
602
|
+
const isCORSError = error instanceof TypeError && (error.message.toLowerCase().includes("cors") || error.message.toLowerCase().includes("failed to fetch") || error.message.toLowerCase().includes("network request failed"));
|
|
603
|
+
if (this.logger) {
|
|
604
|
+
if (isCORSError) {
|
|
605
|
+
this.logger.error(`\u{1F6AB} CORS Error: ${method} ${url}`);
|
|
606
|
+
this.logger.error(` \u2192 ${error instanceof Error ? error.message : String(error)}`);
|
|
607
|
+
this.logger.error(` \u2192 Configure security_domains parameter on the server`);
|
|
608
|
+
} else {
|
|
609
|
+
this.logger.error(`\u26A0\uFE0F Network Error: ${method} ${url}`);
|
|
610
|
+
this.logger.error(` \u2192 ${error instanceof Error ? error.message : String(error)}`);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
if (typeof window !== "undefined") {
|
|
614
|
+
try {
|
|
615
|
+
if (isCORSError) {
|
|
616
|
+
window.dispatchEvent(new CustomEvent("cors-error", {
|
|
617
|
+
detail: {
|
|
618
|
+
url,
|
|
619
|
+
method,
|
|
620
|
+
error: error instanceof Error ? error.message : String(error),
|
|
621
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
622
|
+
},
|
|
623
|
+
bubbles: true,
|
|
624
|
+
cancelable: false
|
|
625
|
+
}));
|
|
626
|
+
} else {
|
|
627
|
+
window.dispatchEvent(new CustomEvent("network-error", {
|
|
628
|
+
detail: {
|
|
629
|
+
url,
|
|
630
|
+
method,
|
|
631
|
+
error: error instanceof Error ? error.message : String(error),
|
|
632
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
633
|
+
},
|
|
634
|
+
bubbles: true,
|
|
635
|
+
cancelable: false
|
|
636
|
+
}));
|
|
637
|
+
}
|
|
638
|
+
} catch (eventError) {
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
const networkError = error instanceof Error ? new NetworkError(error.message, url, error) : new NetworkError("Unknown error", url);
|
|
642
|
+
if (this.logger) {
|
|
643
|
+
this.logger.logError(
|
|
644
|
+
{
|
|
645
|
+
method,
|
|
646
|
+
url,
|
|
647
|
+
headers,
|
|
648
|
+
body: options?.formData || options?.body,
|
|
649
|
+
timestamp: startTime
|
|
650
|
+
},
|
|
651
|
+
{
|
|
652
|
+
message: networkError.message,
|
|
653
|
+
duration,
|
|
654
|
+
timestamp: Date.now()
|
|
655
|
+
}
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
throw networkError;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
__name(_APIClient, "APIClient");
|
|
663
|
+
var APIClient = _APIClient;
|
|
664
|
+
|
|
665
|
+
// src/_api/generated/cfg_monitor/storage.ts
|
|
666
|
+
var _LocalStorageAdapter = class _LocalStorageAdapter {
|
|
667
|
+
constructor(logger) {
|
|
668
|
+
this.logger = logger;
|
|
669
|
+
}
|
|
670
|
+
getItem(key) {
|
|
671
|
+
try {
|
|
672
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
673
|
+
const value = localStorage.getItem(key);
|
|
674
|
+
this.logger?.debug(`LocalStorage.getItem("${key}"): ${value ? "found" : "not found"}`);
|
|
675
|
+
return value;
|
|
676
|
+
}
|
|
677
|
+
this.logger?.warn("LocalStorage not available: window.localStorage is undefined");
|
|
678
|
+
} catch (error) {
|
|
679
|
+
this.logger?.error("LocalStorage.getItem failed:", error);
|
|
680
|
+
}
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
setItem(key, value) {
|
|
684
|
+
try {
|
|
685
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
686
|
+
localStorage.setItem(key, value);
|
|
687
|
+
this.logger?.debug(`LocalStorage.setItem("${key}"): success`);
|
|
688
|
+
} else {
|
|
689
|
+
this.logger?.warn("LocalStorage not available: window.localStorage is undefined");
|
|
690
|
+
}
|
|
691
|
+
} catch (error) {
|
|
692
|
+
this.logger?.error("LocalStorage.setItem failed:", error);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
removeItem(key) {
|
|
696
|
+
try {
|
|
697
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
698
|
+
localStorage.removeItem(key);
|
|
699
|
+
this.logger?.debug(`LocalStorage.removeItem("${key}"): success`);
|
|
700
|
+
} else {
|
|
701
|
+
this.logger?.warn("LocalStorage not available: window.localStorage is undefined");
|
|
702
|
+
}
|
|
703
|
+
} catch (error) {
|
|
704
|
+
this.logger?.error("LocalStorage.removeItem failed:", error);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
__name(_LocalStorageAdapter, "LocalStorageAdapter");
|
|
709
|
+
var LocalStorageAdapter = _LocalStorageAdapter;
|
|
710
|
+
var _MemoryStorageAdapter = class _MemoryStorageAdapter {
|
|
711
|
+
constructor(logger) {
|
|
712
|
+
this.storage = /* @__PURE__ */ new Map();
|
|
713
|
+
this.logger = logger;
|
|
714
|
+
}
|
|
715
|
+
getItem(key) {
|
|
716
|
+
const value = this.storage.get(key) || null;
|
|
717
|
+
this.logger?.debug(`MemoryStorage.getItem("${key}"): ${value ? "found" : "not found"}`);
|
|
718
|
+
return value;
|
|
719
|
+
}
|
|
720
|
+
setItem(key, value) {
|
|
721
|
+
this.storage.set(key, value);
|
|
722
|
+
this.logger?.debug(`MemoryStorage.setItem("${key}"): success`);
|
|
723
|
+
}
|
|
724
|
+
removeItem(key) {
|
|
725
|
+
this.storage.delete(key);
|
|
726
|
+
this.logger?.debug(`MemoryStorage.removeItem("${key}"): success`);
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
__name(_MemoryStorageAdapter, "MemoryStorageAdapter");
|
|
730
|
+
var MemoryStorageAdapter = _MemoryStorageAdapter;
|
|
731
|
+
|
|
732
|
+
// src/_api/generated/cfg_monitor/enums.ts
|
|
733
|
+
var FrontendEventIngestRequestEventType = /* @__PURE__ */ ((FrontendEventIngestRequestEventType2) => {
|
|
734
|
+
FrontendEventIngestRequestEventType2["ERROR"] = "ERROR";
|
|
735
|
+
FrontendEventIngestRequestEventType2["WARNING"] = "WARNING";
|
|
736
|
+
FrontendEventIngestRequestEventType2["INFO"] = "INFO";
|
|
737
|
+
FrontendEventIngestRequestEventType2["PAGE_VIEW"] = "PAGE_VIEW";
|
|
738
|
+
FrontendEventIngestRequestEventType2["PERFORMANCE"] = "PERFORMANCE";
|
|
739
|
+
FrontendEventIngestRequestEventType2["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
740
|
+
FrontendEventIngestRequestEventType2["JS_ERROR"] = "JS_ERROR";
|
|
741
|
+
FrontendEventIngestRequestEventType2["CONSOLE"] = "CONSOLE";
|
|
742
|
+
return FrontendEventIngestRequestEventType2;
|
|
743
|
+
})(FrontendEventIngestRequestEventType || {});
|
|
744
|
+
var FrontendEventIngestRequestLevel = /* @__PURE__ */ ((FrontendEventIngestRequestLevel2) => {
|
|
745
|
+
FrontendEventIngestRequestLevel2["ERROR"] = "error";
|
|
746
|
+
FrontendEventIngestRequestLevel2["WARN"] = "warn";
|
|
747
|
+
FrontendEventIngestRequestLevel2["INFO"] = "info";
|
|
748
|
+
FrontendEventIngestRequestLevel2["DEBUG"] = "debug";
|
|
749
|
+
return FrontendEventIngestRequestLevel2;
|
|
750
|
+
})(FrontendEventIngestRequestLevel || {});
|
|
751
|
+
|
|
752
|
+
// src/_api/generated/cfg_monitor/_utils/schemas/FrontendEventIngestRequest.schema.ts
|
|
753
|
+
import { z } from "zod";
|
|
754
|
+
var FrontendEventIngestRequestSchema = z.object({
|
|
755
|
+
event_type: z.nativeEnum(FrontendEventIngestRequestEventType),
|
|
756
|
+
message: z.string().min(1).max(5e3),
|
|
757
|
+
level: z.nativeEnum(FrontendEventIngestRequestLevel).optional(),
|
|
758
|
+
stack_trace: z.string().max(2e4).optional(),
|
|
759
|
+
url: z.string().max(2e3).optional(),
|
|
760
|
+
fingerprint: z.string().max(64).optional(),
|
|
761
|
+
http_status: z.number().int().nullable().optional(),
|
|
762
|
+
http_method: z.string().max(10).optional(),
|
|
763
|
+
http_url: z.string().max(2e3).optional(),
|
|
764
|
+
user_agent: z.string().max(500).optional(),
|
|
765
|
+
session_id: z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i).nullable().optional(),
|
|
766
|
+
browser_fingerprint: z.string().max(64).optional(),
|
|
767
|
+
extra: z.record(z.string(), z.any()).optional(),
|
|
768
|
+
project_name: z.string().max(100).optional(),
|
|
769
|
+
environment: z.string().max(20).optional()
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
// src/_api/generated/cfg_monitor/_utils/schemas/IngestBatchRequest.schema.ts
|
|
773
|
+
import { z as z2 } from "zod";
|
|
774
|
+
var IngestBatchRequestSchema = z2.object({
|
|
775
|
+
events: z2.array(FrontendEventIngestRequestSchema)
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
// src/_api/generated/cfg_monitor/index.ts
|
|
779
|
+
var TOKEN_KEY = "auth_token";
|
|
780
|
+
var REFRESH_TOKEN_KEY = "refresh_token";
|
|
781
|
+
function detectLocale() {
|
|
782
|
+
try {
|
|
783
|
+
if (typeof document !== "undefined") {
|
|
784
|
+
const match = document.cookie.match(/(?:^|;\s*)NEXT_LOCALE=([^;]*)/);
|
|
785
|
+
if (match) return match[1];
|
|
786
|
+
}
|
|
787
|
+
if (typeof navigator !== "undefined" && navigator.language) {
|
|
788
|
+
return navigator.language;
|
|
789
|
+
}
|
|
790
|
+
} catch {
|
|
791
|
+
}
|
|
792
|
+
return null;
|
|
793
|
+
}
|
|
794
|
+
__name(detectLocale, "detectLocale");
|
|
795
|
+
var _API = class _API {
|
|
796
|
+
constructor(baseUrl, options) {
|
|
797
|
+
this._token = null;
|
|
798
|
+
this._refreshToken = null;
|
|
799
|
+
this._locale = null;
|
|
800
|
+
this.baseUrl = baseUrl;
|
|
801
|
+
this.options = options;
|
|
802
|
+
const logger = options?.loggerConfig ? new APILogger(options.loggerConfig) : void 0;
|
|
803
|
+
this.storage = options?.storage || new LocalStorageAdapter(logger);
|
|
804
|
+
this._locale = options?.locale || null;
|
|
805
|
+
this._loadTokensFromStorage();
|
|
806
|
+
this._client = new APIClient(this.baseUrl, {
|
|
807
|
+
httpClient: this.options?.httpClient,
|
|
808
|
+
retryConfig: this.options?.retryConfig,
|
|
809
|
+
loggerConfig: this.options?.loggerConfig,
|
|
810
|
+
tokenGetter: /* @__PURE__ */ __name(() => this.getToken(), "tokenGetter")
|
|
811
|
+
});
|
|
812
|
+
this._injectAuthHeader();
|
|
813
|
+
this.monitor = this._client.monitor;
|
|
814
|
+
}
|
|
815
|
+
_loadTokensFromStorage() {
|
|
816
|
+
this._token = this.storage.getItem(TOKEN_KEY);
|
|
817
|
+
this._refreshToken = this.storage.getItem(REFRESH_TOKEN_KEY);
|
|
818
|
+
}
|
|
819
|
+
_reinitClients() {
|
|
820
|
+
this._client = new APIClient(this.baseUrl, {
|
|
821
|
+
httpClient: this.options?.httpClient,
|
|
822
|
+
retryConfig: this.options?.retryConfig,
|
|
823
|
+
loggerConfig: this.options?.loggerConfig,
|
|
824
|
+
tokenGetter: /* @__PURE__ */ __name(() => this.getToken(), "tokenGetter")
|
|
825
|
+
});
|
|
826
|
+
this._injectAuthHeader();
|
|
827
|
+
this.monitor = this._client.monitor;
|
|
828
|
+
}
|
|
829
|
+
_injectAuthHeader() {
|
|
830
|
+
const originalRequest = this._client.request.bind(this._client);
|
|
831
|
+
this._client.request = async (method, path, options) => {
|
|
832
|
+
const token = this.getToken();
|
|
833
|
+
const locale = this._locale || detectLocale();
|
|
834
|
+
const mergedOptions = {
|
|
835
|
+
...options,
|
|
836
|
+
headers: {
|
|
837
|
+
...options?.headers || {},
|
|
838
|
+
...token ? { "Authorization": `Bearer ${token}` } : {},
|
|
839
|
+
...locale ? { "Accept-Language": locale } : {}
|
|
840
|
+
}
|
|
841
|
+
};
|
|
842
|
+
return originalRequest(method, path, mergedOptions);
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Get current JWT token
|
|
847
|
+
*/
|
|
848
|
+
getToken() {
|
|
849
|
+
return this.storage.getItem(TOKEN_KEY);
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Get current refresh token
|
|
853
|
+
*/
|
|
854
|
+
getRefreshToken() {
|
|
855
|
+
return this.storage.getItem(REFRESH_TOKEN_KEY);
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Set JWT token and refresh token
|
|
859
|
+
* @param token - JWT access token
|
|
860
|
+
* @param refreshToken - JWT refresh token (optional)
|
|
861
|
+
*/
|
|
862
|
+
setToken(token, refreshToken) {
|
|
863
|
+
this._token = token;
|
|
864
|
+
this.storage.setItem(TOKEN_KEY, token);
|
|
865
|
+
if (refreshToken) {
|
|
866
|
+
this._refreshToken = refreshToken;
|
|
867
|
+
this.storage.setItem(REFRESH_TOKEN_KEY, refreshToken);
|
|
868
|
+
}
|
|
869
|
+
this._reinitClients();
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* Clear all tokens
|
|
873
|
+
*/
|
|
874
|
+
clearTokens() {
|
|
875
|
+
this._token = null;
|
|
876
|
+
this._refreshToken = null;
|
|
877
|
+
this.storage.removeItem(TOKEN_KEY);
|
|
878
|
+
this.storage.removeItem(REFRESH_TOKEN_KEY);
|
|
879
|
+
this._reinitClients();
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Check if user is authenticated
|
|
883
|
+
*/
|
|
884
|
+
isAuthenticated() {
|
|
885
|
+
return !!this.getToken();
|
|
886
|
+
}
|
|
887
|
+
/**
|
|
888
|
+
* Update base URL and reinitialize clients
|
|
889
|
+
* @param url - New base URL
|
|
890
|
+
*/
|
|
891
|
+
setBaseUrl(url) {
|
|
892
|
+
this.baseUrl = url;
|
|
893
|
+
this._reinitClients();
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Get current base URL
|
|
897
|
+
*/
|
|
898
|
+
getBaseUrl() {
|
|
899
|
+
return this.baseUrl;
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* Set locale for Accept-Language header
|
|
903
|
+
* @param locale - Locale string (e.g. 'en', 'ko', 'ru') or null to clear
|
|
904
|
+
*/
|
|
905
|
+
setLocale(locale) {
|
|
906
|
+
this._locale = locale;
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Get current locale
|
|
910
|
+
*/
|
|
911
|
+
getLocale() {
|
|
912
|
+
return this._locale;
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Get OpenAPI schema path
|
|
916
|
+
* @returns Path to the OpenAPI schema JSON file
|
|
917
|
+
*
|
|
918
|
+
* Note: The OpenAPI schema is available in the schema.json file.
|
|
919
|
+
* You can load it dynamically using:
|
|
920
|
+
* ```typescript
|
|
921
|
+
* const schema = await fetch('./schema.json').then(r => r.json());
|
|
922
|
+
* // or using fs in Node.js:
|
|
923
|
+
* // const schema = JSON.parse(fs.readFileSync('./schema.json', 'utf-8'));
|
|
924
|
+
* ```
|
|
925
|
+
*/
|
|
926
|
+
getSchemaPath() {
|
|
927
|
+
return "./schema.json";
|
|
928
|
+
}
|
|
929
|
+
};
|
|
930
|
+
__name(_API, "API");
|
|
931
|
+
var API = _API;
|
|
932
|
+
|
|
933
|
+
// src/_api/BaseClient.ts
|
|
934
|
+
var monitorApi = new API("", { storage: new MemoryStorageAdapter() });
|
|
935
|
+
function configureMonitorApi(baseUrl) {
|
|
936
|
+
monitorApi.setBaseUrl(baseUrl);
|
|
937
|
+
}
|
|
938
|
+
__name(configureMonitorApi, "configureMonitorApi");
|
|
939
|
+
var _BaseClient = class _BaseClient {
|
|
940
|
+
};
|
|
941
|
+
__name(_BaseClient, "BaseClient");
|
|
942
|
+
_BaseClient.monitorApi = monitorApi;
|
|
943
|
+
var BaseClient = _BaseClient;
|
|
944
|
+
|
|
945
|
+
// src/client/transport/ingest.ts
|
|
946
|
+
var monitorApiBeacon = new API("", {
|
|
947
|
+
storage: new MemoryStorageAdapter(),
|
|
948
|
+
httpClient: new KeepAliveFetchAdapter()
|
|
949
|
+
});
|
|
950
|
+
function syncBeaconBaseUrl() {
|
|
951
|
+
monitorApiBeacon.setBaseUrl(monitorApi.getBaseUrl());
|
|
952
|
+
}
|
|
953
|
+
__name(syncBeaconBaseUrl, "syncBeaconBaseUrl");
|
|
954
|
+
async function sendBatch(batch, useBeacon = false) {
|
|
955
|
+
if (batch.events.length === 0) return;
|
|
956
|
+
try {
|
|
957
|
+
if (useBeacon) {
|
|
958
|
+
syncBeaconBaseUrl();
|
|
959
|
+
await monitorApiBeacon.monitor.ingestCreate(batch);
|
|
960
|
+
} else {
|
|
961
|
+
await monitorApi.monitor.ingestCreate(batch);
|
|
962
|
+
}
|
|
963
|
+
} catch {
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
__name(sendBatch, "sendBatch");
|
|
967
|
+
|
|
968
|
+
// src/client/store/index.ts
|
|
969
|
+
var monitorStore = createStore((set, get) => ({
|
|
970
|
+
config: {},
|
|
971
|
+
buffer: [],
|
|
972
|
+
initialized: false,
|
|
973
|
+
push(event) {
|
|
974
|
+
const { config, buffer } = get();
|
|
975
|
+
const maxSize = config.maxBufferSize ?? 20;
|
|
976
|
+
const next = [...buffer, event];
|
|
977
|
+
set({ buffer: next });
|
|
978
|
+
if (next.length >= maxSize || event.level === "error") {
|
|
979
|
+
get().flush();
|
|
980
|
+
}
|
|
981
|
+
},
|
|
982
|
+
flush(useBeacon = false) {
|
|
983
|
+
const { buffer } = get();
|
|
984
|
+
if (buffer.length === 0) return;
|
|
985
|
+
const batch = buffer.slice(0, 50);
|
|
986
|
+
set({ buffer: buffer.slice(50) });
|
|
987
|
+
sendBatch({ events: batch }, useBeacon);
|
|
988
|
+
},
|
|
989
|
+
setConfig(config) {
|
|
990
|
+
set({ config, initialized: true });
|
|
991
|
+
}
|
|
992
|
+
}));
|
|
993
|
+
|
|
994
|
+
// src/client/capture/js-errors.ts
|
|
995
|
+
function installJsErrorCapture() {
|
|
996
|
+
if (typeof window === "undefined") return () => {
|
|
997
|
+
};
|
|
998
|
+
const onError = /* @__PURE__ */ __name(async (message, source, lineno, colno, error) => {
|
|
999
|
+
try {
|
|
1000
|
+
const msg = typeof message === "string" ? message : String(message);
|
|
1001
|
+
const stack = error?.stack ?? `at ${source}:${lineno}:${colno}`;
|
|
1002
|
+
const url = window.location.href;
|
|
1003
|
+
const fingerprint = await computeFingerprint(msg, stack, url);
|
|
1004
|
+
const { config } = monitorStore.getState();
|
|
1005
|
+
monitorStore.getState().push({
|
|
1006
|
+
event_type: "JS_ERROR" /* JS_ERROR */,
|
|
1007
|
+
level: "error" /* ERROR */,
|
|
1008
|
+
message: msg,
|
|
1009
|
+
stack_trace: stack,
|
|
1010
|
+
url,
|
|
1011
|
+
fingerprint,
|
|
1012
|
+
session_id: getSessionId(),
|
|
1013
|
+
user_agent: navigator.userAgent,
|
|
1014
|
+
project_name: config.project,
|
|
1015
|
+
environment: config.environment
|
|
1016
|
+
});
|
|
1017
|
+
} catch {
|
|
1018
|
+
}
|
|
1019
|
+
}, "onError");
|
|
1020
|
+
const onUnhandledRejection = /* @__PURE__ */ __name(async (e) => {
|
|
1021
|
+
try {
|
|
1022
|
+
const reason = e.reason;
|
|
1023
|
+
const msg = reason instanceof Error ? reason.message : typeof reason === "string" ? reason : "Unhandled promise rejection";
|
|
1024
|
+
const stack = reason instanceof Error ? reason.stack ?? "" : "";
|
|
1025
|
+
const url = window.location.href;
|
|
1026
|
+
const fingerprint = await computeFingerprint(msg, stack, url);
|
|
1027
|
+
const { config } = monitorStore.getState();
|
|
1028
|
+
monitorStore.getState().push({
|
|
1029
|
+
event_type: "JS_ERROR" /* JS_ERROR */,
|
|
1030
|
+
level: "error" /* ERROR */,
|
|
1031
|
+
message: msg,
|
|
1032
|
+
stack_trace: stack,
|
|
1033
|
+
url,
|
|
1034
|
+
fingerprint,
|
|
1035
|
+
session_id: getSessionId(),
|
|
1036
|
+
user_agent: navigator.userAgent,
|
|
1037
|
+
project_name: config.project,
|
|
1038
|
+
environment: config.environment
|
|
1039
|
+
});
|
|
1040
|
+
} catch {
|
|
1041
|
+
}
|
|
1042
|
+
}, "onUnhandledRejection");
|
|
1043
|
+
const errHandler = /* @__PURE__ */ __name((e) => onError(e.message, e.filename, e.lineno, e.colno, e.error), "errHandler");
|
|
1044
|
+
window.addEventListener("error", errHandler);
|
|
1045
|
+
window.addEventListener("unhandledrejection", onUnhandledRejection);
|
|
1046
|
+
return () => {
|
|
1047
|
+
window.removeEventListener("error", errHandler);
|
|
1048
|
+
window.removeEventListener("unhandledrejection", onUnhandledRejection);
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
__name(installJsErrorCapture, "installJsErrorCapture");
|
|
1052
|
+
|
|
1053
|
+
// src/client/capture/console.ts
|
|
1054
|
+
var levelMap = {
|
|
1055
|
+
warn: "warn" /* WARN */,
|
|
1056
|
+
error: "error" /* ERROR */
|
|
1057
|
+
};
|
|
1058
|
+
var typeMap = {
|
|
1059
|
+
warn: "WARNING" /* WARNING */,
|
|
1060
|
+
error: "ERROR" /* ERROR */
|
|
1061
|
+
};
|
|
1062
|
+
function stringify(args) {
|
|
1063
|
+
return args.map((a) => {
|
|
1064
|
+
if (typeof a === "string") return a;
|
|
1065
|
+
if (a instanceof Error) return a.message;
|
|
1066
|
+
try {
|
|
1067
|
+
return JSON.stringify(a);
|
|
1068
|
+
} catch {
|
|
1069
|
+
return String(a);
|
|
1070
|
+
}
|
|
1071
|
+
}).join(" ");
|
|
1072
|
+
}
|
|
1073
|
+
__name(stringify, "stringify");
|
|
1074
|
+
async function captureConsoleEvent(level, args) {
|
|
1075
|
+
try {
|
|
1076
|
+
const message = stringify(args);
|
|
1077
|
+
const url = typeof window !== "undefined" ? window.location.href : "";
|
|
1078
|
+
const fingerprint = await computeFingerprint(message, "", url);
|
|
1079
|
+
const { config } = monitorStore.getState();
|
|
1080
|
+
monitorStore.getState().push({
|
|
1081
|
+
event_type: typeMap[level],
|
|
1082
|
+
level: levelMap[level],
|
|
1083
|
+
message,
|
|
1084
|
+
url,
|
|
1085
|
+
fingerprint,
|
|
1086
|
+
session_id: getSessionId(),
|
|
1087
|
+
user_agent: typeof navigator !== "undefined" ? navigator.userAgent : "",
|
|
1088
|
+
project_name: config.project,
|
|
1089
|
+
environment: config.environment
|
|
1090
|
+
});
|
|
1091
|
+
} catch {
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
__name(captureConsoleEvent, "captureConsoleEvent");
|
|
1095
|
+
function installConsoleCapture() {
|
|
1096
|
+
if (typeof window === "undefined") return () => {
|
|
1097
|
+
};
|
|
1098
|
+
try {
|
|
1099
|
+
const g = globalThis;
|
|
1100
|
+
if (g.consola && typeof g.consola.addReporter === "function") {
|
|
1101
|
+
const consolaInst = g.consola;
|
|
1102
|
+
const reporter = {
|
|
1103
|
+
log(logObj) {
|
|
1104
|
+
if (logObj.level === 1) captureConsoleEvent("error", logObj.args);
|
|
1105
|
+
else if (logObj.level === 2) captureConsoleEvent("warn", logObj.args);
|
|
1106
|
+
}
|
|
1107
|
+
};
|
|
1108
|
+
consolaInst.addReporter(reporter);
|
|
1109
|
+
return () => consolaInst.removeReporter(reporter);
|
|
1110
|
+
}
|
|
1111
|
+
} catch {
|
|
1112
|
+
}
|
|
1113
|
+
const origWarn = console.warn.bind(console);
|
|
1114
|
+
const origError = console.error.bind(console);
|
|
1115
|
+
console.warn = (...args) => {
|
|
1116
|
+
origWarn(...args);
|
|
1117
|
+
captureConsoleEvent("warn", args);
|
|
1118
|
+
};
|
|
1119
|
+
console.error = (...args) => {
|
|
1120
|
+
origError(...args);
|
|
1121
|
+
captureConsoleEvent("error", args);
|
|
1122
|
+
};
|
|
1123
|
+
return () => {
|
|
1124
|
+
console.warn = origWarn;
|
|
1125
|
+
console.error = origError;
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
1128
|
+
__name(installConsoleCapture, "installConsoleCapture");
|
|
1129
|
+
|
|
1130
|
+
// src/client/capture/validation.ts
|
|
1131
|
+
function installValidationCapture() {
|
|
1132
|
+
if (typeof window === "undefined") return () => {
|
|
1133
|
+
};
|
|
1134
|
+
const handler = /* @__PURE__ */ __name((event) => {
|
|
1135
|
+
if (!(event instanceof CustomEvent)) return;
|
|
1136
|
+
try {
|
|
1137
|
+
const detail = event.detail;
|
|
1138
|
+
const { config } = monitorStore.getState();
|
|
1139
|
+
monitorStore.getState().push({
|
|
1140
|
+
event_type: "WARNING" /* WARNING */,
|
|
1141
|
+
level: "warn" /* WARN */,
|
|
1142
|
+
message: `Zod validation error in ${detail.operation}: ${detail.error?.message ?? "unknown"}`,
|
|
1143
|
+
url: window.location.href,
|
|
1144
|
+
http_method: detail.method,
|
|
1145
|
+
http_url: detail.path,
|
|
1146
|
+
session_id: getSessionId(),
|
|
1147
|
+
user_agent: navigator.userAgent,
|
|
1148
|
+
project_name: config.project,
|
|
1149
|
+
environment: config.environment,
|
|
1150
|
+
extra: { operation: detail.operation, path: detail.path, method: detail.method }
|
|
1151
|
+
});
|
|
1152
|
+
} catch {
|
|
1153
|
+
}
|
|
1154
|
+
}, "handler");
|
|
1155
|
+
window.addEventListener("zod-validation-error", handler);
|
|
1156
|
+
return () => window.removeEventListener("zod-validation-error", handler);
|
|
1157
|
+
}
|
|
1158
|
+
__name(installValidationCapture, "installValidationCapture");
|
|
1159
|
+
|
|
1160
|
+
// src/client/capture/network.ts
|
|
1161
|
+
async function monitoredFetch(input, init) {
|
|
1162
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
1163
|
+
const method = (init?.method ?? "GET").toUpperCase();
|
|
1164
|
+
try {
|
|
1165
|
+
const response = await fetch(input, init);
|
|
1166
|
+
if (!response.ok) {
|
|
1167
|
+
const { config } = monitorStore.getState();
|
|
1168
|
+
monitorStore.getState().push({
|
|
1169
|
+
event_type: "NETWORK_ERROR" /* NETWORK_ERROR */,
|
|
1170
|
+
level: response.status >= 500 ? "error" /* ERROR */ : "warn" /* WARN */,
|
|
1171
|
+
message: `HTTP ${response.status} ${response.statusText} \u2014 ${method} ${url}`,
|
|
1172
|
+
url: typeof window !== "undefined" ? window.location.href : "",
|
|
1173
|
+
http_status: response.status,
|
|
1174
|
+
http_method: method,
|
|
1175
|
+
http_url: url,
|
|
1176
|
+
session_id: getSessionId(),
|
|
1177
|
+
user_agent: typeof navigator !== "undefined" ? navigator.userAgent : "",
|
|
1178
|
+
project_name: config.project,
|
|
1179
|
+
environment: config.environment
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
return response;
|
|
1183
|
+
} catch (err) {
|
|
1184
|
+
const { config } = monitorStore.getState();
|
|
1185
|
+
monitorStore.getState().push({
|
|
1186
|
+
event_type: "NETWORK_ERROR" /* NETWORK_ERROR */,
|
|
1187
|
+
level: "error" /* ERROR */,
|
|
1188
|
+
message: err instanceof Error ? err.message : `Network error \u2014 ${method} ${url}`,
|
|
1189
|
+
url: typeof window !== "undefined" ? window.location.href : "",
|
|
1190
|
+
http_method: method,
|
|
1191
|
+
http_url: url,
|
|
1192
|
+
session_id: getSessionId(),
|
|
1193
|
+
user_agent: typeof navigator !== "undefined" ? navigator.userAgent : "",
|
|
1194
|
+
project_name: config.project,
|
|
1195
|
+
environment: config.environment
|
|
1196
|
+
});
|
|
1197
|
+
throw err;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
__name(monitoredFetch, "monitoredFetch");
|
|
1201
|
+
|
|
1202
|
+
// src/client/index.ts
|
|
1203
|
+
var flushInterval = null;
|
|
1204
|
+
var cleanupFns = [];
|
|
1205
|
+
var FrontendMonitor = {
|
|
1206
|
+
init(config = {}) {
|
|
1207
|
+
if (typeof window === "undefined") return;
|
|
1208
|
+
if (config.baseUrl) configureMonitorApi(config.baseUrl);
|
|
1209
|
+
monitorStore.getState().setConfig(config);
|
|
1210
|
+
ensureSessionCookie();
|
|
1211
|
+
if (config.captureJsErrors !== false) cleanupFns.push(installJsErrorCapture());
|
|
1212
|
+
if (config.captureConsole !== false) cleanupFns.push(installConsoleCapture());
|
|
1213
|
+
cleanupFns.push(installValidationCapture());
|
|
1214
|
+
const interval = config.flushInterval ?? 5e3;
|
|
1215
|
+
flushInterval = setInterval(() => monitorStore.getState().flush(), interval);
|
|
1216
|
+
const onHide = /* @__PURE__ */ __name(() => {
|
|
1217
|
+
if (document.visibilityState === "hidden") monitorStore.getState().flush(true);
|
|
1218
|
+
}, "onHide");
|
|
1219
|
+
window.addEventListener("visibilitychange", onHide);
|
|
1220
|
+
window.addEventListener("beforeunload", () => monitorStore.getState().flush(true));
|
|
1221
|
+
if (config.debug) console.info("[FrontendMonitor] initialized", config);
|
|
1222
|
+
},
|
|
1223
|
+
capture(event) {
|
|
1224
|
+
monitorStore.getState().push(event);
|
|
1225
|
+
},
|
|
1226
|
+
flush() {
|
|
1227
|
+
monitorStore.getState().flush();
|
|
1228
|
+
},
|
|
1229
|
+
destroy() {
|
|
1230
|
+
if (flushInterval !== null) {
|
|
1231
|
+
clearInterval(flushInterval);
|
|
1232
|
+
flushInterval = null;
|
|
1233
|
+
}
|
|
1234
|
+
cleanupFns.forEach((fn) => fn());
|
|
1235
|
+
cleanupFns.length = 0;
|
|
1236
|
+
}
|
|
1237
|
+
};
|
|
1238
|
+
export {
|
|
1239
|
+
FrontendMonitor,
|
|
1240
|
+
getSessionId,
|
|
1241
|
+
monitoredFetch
|
|
1242
|
+
};
|
|
1243
|
+
//# sourceMappingURL=client.mjs.map
|