@devkitio/faultlens 0.1.5 → 1.0.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/dist/chunk-6LF3JLVG.js +293 -0
- package/dist/chunk-ZUVQIOFO.js +1282 -0
- package/dist/cli/index.d.ts +14 -0
- package/dist/cli/index.js +50 -26
- package/dist/express/index.d.ts +1 -1
- package/dist/fastify/index.d.ts +1 -1
- package/dist/index.d.ts +30 -3
- package/dist/index.js +2 -2
- package/dist/node/index.d.ts +61 -2
- package/dist/node/index.js +437 -21
- package/dist/nuxt/runtime/nitro-plugin.js +1 -1
- package/dist/nuxt/runtime/plugin.js +2 -2
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +2 -2
- package/dist/testing/index.d.ts +12 -4
- package/dist/testing/index.js +43 -6
- package/dist/types-hWS-GSV1.d.ts +302 -0
- package/dist/vue/index.d.ts +1 -1
- package/dist/web-vitals/index.d.ts +28 -0
- package/dist/web-vitals/index.js +58 -0
- package/package.json +18 -10
- package/dist/chunk-K63I3OJT.js +0 -154
- package/dist/chunk-LRHXSFK2.js +0 -535
- package/dist/types-DxONWOH8.d.ts +0 -136
package/dist/node/index.js
CHANGED
|
@@ -1,11 +1,328 @@
|
|
|
1
|
-
import { createMonitorRuntime } from '../chunk-
|
|
1
|
+
import { parseTransportResponse, parseRemoteSdkConfig, parseTelemetryTransportResponse, createMonitorRuntime } from '../chunk-ZUVQIOFO.js';
|
|
2
2
|
import { createSignedRelayHeaders } from '../chunk-RXQF7F2E.js';
|
|
3
|
+
import { AsyncLocalStorage } from 'async_hooks';
|
|
4
|
+
import { createDecipheriv, randomBytes, createCipheriv } from 'crypto';
|
|
5
|
+
import { unlink, mkdir, lstat, chmod, open, stat, readFile, rename } from 'fs/promises';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { createRequire } from 'module';
|
|
3
8
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
var STATE_FILE = "events.v2.enc";
|
|
10
|
+
var LOCK_FILE = "events.v2.lock";
|
|
11
|
+
var AAD_PREFIX = "faultlens-node-queue:v2";
|
|
12
|
+
function sleep(timeoutMs) {
|
|
13
|
+
return new Promise((resolve) => setTimeout(resolve, timeoutMs));
|
|
14
|
+
}
|
|
15
|
+
function errorCode(error) {
|
|
16
|
+
return typeof error === "object" && error !== null && "code" in error ? String(error.code) : void 0;
|
|
17
|
+
}
|
|
18
|
+
function isQueueItem(value) {
|
|
19
|
+
if (typeof value !== "object" || value === null) return false;
|
|
20
|
+
const item = value;
|
|
21
|
+
return typeof item.id === "string" && typeof item.event === "object" && item.event !== null && item.event.eventId === item.id && typeof item.enqueuedAt === "number" && typeof item.bytes === "number" && typeof item.protocolVersion === "string" && typeof item.attemptCount === "number" && typeof item.nextAttemptAt === "number" && (item.lastErrorCode === void 0 || typeof item.lastErrorCode === "string");
|
|
22
|
+
}
|
|
23
|
+
var NodeEncryptedFileQueue = class {
|
|
24
|
+
directory;
|
|
25
|
+
keys;
|
|
26
|
+
maxItems;
|
|
27
|
+
maxBytes;
|
|
28
|
+
maxAgeMs;
|
|
29
|
+
lockTimeoutMs;
|
|
30
|
+
staleLockMs;
|
|
31
|
+
serial = Promise.resolve();
|
|
32
|
+
constructor(options) {
|
|
33
|
+
if (!path.isAbsolute(options.directory)) {
|
|
34
|
+
throw new Error("Node \u4E8B\u4EF6\u961F\u5217\u76EE\u5F55\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84");
|
|
35
|
+
}
|
|
36
|
+
if (options.encryptionKeys.length === 0) {
|
|
37
|
+
throw new Error("Node \u4E8B\u4EF6\u961F\u5217\u81F3\u5C11\u9700\u8981\u4E00\u4E2A\u52A0\u5BC6\u5BC6\u94A5");
|
|
38
|
+
}
|
|
39
|
+
const ids = /* @__PURE__ */ new Set();
|
|
40
|
+
this.keys = options.encryptionKeys.map((current) => {
|
|
41
|
+
if (!/^[A-Za-z0-9._-]{1,64}$/.test(current.id) || ids.has(current.id)) {
|
|
42
|
+
throw new Error("Node \u4E8B\u4EF6\u961F\u5217\u5BC6\u94A5 ID \u65E0\u6548\u6216\u91CD\u590D");
|
|
43
|
+
}
|
|
44
|
+
ids.add(current.id);
|
|
45
|
+
const key = Buffer.from(current.key);
|
|
46
|
+
if (key.length !== 32) throw new Error("Node \u4E8B\u4EF6\u961F\u5217\u5BC6\u94A5\u5FC5\u987B\u4E3A 32 \u5B57\u8282");
|
|
47
|
+
return { id: current.id, key };
|
|
48
|
+
});
|
|
49
|
+
this.directory = path.resolve(options.directory);
|
|
50
|
+
this.maxItems = Math.min(1e4, Math.max(1, options.maxItems ?? 1e3));
|
|
51
|
+
this.maxBytes = Math.min(512 * 1024 * 1024, Math.max(1024, options.maxBytes ?? 50 * 1024 * 1024));
|
|
52
|
+
this.maxAgeMs = Math.min(30 * 24 * 60 * 60 * 1e3, Math.max(6e4, options.maxAgeMs ?? 24 * 60 * 60 * 1e3));
|
|
53
|
+
this.lockTimeoutMs = Math.min(3e4, Math.max(100, options.lockTimeoutMs ?? 2e3));
|
|
54
|
+
this.staleLockMs = Math.min(12e4, Math.max(1e3, options.staleLockMs ?? 3e4));
|
|
55
|
+
}
|
|
56
|
+
load(now) {
|
|
57
|
+
return this.runExclusive(
|
|
58
|
+
async () => this.withLock(async () => {
|
|
59
|
+
const items = await this.readItems();
|
|
60
|
+
const retained = items.filter((item) => now - item.enqueuedAt <= this.maxAgeMs);
|
|
61
|
+
if (retained.length !== items.length) await this.writeItems(retained);
|
|
62
|
+
return structuredClone(retained);
|
|
63
|
+
})
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
put(item) {
|
|
67
|
+
return this.runExclusive(
|
|
68
|
+
async () => this.withLock(async () => {
|
|
69
|
+
const items = (await this.readItems()).filter((current) => current.id !== item.id);
|
|
70
|
+
items.push(structuredClone(item));
|
|
71
|
+
items.sort((first, second) => first.enqueuedAt - second.enqueuedAt);
|
|
72
|
+
let bytes = items.reduce((total, current) => total + Math.max(0, current.bytes), 0);
|
|
73
|
+
while (items.length > this.maxItems || bytes > this.maxBytes) {
|
|
74
|
+
const removed = items.shift();
|
|
75
|
+
if (!removed) break;
|
|
76
|
+
bytes -= Math.max(0, removed.bytes);
|
|
77
|
+
}
|
|
78
|
+
await this.writeItems(items);
|
|
79
|
+
})
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
remove(ids) {
|
|
83
|
+
if (ids.length === 0) return Promise.resolve();
|
|
84
|
+
const removedIds = new Set(ids);
|
|
85
|
+
return this.runExclusive(
|
|
86
|
+
async () => this.withLock(async () => {
|
|
87
|
+
const items = (await this.readItems()).filter((item) => !removedIds.has(item.id));
|
|
88
|
+
await this.writeItems(items);
|
|
89
|
+
})
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
clear() {
|
|
93
|
+
return this.runExclusive(
|
|
94
|
+
async () => this.withLock(async () => {
|
|
95
|
+
await unlink(this.statePath()).catch((error) => {
|
|
96
|
+
if (errorCode(error) !== "ENOENT") throw error;
|
|
97
|
+
});
|
|
98
|
+
})
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
runExclusive(operation) {
|
|
102
|
+
const result = this.serial.then(operation, operation);
|
|
103
|
+
this.serial = result.then(
|
|
104
|
+
() => void 0,
|
|
105
|
+
() => void 0
|
|
106
|
+
);
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
async ensureDirectory() {
|
|
110
|
+
await mkdir(this.directory, { recursive: true, mode: 448 });
|
|
111
|
+
const metadata = await lstat(this.directory);
|
|
112
|
+
if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
|
|
113
|
+
throw new Error("Node \u4E8B\u4EF6\u961F\u5217\u76EE\u5F55\u4E0D\u5B89\u5168");
|
|
114
|
+
}
|
|
115
|
+
await chmod(this.directory, 448);
|
|
116
|
+
}
|
|
117
|
+
async withLock(operation) {
|
|
118
|
+
await this.ensureDirectory();
|
|
119
|
+
const lockPath = path.join(this.directory, LOCK_FILE);
|
|
120
|
+
const startedAt = Date.now();
|
|
121
|
+
let handle;
|
|
122
|
+
while (!handle) {
|
|
123
|
+
try {
|
|
124
|
+
handle = await open(lockPath, "wx", 384);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
if (errorCode(error) !== "EEXIST") throw error;
|
|
127
|
+
const lockStat = await stat(lockPath).catch(() => void 0);
|
|
128
|
+
if (lockStat && Date.now() - lockStat.mtimeMs > this.staleLockMs) {
|
|
129
|
+
await unlink(lockPath).catch(() => void 0);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (Date.now() - startedAt >= this.lockTimeoutMs) {
|
|
133
|
+
throw new Error("Node \u4E8B\u4EF6\u961F\u5217\u9501\u7B49\u5F85\u8D85\u65F6");
|
|
134
|
+
}
|
|
135
|
+
await sleep(20);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
return await operation();
|
|
140
|
+
} finally {
|
|
141
|
+
await handle.close().catch(() => void 0);
|
|
142
|
+
await unlink(lockPath).catch(() => void 0);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
statePath() {
|
|
146
|
+
return path.join(this.directory, STATE_FILE);
|
|
147
|
+
}
|
|
148
|
+
async readItems() {
|
|
149
|
+
const statePath = this.statePath();
|
|
150
|
+
const metadata = await lstat(statePath).catch((error) => {
|
|
151
|
+
if (errorCode(error) === "ENOENT") return void 0;
|
|
152
|
+
throw error;
|
|
153
|
+
});
|
|
154
|
+
if (!metadata) return [];
|
|
155
|
+
if (!metadata.isFile() || metadata.isSymbolicLink()) {
|
|
156
|
+
throw new Error("Node \u4E8B\u4EF6\u961F\u5217\u72B6\u6001\u6587\u4EF6\u4E0D\u5B89\u5168");
|
|
157
|
+
}
|
|
158
|
+
await chmod(statePath, 384);
|
|
159
|
+
const envelope = JSON.parse(await readFile(statePath, "utf8"));
|
|
160
|
+
if (envelope.formatVersion !== 2 || typeof envelope.keyId !== "string" || typeof envelope.iv !== "string" || typeof envelope.ciphertext !== "string" || typeof envelope.authTag !== "string") {
|
|
161
|
+
throw new Error("Node \u4E8B\u4EF6\u961F\u5217\u6587\u4EF6\u683C\u5F0F\u65E0\u6548");
|
|
162
|
+
}
|
|
163
|
+
const key = this.keys.find((current) => current.id === envelope.keyId);
|
|
164
|
+
if (!key) throw new Error("Node \u4E8B\u4EF6\u961F\u5217\u7F3A\u5C11\u89E3\u5BC6\u5BC6\u94A5");
|
|
165
|
+
const decipher = createDecipheriv("aes-256-gcm", key.key, Buffer.from(envelope.iv, "base64url"));
|
|
166
|
+
decipher.setAAD(Buffer.from(`${AAD_PREFIX}:${envelope.keyId}`));
|
|
167
|
+
decipher.setAuthTag(Buffer.from(envelope.authTag, "base64url"));
|
|
168
|
+
const plaintext = Buffer.concat([
|
|
169
|
+
decipher.update(Buffer.from(envelope.ciphertext, "base64url")),
|
|
170
|
+
decipher.final()
|
|
171
|
+
]);
|
|
172
|
+
const parsed = JSON.parse(plaintext.toString("utf8"));
|
|
173
|
+
if (!Array.isArray(parsed.items) || !parsed.items.every(isQueueItem)) {
|
|
174
|
+
throw new Error("Node \u4E8B\u4EF6\u961F\u5217\u5185\u5BB9\u65E0\u6548");
|
|
175
|
+
}
|
|
176
|
+
return parsed.items.sort((first, second) => first.enqueuedAt - second.enqueuedAt);
|
|
177
|
+
}
|
|
178
|
+
async writeItems(items) {
|
|
179
|
+
const statePath = this.statePath();
|
|
180
|
+
if (items.length === 0) {
|
|
181
|
+
await unlink(statePath).catch((error) => {
|
|
182
|
+
if (errorCode(error) !== "ENOENT") throw error;
|
|
183
|
+
});
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const currentKey = this.keys[0];
|
|
187
|
+
const iv = randomBytes(12);
|
|
188
|
+
const cipher = createCipheriv("aes-256-gcm", currentKey.key, iv);
|
|
189
|
+
cipher.setAAD(Buffer.from(`${AAD_PREFIX}:${currentKey.id}`));
|
|
190
|
+
const ciphertext = Buffer.concat([
|
|
191
|
+
cipher.update(JSON.stringify({ items })),
|
|
192
|
+
cipher.final()
|
|
193
|
+
]);
|
|
194
|
+
const envelope = {
|
|
195
|
+
formatVersion: 2,
|
|
196
|
+
keyId: currentKey.id,
|
|
197
|
+
iv: iv.toString("base64url"),
|
|
198
|
+
ciphertext: ciphertext.toString("base64url"),
|
|
199
|
+
authTag: cipher.getAuthTag().toString("base64url")
|
|
200
|
+
};
|
|
201
|
+
const temporaryPath = path.join(
|
|
202
|
+
this.directory,
|
|
203
|
+
`${STATE_FILE}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`
|
|
204
|
+
);
|
|
205
|
+
const handle = await open(temporaryPath, "wx", 384);
|
|
206
|
+
try {
|
|
207
|
+
await handle.writeFile(JSON.stringify(envelope), "utf8");
|
|
208
|
+
await handle.sync();
|
|
209
|
+
await handle.close();
|
|
210
|
+
await rename(temporaryPath, statePath);
|
|
211
|
+
await chmod(statePath, 384);
|
|
212
|
+
} catch (error) {
|
|
213
|
+
await handle.close().catch(() => void 0);
|
|
214
|
+
await unlink(temporaryPath).catch(() => void 0);
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
function builtInModules() {
|
|
220
|
+
const require2 = createRequire(import.meta.url);
|
|
221
|
+
return [
|
|
222
|
+
require2("node:http"),
|
|
223
|
+
require2("node:https")
|
|
224
|
+
];
|
|
225
|
+
}
|
|
226
|
+
function isRecord(value) {
|
|
227
|
+
return typeof value === "object" && value !== null && !(value instanceof URL);
|
|
228
|
+
}
|
|
229
|
+
function requestOptions(args) {
|
|
230
|
+
if (isRecord(args[0])) return args[0];
|
|
231
|
+
return isRecord(args[1]) ? args[1] : {};
|
|
8
232
|
}
|
|
233
|
+
function requestUrl(args, options) {
|
|
234
|
+
const first = args[0];
|
|
235
|
+
if (first instanceof URL) return first;
|
|
236
|
+
if (typeof first === "string") {
|
|
237
|
+
try {
|
|
238
|
+
return new URL(first);
|
|
239
|
+
} catch {
|
|
240
|
+
return void 0;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
const protocol = typeof options.protocol === "string" ? options.protocol : "http:";
|
|
244
|
+
const hostname = typeof options.hostname === "string" ? options.hostname : typeof options.host === "string" ? options.host : void 0;
|
|
245
|
+
if (!hostname) return void 0;
|
|
246
|
+
const path2 = typeof options.path === "string" ? options.path : "/";
|
|
247
|
+
try {
|
|
248
|
+
return new URL(path2, `${protocol}//${hostname}`);
|
|
249
|
+
} catch {
|
|
250
|
+
return void 0;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function normalizedHeaders(value) {
|
|
254
|
+
if (value instanceof Headers) return Object.fromEntries(value.entries());
|
|
255
|
+
return isRecord(value) ? { ...value } : {};
|
|
256
|
+
}
|
|
257
|
+
function instrumentedArguments(args, headers) {
|
|
258
|
+
const next = [...args];
|
|
259
|
+
if (isRecord(next[0])) {
|
|
260
|
+
next[0] = { ...next[0], headers };
|
|
261
|
+
return next;
|
|
262
|
+
}
|
|
263
|
+
if (isRecord(next[1])) next[1] = { ...next[1], headers };
|
|
264
|
+
else next.splice(1, 0, { headers });
|
|
265
|
+
return next;
|
|
266
|
+
}
|
|
267
|
+
function installNodeHttpInstrumentation(monitor, options = {}) {
|
|
268
|
+
const restorers = [];
|
|
269
|
+
for (const module of options.modules ?? builtInModules()) {
|
|
270
|
+
for (const key of ["request", "get"]) {
|
|
271
|
+
const original = module[key];
|
|
272
|
+
if (typeof original !== "function") continue;
|
|
273
|
+
const instrumented = function instrumentedRequest(...args) {
|
|
274
|
+
const currentOptions = requestOptions(args);
|
|
275
|
+
const method = typeof currentOptions.method === "string" ? currentOptions.method.trim().toUpperCase().slice(0, 32) || "GET" : "GET";
|
|
276
|
+
const url = requestUrl(args, currentOptions);
|
|
277
|
+
if (options.shouldTrace && !options.shouldTrace(method, url)) {
|
|
278
|
+
return original.apply(module, args);
|
|
279
|
+
}
|
|
280
|
+
const span = monitor.startSpan(`HTTP ${method}`, {
|
|
281
|
+
attributes: {
|
|
282
|
+
"http.method": method,
|
|
283
|
+
...url ? { "url.path": url.pathname.slice(0, 2048) || "/" } : {}
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
const headers = normalizedHeaders(currentOptions.headers);
|
|
287
|
+
if (options.shouldPropagateTraceContext?.(url) === true) {
|
|
288
|
+
Object.assign(headers, span.toTraceHeaders());
|
|
289
|
+
}
|
|
290
|
+
let request;
|
|
291
|
+
try {
|
|
292
|
+
request = original.apply(module, instrumentedArguments(args, headers));
|
|
293
|
+
} catch (error) {
|
|
294
|
+
span.end("error");
|
|
295
|
+
throw error;
|
|
296
|
+
}
|
|
297
|
+
let finished = false;
|
|
298
|
+
const finish = (status, response) => {
|
|
299
|
+
if (finished) return;
|
|
300
|
+
finished = true;
|
|
301
|
+
if (response?.statusCode) span.setAttribute("http.status_code", response.statusCode);
|
|
302
|
+
span.end(status);
|
|
303
|
+
};
|
|
304
|
+
request.once("response", (response) => {
|
|
305
|
+
const status = response.statusCode ?? 0;
|
|
306
|
+
response.once("end", () => finish(status >= 400 ? "error" : "ok", response));
|
|
307
|
+
response.once("aborted", () => finish("error", response));
|
|
308
|
+
response.once("error", () => finish("error", response));
|
|
309
|
+
});
|
|
310
|
+
request.once("error", () => finish("error"));
|
|
311
|
+
request.once("abort", () => finish("error"));
|
|
312
|
+
return request;
|
|
313
|
+
};
|
|
314
|
+
module[key] = instrumented;
|
|
315
|
+
restorers.push(() => {
|
|
316
|
+
if (module[key] === instrumented) module[key] = original;
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return () => {
|
|
321
|
+
for (const restore of restorers.reverse()) restore();
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// src/node/index.ts
|
|
9
326
|
function delay(timeoutMs) {
|
|
10
327
|
return new Promise((resolve) => setTimeout(resolve, timeoutMs));
|
|
11
328
|
}
|
|
@@ -55,31 +372,124 @@ var NodeRelayTransport = class {
|
|
|
55
372
|
redirect: "error",
|
|
56
373
|
signal: controller.signal
|
|
57
374
|
});
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
375
|
+
const outcome = await parseTransportResponse(response, envelope);
|
|
376
|
+
if (outcome.kind === "batch" || outcome.kind === "permanent_error") return outcome;
|
|
377
|
+
const waitMs = outcome.kind === "retryable" && outcome.retryAfterMs > 0 ? outcome.retryAfterMs : Math.min(3e4, this.retryBaseMs * 2 ** attempt);
|
|
378
|
+
if (attempt + 1 >= this.maxAttempts) {
|
|
379
|
+
return {
|
|
380
|
+
kind: "retryable",
|
|
381
|
+
retryAfterMs: waitMs,
|
|
382
|
+
code: outcome.code
|
|
383
|
+
};
|
|
67
384
|
}
|
|
68
|
-
const waitMs = retryAfter(response) ?? Math.min(3e4, this.retryBaseMs * 2 ** attempt);
|
|
69
|
-
if (attempt + 1 >= this.maxAttempts) return { kind: "retryable", retryAfterMs: waitMs };
|
|
70
385
|
await delay(waitMs);
|
|
71
386
|
} catch {
|
|
72
|
-
if (attempt + 1 >= this.maxAttempts)
|
|
387
|
+
if (attempt + 1 >= this.maxAttempts) {
|
|
388
|
+
return { kind: "network_error", code: "network_error" };
|
|
389
|
+
}
|
|
73
390
|
await delay(Math.min(3e4, this.retryBaseMs * 2 ** attempt));
|
|
74
391
|
} finally {
|
|
75
392
|
clearTimeout(timer);
|
|
76
393
|
}
|
|
77
394
|
}
|
|
78
|
-
return { kind: "network_error" };
|
|
395
|
+
return { kind: "network_error", code: "network_error" };
|
|
396
|
+
}
|
|
397
|
+
async loadRemoteConfig() {
|
|
398
|
+
const result = await this.loadRemoteConfigResult();
|
|
399
|
+
return result.kind === "configured" ? result.config : void 0;
|
|
400
|
+
}
|
|
401
|
+
async loadRemoteConfigResult() {
|
|
402
|
+
const endpoint = new URL(this.endpoint);
|
|
403
|
+
endpoint.pathname = "/v1/relay/config";
|
|
404
|
+
const controller = new AbortController();
|
|
405
|
+
const timer = setTimeout(() => controller.abort(), this.requestTimeoutMs);
|
|
406
|
+
try {
|
|
407
|
+
const response = await this.fetcher(endpoint, {
|
|
408
|
+
method: "GET",
|
|
409
|
+
headers: {
|
|
410
|
+
"x-faultlens-key-id": this.ingest.keyId,
|
|
411
|
+
"x-faultlens-key-version": this.ingest.keyVersion
|
|
412
|
+
},
|
|
413
|
+
cache: "no-store",
|
|
414
|
+
redirect: "error",
|
|
415
|
+
signal: controller.signal
|
|
416
|
+
});
|
|
417
|
+
if (response.status === 404) return { kind: "unsupported", code: "config_not_found" };
|
|
418
|
+
if (response.status === 422) return { kind: "unsupported", code: "unsupported_protocol" };
|
|
419
|
+
if (!response.ok) return { kind: "retryable_error", code: `http_${response.status}` };
|
|
420
|
+
let body;
|
|
421
|
+
try {
|
|
422
|
+
body = await response.json();
|
|
423
|
+
} catch {
|
|
424
|
+
return { kind: "retryable_error", code: "invalid_remote_config" };
|
|
425
|
+
}
|
|
426
|
+
const config = parseRemoteSdkConfig(body);
|
|
427
|
+
return config ? { kind: "configured", config } : { kind: "retryable_error", code: "invalid_remote_config" };
|
|
428
|
+
} catch {
|
|
429
|
+
return { kind: "retryable_error", code: "network_error" };
|
|
430
|
+
} finally {
|
|
431
|
+
clearTimeout(timer);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
var NodeRelayTelemetryTransport = class {
|
|
436
|
+
constructor(ingest, options = {}) {
|
|
437
|
+
this.ingest = ingest;
|
|
438
|
+
const endpoint = new URL(ingest.endpoint);
|
|
439
|
+
const local = endpoint.hostname === "localhost" || endpoint.hostname === "127.0.0.1";
|
|
440
|
+
if (endpoint.protocol !== "https:" && !(endpoint.protocol === "http:" && local)) {
|
|
441
|
+
throw new Error("Node Relay Endpoint \u5FC5\u987B\u4F7F\u7528 HTTPS");
|
|
442
|
+
}
|
|
443
|
+
if (endpoint.pathname !== "/v1/relay/envelope" || endpoint.username || endpoint.password || endpoint.search || endpoint.hash) {
|
|
444
|
+
throw new Error("Node Relay Endpoint \u683C\u5F0F\u65E0\u6548");
|
|
445
|
+
}
|
|
446
|
+
if (!ingest.keyId || !ingest.keyVersion || !ingest.secret) {
|
|
447
|
+
throw new Error("Node Relay \u51ED\u636E\u4E0D\u5B8C\u6574");
|
|
448
|
+
}
|
|
449
|
+
endpoint.pathname = "/v1/relay/telemetry";
|
|
450
|
+
this.endpoint = endpoint.toString();
|
|
451
|
+
this.fetcher = options.fetcher ?? fetch;
|
|
452
|
+
this.requestTimeoutMs = Math.min(12e4, Math.max(100, options.requestTimeoutMs ?? 5e3));
|
|
453
|
+
}
|
|
454
|
+
fetcher;
|
|
455
|
+
requestTimeoutMs;
|
|
456
|
+
endpoint;
|
|
457
|
+
async send(envelope) {
|
|
458
|
+
const body = JSON.stringify(envelope);
|
|
459
|
+
const telemetryId = envelope.traces[0]?.telemetryId ?? envelope.spans[0]?.telemetryId ?? envelope.webVitals[0]?.telemetryId;
|
|
460
|
+
if (!telemetryId) return { kind: "permanent_error", code: "empty_telemetry_envelope" };
|
|
461
|
+
const controller = new AbortController();
|
|
462
|
+
const timer = setTimeout(() => controller.abort(), this.requestTimeoutMs);
|
|
463
|
+
try {
|
|
464
|
+
const response = await this.fetcher(this.endpoint, {
|
|
465
|
+
method: "POST",
|
|
466
|
+
headers: createSignedRelayHeaders({
|
|
467
|
+
upstream: this.endpoint,
|
|
468
|
+
relayKeyId: this.ingest.keyId,
|
|
469
|
+
relayKeyVersion: this.ingest.keyVersion,
|
|
470
|
+
relaySecret: this.ingest.secret
|
|
471
|
+
}, body, telemetryId),
|
|
472
|
+
body,
|
|
473
|
+
cache: "no-store",
|
|
474
|
+
redirect: "error",
|
|
475
|
+
signal: controller.signal
|
|
476
|
+
});
|
|
477
|
+
return await parseTelemetryTransportResponse(response, envelope);
|
|
478
|
+
} catch {
|
|
479
|
+
return { kind: "network_error", code: "network_error" };
|
|
480
|
+
} finally {
|
|
481
|
+
clearTimeout(timer);
|
|
482
|
+
}
|
|
79
483
|
}
|
|
80
484
|
};
|
|
81
485
|
function createNodeErrorMonitor(options, overrides = {}) {
|
|
82
|
-
const {
|
|
486
|
+
const {
|
|
487
|
+
captureGlobalErrors = true,
|
|
488
|
+
ingest,
|
|
489
|
+
persistentQueue: persistentQueueOptions,
|
|
490
|
+
...monitorOptions
|
|
491
|
+
} = options;
|
|
492
|
+
const spanStorage = new AsyncLocalStorage();
|
|
83
493
|
const monitor = createMonitorRuntime(
|
|
84
494
|
{
|
|
85
495
|
...monitorOptions,
|
|
@@ -87,7 +497,13 @@ function createNodeErrorMonitor(options, overrides = {}) {
|
|
|
87
497
|
},
|
|
88
498
|
{
|
|
89
499
|
...overrides,
|
|
90
|
-
|
|
500
|
+
...overrides.persistentQueue ? { persistentQueue: overrides.persistentQueue } : persistentQueueOptions ? { persistentQueue: new NodeEncryptedFileQueue(persistentQueueOptions) } : {},
|
|
501
|
+
transport: overrides.transport ?? new NodeRelayTransport(ingest),
|
|
502
|
+
telemetryTransport: overrides.telemetryTransport ?? new NodeRelayTelemetryTransport(ingest),
|
|
503
|
+
spanContextManager: overrides.spanContextManager ?? {
|
|
504
|
+
active: () => spanStorage.getStore(),
|
|
505
|
+
run: (span, callback) => spanStorage.run(span, callback)
|
|
506
|
+
}
|
|
91
507
|
}
|
|
92
508
|
);
|
|
93
509
|
if (!captureGlobalErrors) return monitor;
|
|
@@ -114,4 +530,4 @@ function createNodeErrorMonitor(options, overrides = {}) {
|
|
|
114
530
|
return monitor;
|
|
115
531
|
}
|
|
116
532
|
|
|
117
|
-
export { NodeRelayTransport, createNodeErrorMonitor };
|
|
533
|
+
export { NodeEncryptedFileQueue, NodeRelayTelemetryTransport, NodeRelayTransport, createNodeErrorMonitor, installNodeHttpInstrumentation };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { clientRedact, SDK_VERSION, parseErrorStack } from '../../chunk-
|
|
1
|
+
import { clientRedact, SDK_VERSION, parseErrorStack } from '../../chunk-ZUVQIOFO.js';
|
|
2
2
|
import { createSignedRelayHeaders } from '../../chunk-RXQF7F2E.js';
|
|
3
3
|
import { randomUUID } from 'crypto';
|
|
4
4
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { createErrorMonitor } from '../../chunk-
|
|
1
|
+
import { createErrorMonitor } from '../../chunk-6LF3JLVG.js';
|
|
2
2
|
import { installVueErrorMonitor } from '../../chunk-P6Q63P6L.js';
|
|
3
|
-
import '../../chunk-
|
|
3
|
+
import '../../chunk-ZUVQIOFO.js';
|
|
4
4
|
import { defineNuxtPlugin, useRuntimeConfig } from '#app';
|
|
5
5
|
|
|
6
6
|
var plugin_default = defineNuxtPlugin({
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Component, ReactNode, ErrorInfo } from 'react';
|
|
2
|
-
import { E as ErrorMonitor, M as MonitorOptions } from '../types-
|
|
2
|
+
import { E as ErrorMonitor, M as MonitorOptions } from '../types-hWS-GSV1.js';
|
|
3
3
|
|
|
4
4
|
interface ReactMonitorOptions extends Omit<MonitorOptions, "ingest"> {
|
|
5
5
|
ingest: {
|
package/dist/react/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createErrorMonitor } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
1
|
+
import { createErrorMonitor } from '../chunk-6LF3JLVG.js';
|
|
2
|
+
import '../chunk-ZUVQIOFO.js';
|
|
3
3
|
import { Component, createElement } from 'react';
|
|
4
4
|
|
|
5
5
|
function createReactErrorMonitor(options) {
|
package/dist/testing/index.d.ts
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { T as TelemetryTransport, a as TelemetryEnvelope, b as TelemetryTransportOutcome, j as EventTransport, k as EventEnvelope, l as TransportOutcome, R as RemoteSdkConfig, M as MonitorOptions, E as ErrorMonitor } from '../types-hWS-GSV1.js';
|
|
2
2
|
|
|
3
3
|
declare class TestTransport implements EventTransport {
|
|
4
|
+
private readonly remoteConfig?;
|
|
4
5
|
readonly envelopes: EventEnvelope[];
|
|
5
6
|
private readonly outcomes;
|
|
6
|
-
constructor(outcomes?: TransportOutcome[]);
|
|
7
|
+
constructor(outcomes?: TransportOutcome[], remoteConfig?: RemoteSdkConfig | undefined);
|
|
7
8
|
send(envelope: EventEnvelope): Promise<TransportOutcome>;
|
|
9
|
+
loadRemoteConfig(): Promise<RemoteSdkConfig | undefined>;
|
|
8
10
|
}
|
|
9
|
-
declare
|
|
11
|
+
declare class TestTelemetryTransport implements TelemetryTransport {
|
|
12
|
+
readonly envelopes: TelemetryEnvelope[];
|
|
13
|
+
private readonly outcomes;
|
|
14
|
+
constructor(outcomes?: TelemetryTransportOutcome[]);
|
|
15
|
+
send(envelope: TelemetryEnvelope): Promise<TelemetryTransportOutcome>;
|
|
16
|
+
}
|
|
17
|
+
declare function createTestMonitor(options: MonitorOptions, transport: EventTransport, telemetryTransport?: TelemetryTransport): ErrorMonitor;
|
|
10
18
|
|
|
11
|
-
export { TestTransport, createTestMonitor };
|
|
19
|
+
export { TestTelemetryTransport, TestTransport, createTestMonitor };
|
package/dist/testing/index.js
CHANGED
|
@@ -1,24 +1,61 @@
|
|
|
1
|
-
import { createMonitorRuntime } from '../chunk-
|
|
1
|
+
import { createMonitorRuntime } from '../chunk-ZUVQIOFO.js';
|
|
2
2
|
|
|
3
3
|
// src/testing/index.ts
|
|
4
4
|
var TestTransport = class {
|
|
5
|
+
constructor(outcomes = [], remoteConfig) {
|
|
6
|
+
this.remoteConfig = remoteConfig;
|
|
7
|
+
this.outcomes = [...outcomes];
|
|
8
|
+
}
|
|
9
|
+
envelopes = [];
|
|
10
|
+
outcomes;
|
|
11
|
+
async send(envelope) {
|
|
12
|
+
this.envelopes.push(structuredClone(envelope));
|
|
13
|
+
return this.outcomes.shift() ?? {
|
|
14
|
+
kind: "batch",
|
|
15
|
+
ingestId: `test-ingest-${this.envelopes.length}`,
|
|
16
|
+
results: envelope.events.map((event) => ({
|
|
17
|
+
eventId: event.eventId,
|
|
18
|
+
status: "accepted"
|
|
19
|
+
})),
|
|
20
|
+
retryAfterMs: 0
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
async loadRemoteConfig() {
|
|
24
|
+
return this.remoteConfig ? structuredClone(this.remoteConfig) : void 0;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
var TestTelemetryTransport = class {
|
|
5
28
|
envelopes = [];
|
|
6
29
|
outcomes;
|
|
7
|
-
constructor(outcomes = [
|
|
30
|
+
constructor(outcomes = []) {
|
|
8
31
|
this.outcomes = [...outcomes];
|
|
9
32
|
}
|
|
10
33
|
async send(envelope) {
|
|
11
34
|
this.envelopes.push(structuredClone(envelope));
|
|
12
|
-
return this.outcomes.shift() ?? {
|
|
35
|
+
return this.outcomes.shift() ?? {
|
|
36
|
+
kind: "batch",
|
|
37
|
+
ingestId: `test-telemetry-${this.envelopes.length}`,
|
|
38
|
+
results: [
|
|
39
|
+
...envelope.traces,
|
|
40
|
+
...envelope.spans,
|
|
41
|
+
...envelope.webVitals
|
|
42
|
+
].map((item) => ({ telemetryId: item.telemetryId, status: "accepted" })),
|
|
43
|
+
retryAfterMs: 0
|
|
44
|
+
};
|
|
13
45
|
}
|
|
14
46
|
};
|
|
15
|
-
function createTestMonitor(options, transport) {
|
|
47
|
+
function createTestMonitor(options, transport, telemetryTransport = new TestTelemetryTransport()) {
|
|
16
48
|
let sequence = 0;
|
|
49
|
+
let traceSequence = 0;
|
|
50
|
+
let spanSequence = 0;
|
|
17
51
|
return createMonitorRuntime(options, {
|
|
18
52
|
transport,
|
|
53
|
+
telemetryTransport,
|
|
19
54
|
random: () => 0,
|
|
20
|
-
createEventId: () => `test-event-${++sequence}
|
|
55
|
+
createEventId: () => `test-event-${++sequence}`,
|
|
56
|
+
createTraceId: () => (++traceSequence).toString(16).padStart(32, "0"),
|
|
57
|
+
createSpanId: () => (++spanSequence).toString(16).padStart(16, "0")
|
|
21
58
|
});
|
|
22
59
|
}
|
|
23
60
|
|
|
24
|
-
export { TestTransport, createTestMonitor };
|
|
61
|
+
export { TestTelemetryTransport, TestTransport, createTestMonitor };
|