@autter/runtime-node 1.9.1 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -6
- package/dist/chunk-3UODRQBD.js +878 -0
- package/dist/chunk-HBH64LWQ.cjs +878 -0
- package/dist/{chunk-VIP2EL47.js → chunk-KWAJIELR.js} +304 -201
- package/dist/{chunk-3P573RHA.cjs → chunk-S6WJ5YLP.cjs} +304 -201
- package/dist/{chunk-FV64SIRQ.cjs → chunk-STOXJZ7Z.cjs} +21 -10
- package/dist/{chunk-D3PJ7TQ4.js → chunk-V65UY6LU.js} +13 -2
- package/dist/cli.cjs +36 -9
- package/dist/cli.js +32 -5
- package/dist/index.cjs +622 -281
- package/dist/index.d.cts +152 -11
- package/dist/index.d.ts +152 -11
- package/dist/index.js +403 -62
- package/dist/testing.cjs +8 -8
- package/dist/testing.js +2 -2
- package/package.json +2 -1
- package/dist/chunk-FYMEBULO.js +0 -203
- package/dist/chunk-LYXQC4FI.cjs +0 -203
|
@@ -1,4 +1,199 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;//
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;// src/lifecycle.ts
|
|
2
|
+
var _core = require('@opentelemetry/core');
|
|
3
|
+
var debugEnabled = process.env.AUTTER_DEBUG === "1" || process.env.AUTTER_DEBUG === "true";
|
|
4
|
+
function setDebugMode(enabled) {
|
|
5
|
+
debugEnabled = enabled;
|
|
6
|
+
}
|
|
7
|
+
function isDebugEnabled() {
|
|
8
|
+
return debugEnabled;
|
|
9
|
+
}
|
|
10
|
+
function debugLog(...args) {
|
|
11
|
+
if (debugEnabled) {
|
|
12
|
+
console.error("[autter]", ...args);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
var TelemetryStats = (_class = class {constructor() { _class.prototype.__init.call(this); }
|
|
16
|
+
/** Captures queued via our APIs and not yet seen leaving an exporter. */
|
|
17
|
+
__init() {this.pendingCaptures = 0}
|
|
18
|
+
markCaptured(count = 1) {
|
|
19
|
+
this.pendingCaptures += count;
|
|
20
|
+
}
|
|
21
|
+
/** An exporter batch left the process successfully. */
|
|
22
|
+
onExported(spanCount) {
|
|
23
|
+
this.pendingCaptures = Math.max(0, this.pendingCaptures - spanCount);
|
|
24
|
+
if (debugEnabled && spanCount > 0) {
|
|
25
|
+
debugLog(`exported ${spanCount} span${spanCount === 1 ? "" : "s"}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
markAllFlushed() {
|
|
29
|
+
this.pendingCaptures = 0;
|
|
30
|
+
}
|
|
31
|
+
}, _class);
|
|
32
|
+
var telemetryStats = new TelemetryStats();
|
|
33
|
+
var CountingExporter = class {
|
|
34
|
+
constructor(inner) {
|
|
35
|
+
this.inner = inner;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export(spans, resultCallback) {
|
|
39
|
+
this.inner.export(spans, (result) => {
|
|
40
|
+
if (result.code === _core.ExportResultCode.SUCCESS) {
|
|
41
|
+
telemetryStats.onExported(spans.length);
|
|
42
|
+
}
|
|
43
|
+
resultCallback(result);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
shutdown() {
|
|
47
|
+
return this.inner.shutdown();
|
|
48
|
+
}
|
|
49
|
+
forceFlush() {
|
|
50
|
+
return _nullishCoalesce(_optionalChain([this, 'access', _ => _.inner, 'access', _2 => _2.forceFlush, 'optionalCall', _3 => _3()]), () => ( Promise.resolve()));
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
var flushTargets = /* @__PURE__ */ new Map();
|
|
54
|
+
function registerFlushTarget(key, target) {
|
|
55
|
+
flushTargets.set(key, target);
|
|
56
|
+
}
|
|
57
|
+
function unregisterFlushTargets() {
|
|
58
|
+
flushTargets.clear();
|
|
59
|
+
}
|
|
60
|
+
function activeFlushTargets() {
|
|
61
|
+
return [...flushTargets.values()];
|
|
62
|
+
}
|
|
63
|
+
var DEFAULT_SIGNALS = ["SIGINT", "SIGTERM"];
|
|
64
|
+
var SIGNAL_EXIT_CODES = {
|
|
65
|
+
SIGINT: 130,
|
|
66
|
+
SIGTERM: 143
|
|
67
|
+
};
|
|
68
|
+
function installAutterAutoFlush(options = {}) {
|
|
69
|
+
const logger = _nullishCoalesce(options.logger, () => ( console));
|
|
70
|
+
const log = options.log !== false;
|
|
71
|
+
const warnOnUnflushedExit = options.warnOnUnflushedExit !== false;
|
|
72
|
+
const timeoutMs = Math.max(100, _nullishCoalesce(options.timeoutMs, () => ( 3e3)));
|
|
73
|
+
const signals = _nullishCoalesce(options.signals, () => ( DEFAULT_SIGNALS));
|
|
74
|
+
const flushOnUncaughtException = options.flushOnUncaughtException !== false;
|
|
75
|
+
let disposed = false;
|
|
76
|
+
let inFlight = null;
|
|
77
|
+
const doFlush = async () => {
|
|
78
|
+
const targets = _nullishCoalesce(options.targets, () => ( activeFlushTargets()));
|
|
79
|
+
if (targets.length === 0) return true;
|
|
80
|
+
let timer;
|
|
81
|
+
const timedOut = new Promise((resolve) => {
|
|
82
|
+
timer = setTimeout(() => resolve(false), timeoutMs);
|
|
83
|
+
});
|
|
84
|
+
const drained = Promise.allSettled(
|
|
85
|
+
targets.map(
|
|
86
|
+
(target) => Promise.resolve().then(() => target.forceFlush())
|
|
87
|
+
)
|
|
88
|
+
).then(
|
|
89
|
+
(results) => results.every((result) => result.status === "fulfilled")
|
|
90
|
+
);
|
|
91
|
+
let ok;
|
|
92
|
+
try {
|
|
93
|
+
ok = await Promise.race([drained, timedOut]);
|
|
94
|
+
} finally {
|
|
95
|
+
clearTimeout(timer);
|
|
96
|
+
}
|
|
97
|
+
if (ok) {
|
|
98
|
+
telemetryStats.markAllFlushed();
|
|
99
|
+
if (log) {
|
|
100
|
+
logger.log("[autter] telemetry flushed");
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
logger.error(
|
|
104
|
+
`[autter] flush did not finish within ${timeoutMs}ms \u2014 some telemetry may be lost`
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
return ok;
|
|
108
|
+
};
|
|
109
|
+
const flush = (reason = "exit") => {
|
|
110
|
+
if (disposed) return Promise.resolve(false);
|
|
111
|
+
if (!inFlight) {
|
|
112
|
+
inFlight = doFlush().finally(() => {
|
|
113
|
+
inFlight = null;
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
if (isDebugEnabled()) debugLog(`auto-flush requested on ${reason}`);
|
|
117
|
+
return inFlight;
|
|
118
|
+
};
|
|
119
|
+
const signalCounts = /* @__PURE__ */ new Map();
|
|
120
|
+
let handlingUncaughtException = false;
|
|
121
|
+
const onUncaughtException = (error) => {
|
|
122
|
+
if (process.listenerCount("uncaughtException") > 1) return;
|
|
123
|
+
if (handlingUncaughtException) return;
|
|
124
|
+
handlingUncaughtException = true;
|
|
125
|
+
logger.error(error instanceof Error ? _nullishCoalesce(error.stack, () => ( error.message)) : String(error));
|
|
126
|
+
const keepAlive = setTimeout(() => {
|
|
127
|
+
}, timeoutMs + 50);
|
|
128
|
+
void flush("uncaughtException").finally(() => {
|
|
129
|
+
clearTimeout(keepAlive);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
const onSignal = (signal) => {
|
|
134
|
+
const count = (_nullishCoalesce(signalCounts.get(signal), () => ( 0))) + 1;
|
|
135
|
+
signalCounts.set(signal, count);
|
|
136
|
+
const exitCode = _nullishCoalesce(SIGNAL_EXIT_CODES[signal], () => ( 1));
|
|
137
|
+
if (count > 1) {
|
|
138
|
+
process.exit(exitCode);
|
|
139
|
+
}
|
|
140
|
+
const otherHandlers = process.listenerCount(signal) - 1;
|
|
141
|
+
if (otherHandlers > 0) {
|
|
142
|
+
void flush(signal);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
void flush(signal).finally(() => {
|
|
146
|
+
process.exit(exitCode);
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
const MAX_BEFORE_EXIT_FLUSHES = 10;
|
|
150
|
+
let beforeExitFlushes = 0;
|
|
151
|
+
const onBeforeExit = () => {
|
|
152
|
+
if (disposed) return;
|
|
153
|
+
const targets = _nullishCoalesce(options.targets, () => ( activeFlushTargets()));
|
|
154
|
+
const worthIt = beforeExitFlushes === 0 ? targets.length > 0 : telemetryStats.pendingCaptures > 0;
|
|
155
|
+
if (!worthIt || beforeExitFlushes >= MAX_BEFORE_EXIT_FLUSHES) return;
|
|
156
|
+
beforeExitFlushes++;
|
|
157
|
+
const keepAlive = setTimeout(() => {
|
|
158
|
+
}, timeoutMs);
|
|
159
|
+
void flush("beforeExit").finally(() => clearTimeout(keepAlive));
|
|
160
|
+
};
|
|
161
|
+
const onExit = () => {
|
|
162
|
+
if (!warnOnUnflushedExit || disposed) return;
|
|
163
|
+
const pending = telemetryStats.pendingCaptures;
|
|
164
|
+
if (pending > 0) {
|
|
165
|
+
logger.error(
|
|
166
|
+
`[autter] process exiting with ~${pending} captured item(s) never confirmed exported \u2014 await autter.shutdown() on graceful shutdown`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
const signalHandlers = /* @__PURE__ */ new Map();
|
|
171
|
+
process.on("beforeExit", onBeforeExit);
|
|
172
|
+
if (flushOnUncaughtException) {
|
|
173
|
+
process.on("uncaughtException", onUncaughtException);
|
|
174
|
+
}
|
|
175
|
+
for (const signal of signals) {
|
|
176
|
+
const handler = () => onSignal(signal);
|
|
177
|
+
signalHandlers.set(signal, handler);
|
|
178
|
+
process.on(signal, handler);
|
|
179
|
+
}
|
|
180
|
+
process.on("exit", onExit);
|
|
181
|
+
return {
|
|
182
|
+
flush,
|
|
183
|
+
dispose() {
|
|
184
|
+
disposed = true;
|
|
185
|
+
process.removeListener("beforeExit", onBeforeExit);
|
|
186
|
+
process.removeListener("uncaughtException", onUncaughtException);
|
|
187
|
+
process.removeListener("exit", onExit);
|
|
188
|
+
for (const [signal, handler] of signalHandlers) {
|
|
189
|
+
process.removeListener(signal, handler);
|
|
190
|
+
}
|
|
191
|
+
signalHandlers.clear();
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ../otlp-ingester/src/compat-manifest.json
|
|
2
197
|
var compat_manifest_default = {
|
|
3
198
|
manifestVersion: 1,
|
|
4
199
|
docs: "https://github.com/Autter-dev/autter-runtime/blob/main/docs/COMPATIBILITY.md",
|
|
@@ -248,7 +443,7 @@ function featureById(id, manifest = COMPAT_MANIFEST) {
|
|
|
248
443
|
}
|
|
249
444
|
function featuresForBrowserEvents(types, manifest = COMPAT_MANIFEST) {
|
|
250
445
|
const wanted = new Set(types);
|
|
251
|
-
return manifest.features.filter((feature) => _optionalChain([feature, 'access',
|
|
446
|
+
return manifest.features.filter((feature) => _optionalChain([feature, 'access', _4 => _4.browserEvents, 'optionalAccess', _5 => _5.some, 'call', _6 => _6((type) => wanted.has(type))])).map((feature) => feature.id);
|
|
252
447
|
}
|
|
253
448
|
function ingesterUpgradeHint(manifest = COMPAT_MANIFEST) {
|
|
254
449
|
return `Upgrade the ingester: ${manifest.upgrade.ingester}. See ${manifest.docs}`;
|
|
@@ -296,7 +491,7 @@ function evaluateCompat(input) {
|
|
|
296
491
|
});
|
|
297
492
|
continue;
|
|
298
493
|
}
|
|
299
|
-
if (ingester.version === null && feature.route && _optionalChain([ingester, 'access',
|
|
494
|
+
if (ingester.version === null && feature.route && _optionalChain([ingester, 'access', _7 => _7.routes, 'optionalAccess', _8 => _8[feature.route]]) === false) {
|
|
300
495
|
const actual = "1.4.0 or older (no /v1/compat)";
|
|
301
496
|
issues.push({
|
|
302
497
|
feature: id,
|
|
@@ -335,7 +530,7 @@ function evaluateCompat(input) {
|
|
|
335
530
|
function ingesterInfoFromReport(body) {
|
|
336
531
|
if (!body || typeof body !== "object") return null;
|
|
337
532
|
const report = body;
|
|
338
|
-
const version = _optionalChain([report, 'access',
|
|
533
|
+
const version = _optionalChain([report, 'access', _9 => _9.ingester, 'optionalAccess', _10 => _10.version]);
|
|
339
534
|
if (typeof version !== "string" || !parseVersion(version)) return null;
|
|
340
535
|
const schema = report.schema;
|
|
341
536
|
const statuses = ["ready", "pending", "failed", "unconfigured"];
|
|
@@ -350,201 +545,6 @@ function ingesterInfoFromReport(body) {
|
|
|
350
545
|
};
|
|
351
546
|
}
|
|
352
547
|
|
|
353
|
-
// src/lifecycle.ts
|
|
354
|
-
var _core = require('@opentelemetry/core');
|
|
355
|
-
var debugEnabled = process.env.AUTTER_DEBUG === "1" || process.env.AUTTER_DEBUG === "true";
|
|
356
|
-
function setDebugMode(enabled) {
|
|
357
|
-
debugEnabled = enabled;
|
|
358
|
-
}
|
|
359
|
-
function isDebugEnabled() {
|
|
360
|
-
return debugEnabled;
|
|
361
|
-
}
|
|
362
|
-
function debugLog(...args) {
|
|
363
|
-
if (debugEnabled) {
|
|
364
|
-
console.error("[autter]", ...args);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
var TelemetryStats = (_class = class {constructor() { _class.prototype.__init.call(this); }
|
|
368
|
-
/** Captures queued via our APIs and not yet seen leaving an exporter. */
|
|
369
|
-
__init() {this.pendingCaptures = 0}
|
|
370
|
-
markCaptured(count = 1) {
|
|
371
|
-
this.pendingCaptures += count;
|
|
372
|
-
}
|
|
373
|
-
/** An exporter batch left the process successfully. */
|
|
374
|
-
onExported(spanCount) {
|
|
375
|
-
this.pendingCaptures = Math.max(0, this.pendingCaptures - spanCount);
|
|
376
|
-
if (debugEnabled && spanCount > 0) {
|
|
377
|
-
debugLog(`exported ${spanCount} span${spanCount === 1 ? "" : "s"}`);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
markAllFlushed() {
|
|
381
|
-
this.pendingCaptures = 0;
|
|
382
|
-
}
|
|
383
|
-
}, _class);
|
|
384
|
-
var telemetryStats = new TelemetryStats();
|
|
385
|
-
var CountingExporter = class {
|
|
386
|
-
constructor(inner) {
|
|
387
|
-
this.inner = inner;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
export(spans, resultCallback) {
|
|
391
|
-
this.inner.export(spans, (result) => {
|
|
392
|
-
if (result.code === _core.ExportResultCode.SUCCESS) {
|
|
393
|
-
telemetryStats.onExported(spans.length);
|
|
394
|
-
}
|
|
395
|
-
resultCallback(result);
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
shutdown() {
|
|
399
|
-
return this.inner.shutdown();
|
|
400
|
-
}
|
|
401
|
-
forceFlush() {
|
|
402
|
-
return _nullishCoalesce(_optionalChain([this, 'access', _8 => _8.inner, 'access', _9 => _9.forceFlush, 'optionalCall', _10 => _10()]), () => ( Promise.resolve()));
|
|
403
|
-
}
|
|
404
|
-
};
|
|
405
|
-
var flushTargets = /* @__PURE__ */ new Map();
|
|
406
|
-
function registerFlushTarget(key, target) {
|
|
407
|
-
flushTargets.set(key, target);
|
|
408
|
-
}
|
|
409
|
-
function unregisterFlushTargets() {
|
|
410
|
-
flushTargets.clear();
|
|
411
|
-
}
|
|
412
|
-
function activeFlushTargets() {
|
|
413
|
-
return [...flushTargets.values()];
|
|
414
|
-
}
|
|
415
|
-
var DEFAULT_SIGNALS = ["SIGINT", "SIGTERM"];
|
|
416
|
-
var SIGNAL_EXIT_CODES = {
|
|
417
|
-
SIGINT: 130,
|
|
418
|
-
SIGTERM: 143
|
|
419
|
-
};
|
|
420
|
-
function installAutterAutoFlush(options = {}) {
|
|
421
|
-
const logger = _nullishCoalesce(options.logger, () => ( console));
|
|
422
|
-
const log = options.log !== false;
|
|
423
|
-
const warnOnUnflushedExit = options.warnOnUnflushedExit !== false;
|
|
424
|
-
const timeoutMs = Math.max(100, _nullishCoalesce(options.timeoutMs, () => ( 3e3)));
|
|
425
|
-
const signals = _nullishCoalesce(options.signals, () => ( DEFAULT_SIGNALS));
|
|
426
|
-
const flushOnUncaughtException = options.flushOnUncaughtException !== false;
|
|
427
|
-
let disposed = false;
|
|
428
|
-
let inFlight = null;
|
|
429
|
-
const doFlush = async () => {
|
|
430
|
-
const targets = _nullishCoalesce(options.targets, () => ( activeFlushTargets()));
|
|
431
|
-
if (targets.length === 0) return true;
|
|
432
|
-
let timer;
|
|
433
|
-
const timedOut = new Promise((resolve) => {
|
|
434
|
-
timer = setTimeout(() => resolve(false), timeoutMs);
|
|
435
|
-
});
|
|
436
|
-
const drained = Promise.allSettled(
|
|
437
|
-
targets.map(
|
|
438
|
-
(target) => Promise.resolve().then(() => target.forceFlush())
|
|
439
|
-
)
|
|
440
|
-
).then(
|
|
441
|
-
(results) => results.every((result) => result.status === "fulfilled")
|
|
442
|
-
);
|
|
443
|
-
let ok;
|
|
444
|
-
try {
|
|
445
|
-
ok = await Promise.race([drained, timedOut]);
|
|
446
|
-
} finally {
|
|
447
|
-
clearTimeout(timer);
|
|
448
|
-
}
|
|
449
|
-
if (ok) {
|
|
450
|
-
telemetryStats.markAllFlushed();
|
|
451
|
-
if (log) {
|
|
452
|
-
logger.log("[autter] telemetry flushed");
|
|
453
|
-
}
|
|
454
|
-
} else {
|
|
455
|
-
logger.error(
|
|
456
|
-
`[autter] flush did not finish within ${timeoutMs}ms \u2014 some telemetry may be lost`
|
|
457
|
-
);
|
|
458
|
-
}
|
|
459
|
-
return ok;
|
|
460
|
-
};
|
|
461
|
-
const flush = (reason = "exit") => {
|
|
462
|
-
if (disposed) return Promise.resolve(false);
|
|
463
|
-
if (!inFlight) {
|
|
464
|
-
inFlight = doFlush().finally(() => {
|
|
465
|
-
inFlight = null;
|
|
466
|
-
});
|
|
467
|
-
}
|
|
468
|
-
if (isDebugEnabled()) debugLog(`auto-flush requested on ${reason}`);
|
|
469
|
-
return inFlight;
|
|
470
|
-
};
|
|
471
|
-
const signalCounts = /* @__PURE__ */ new Map();
|
|
472
|
-
let handlingUncaughtException = false;
|
|
473
|
-
const onUncaughtException = (error) => {
|
|
474
|
-
if (process.listenerCount("uncaughtException") > 1) return;
|
|
475
|
-
if (handlingUncaughtException) return;
|
|
476
|
-
handlingUncaughtException = true;
|
|
477
|
-
logger.error(error instanceof Error ? _nullishCoalesce(error.stack, () => ( error.message)) : String(error));
|
|
478
|
-
const keepAlive = setTimeout(() => {
|
|
479
|
-
}, timeoutMs + 50);
|
|
480
|
-
void flush("uncaughtException").finally(() => {
|
|
481
|
-
clearTimeout(keepAlive);
|
|
482
|
-
process.exit(1);
|
|
483
|
-
});
|
|
484
|
-
};
|
|
485
|
-
const onSignal = (signal) => {
|
|
486
|
-
const count = (_nullishCoalesce(signalCounts.get(signal), () => ( 0))) + 1;
|
|
487
|
-
signalCounts.set(signal, count);
|
|
488
|
-
const exitCode = _nullishCoalesce(SIGNAL_EXIT_CODES[signal], () => ( 1));
|
|
489
|
-
if (count > 1) {
|
|
490
|
-
process.exit(exitCode);
|
|
491
|
-
}
|
|
492
|
-
const otherHandlers = process.listenerCount(signal) - 1;
|
|
493
|
-
if (otherHandlers > 0) {
|
|
494
|
-
void flush(signal);
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
void flush(signal).finally(() => {
|
|
498
|
-
process.exit(exitCode);
|
|
499
|
-
});
|
|
500
|
-
};
|
|
501
|
-
const MAX_BEFORE_EXIT_FLUSHES = 10;
|
|
502
|
-
let beforeExitFlushes = 0;
|
|
503
|
-
const onBeforeExit = () => {
|
|
504
|
-
if (disposed) return;
|
|
505
|
-
const targets = _nullishCoalesce(options.targets, () => ( activeFlushTargets()));
|
|
506
|
-
const worthIt = beforeExitFlushes === 0 ? targets.length > 0 : telemetryStats.pendingCaptures > 0;
|
|
507
|
-
if (!worthIt || beforeExitFlushes >= MAX_BEFORE_EXIT_FLUSHES) return;
|
|
508
|
-
beforeExitFlushes++;
|
|
509
|
-
const keepAlive = setTimeout(() => {
|
|
510
|
-
}, timeoutMs);
|
|
511
|
-
void flush("beforeExit").finally(() => clearTimeout(keepAlive));
|
|
512
|
-
};
|
|
513
|
-
const onExit = () => {
|
|
514
|
-
if (!warnOnUnflushedExit || disposed) return;
|
|
515
|
-
const pending = telemetryStats.pendingCaptures;
|
|
516
|
-
if (pending > 0) {
|
|
517
|
-
logger.error(
|
|
518
|
-
`[autter] process exiting with ~${pending} captured item(s) never confirmed exported \u2014 await autter.shutdown() on graceful shutdown`
|
|
519
|
-
);
|
|
520
|
-
}
|
|
521
|
-
};
|
|
522
|
-
const signalHandlers = /* @__PURE__ */ new Map();
|
|
523
|
-
process.on("beforeExit", onBeforeExit);
|
|
524
|
-
if (flushOnUncaughtException) {
|
|
525
|
-
process.on("uncaughtException", onUncaughtException);
|
|
526
|
-
}
|
|
527
|
-
for (const signal of signals) {
|
|
528
|
-
const handler = () => onSignal(signal);
|
|
529
|
-
signalHandlers.set(signal, handler);
|
|
530
|
-
process.on(signal, handler);
|
|
531
|
-
}
|
|
532
|
-
process.on("exit", onExit);
|
|
533
|
-
return {
|
|
534
|
-
flush,
|
|
535
|
-
dispose() {
|
|
536
|
-
disposed = true;
|
|
537
|
-
process.removeListener("beforeExit", onBeforeExit);
|
|
538
|
-
process.removeListener("uncaughtException", onUncaughtException);
|
|
539
|
-
process.removeListener("exit", onExit);
|
|
540
|
-
for (const [signal, handler] of signalHandlers) {
|
|
541
|
-
process.removeListener(signal, handler);
|
|
542
|
-
}
|
|
543
|
-
signalHandlers.clear();
|
|
544
|
-
}
|
|
545
|
-
};
|
|
546
|
-
}
|
|
547
|
-
|
|
548
548
|
// src/compat.ts
|
|
549
549
|
var _http = require('http');
|
|
550
550
|
var _https = require('https');
|
|
@@ -552,7 +552,7 @@ var _https = require('https');
|
|
|
552
552
|
// package.json
|
|
553
553
|
var package_default = {
|
|
554
554
|
name: "@autter/runtime-node",
|
|
555
|
-
version: "1.
|
|
555
|
+
version: "1.10.0",
|
|
556
556
|
description: "Autter Runtime for Node.js: request wide events, coded errors, operation logging, same-origin browser relay and a curated OpenTelemetry server tracker",
|
|
557
557
|
license: "MIT",
|
|
558
558
|
type: "module",
|
|
@@ -605,6 +605,7 @@ var package_default = {
|
|
|
605
605
|
"@types/node": "^22.10.0",
|
|
606
606
|
bunyan: "^1.8.15",
|
|
607
607
|
express: "^4.21.2",
|
|
608
|
+
express5: "npm:express@^5.3.0",
|
|
608
609
|
fastify: "^5.12.5",
|
|
609
610
|
koa: "^3.2.1",
|
|
610
611
|
pino: "^10.4.0",
|
|
@@ -926,6 +927,108 @@ function noteRelayRejection(eventTypes, status, headers) {
|
|
|
926
927
|
}
|
|
927
928
|
}
|
|
928
929
|
|
|
930
|
+
// src/ingest-auth.ts
|
|
931
|
+
var NOT_A_KEY = /* @__PURE__ */ new Set(["", "undefined", "null"]);
|
|
932
|
+
function resolveIngestKey(explicit, env = process.env) {
|
|
933
|
+
for (const candidate of [explicit, env.AUTTER_RUNTIME_KEY]) {
|
|
934
|
+
if (typeof candidate !== "string") continue;
|
|
935
|
+
const key = candidate.trim();
|
|
936
|
+
if (!NOT_A_KEY.has(key.toLowerCase())) return key;
|
|
937
|
+
}
|
|
938
|
+
return null;
|
|
939
|
+
}
|
|
940
|
+
function authHeaders(key) {
|
|
941
|
+
return key ? { authorization: `Bearer ${key}` } : {};
|
|
942
|
+
}
|
|
943
|
+
var defaultWarn2 = (message) => console.warn(`[autter-runtime] ${message}`);
|
|
944
|
+
var state = sharedSlot("ingest-auth", () => ({ warned: /* @__PURE__ */ new Set(), warn: defaultWarn2 }));
|
|
945
|
+
function once(id, message) {
|
|
946
|
+
if (state.warned.has(id)) return false;
|
|
947
|
+
state.warned.add(id);
|
|
948
|
+
try {
|
|
949
|
+
state.warn(message);
|
|
950
|
+
} catch (e10) {
|
|
951
|
+
}
|
|
952
|
+
return true;
|
|
953
|
+
}
|
|
954
|
+
var VERIFY_HINT = "Then run: npx @autter/runtime-node verify";
|
|
955
|
+
function warnMissingKey(where, service) {
|
|
956
|
+
const what = where === "relay" ? "the browser relay has no ingest key (apiKey is unset and AUTTER_RUNTIME_KEY is not set), so browser events are refused (500) instead of being sent" : `initAutterServer() got no apiKey and AUTTER_RUNTIME_KEY is not set, so traces, metrics and logs${service ? ` from "${service}"` : ""} are not sent`;
|
|
957
|
+
return once(
|
|
958
|
+
`missing:${where}`,
|
|
959
|
+
`No ingest key: ${what}. Set AUTTER_RUNTIME_KEY to a server key (autter_rt_\u2026). ${VERIFY_HINT}`
|
|
960
|
+
);
|
|
961
|
+
}
|
|
962
|
+
function warnKeyKind(key) {
|
|
963
|
+
if (!key) return false;
|
|
964
|
+
if (key.startsWith("autter_rtc_")) {
|
|
965
|
+
return once(
|
|
966
|
+
"kind:client",
|
|
967
|
+
"AUTTER_RUNTIME_KEY is a client (browser) key (autter_rtc_\u2026). Server SDKs and the relay need a server key (autter_rt_\u2026); the ingester refuses this one with 403."
|
|
968
|
+
);
|
|
969
|
+
}
|
|
970
|
+
if (key.startsWith("autter_rtu_")) {
|
|
971
|
+
return once(
|
|
972
|
+
"kind:upload",
|
|
973
|
+
"AUTTER_RUNTIME_KEY is an upload-only key (autter_rtu_\u2026) for source maps. Server SDKs need a server key (autter_rt_\u2026); the ingester refuses this one with 403."
|
|
974
|
+
);
|
|
975
|
+
}
|
|
976
|
+
return false;
|
|
977
|
+
}
|
|
978
|
+
function refusalReason(body) {
|
|
979
|
+
let text = "";
|
|
980
|
+
if (typeof body === "string") {
|
|
981
|
+
try {
|
|
982
|
+
const parsed = JSON.parse(body);
|
|
983
|
+
text = [parsed.error, parsed.detail].filter((v) => typeof v === "string" && v).join(": ");
|
|
984
|
+
} catch (e11) {
|
|
985
|
+
text = body;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
return text.replace(/\s+/g, " ").trim().slice(0, 300);
|
|
989
|
+
}
|
|
990
|
+
function noteIngestRefusal(input) {
|
|
991
|
+
try {
|
|
992
|
+
const { status, route, endpoint } = input;
|
|
993
|
+
const reason = refusalReason(input.body);
|
|
994
|
+
const said = reason ? ` (${status}: ${reason})` : ` (${status})`;
|
|
995
|
+
const who = input.via === "relay" ? "browser events forwarded by the relay are" : "this data is";
|
|
996
|
+
if (status === 401) {
|
|
997
|
+
return once(
|
|
998
|
+
"status:401",
|
|
999
|
+
`The ingester at ${endpoint} refused the ingest key on ${route}${said}. Nothing is stored until it is fixed: ${who} dropped. Check AUTTER_RUNTIME_KEY (a server key, autter_rt_\u2026, for this repository). ${VERIFY_HINT}`
|
|
1000
|
+
);
|
|
1001
|
+
}
|
|
1002
|
+
if (status === 403) {
|
|
1003
|
+
return once(
|
|
1004
|
+
`status:403:${route}`,
|
|
1005
|
+
`The ingester at ${endpoint} refused ${route}${said}; ${who} dropped. ${VERIFY_HINT}`
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
if (status === 404) {
|
|
1009
|
+
return once(
|
|
1010
|
+
`status:404:${route}`,
|
|
1011
|
+
`${endpoint}${route} answered 404: this ingester has no ${route} route, so ${who} dropped. Either the ingester is older than the feature (run: npx @autter/runtime-node doctor) or the endpoint has an extra path (set it to the ingester's base URL).`
|
|
1012
|
+
);
|
|
1013
|
+
}
|
|
1014
|
+
if (status === 400 || status === 413 || status === 415) {
|
|
1015
|
+
return once(
|
|
1016
|
+
`status:${status}:${route}`,
|
|
1017
|
+
`The ingester at ${endpoint} rejected a ${route} batch${said}; ${who} dropped.`
|
|
1018
|
+
);
|
|
1019
|
+
}
|
|
1020
|
+
return false;
|
|
1021
|
+
} catch (e12) {
|
|
1022
|
+
return false;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
|
|
1027
|
+
|
|
1028
|
+
|
|
1029
|
+
|
|
1030
|
+
|
|
1031
|
+
|
|
929
1032
|
|
|
930
1033
|
|
|
931
1034
|
|
|
@@ -949,4 +1052,4 @@ function noteRelayRejection(eventTypes, status, headers) {
|
|
|
949
1052
|
|
|
950
1053
|
|
|
951
1054
|
|
|
952
|
-
exports.
|
|
1055
|
+
exports.setDebugMode = setDebugMode; exports.isDebugEnabled = isDebugEnabled; exports.debugLog = debugLog; exports.telemetryStats = telemetryStats; exports.CountingExporter = CountingExporter; exports.registerFlushTarget = registerFlushTarget; exports.unregisterFlushTargets = unregisterFlushTargets; exports.installAutterAutoFlush = installAutterAutoFlush; exports.sharedSlot = sharedSlot; exports.resolveIngestKey = resolveIngestKey; exports.authHeaders = authHeaders; exports.warnMissingKey = warnMissingKey; exports.warnKeyKind = warnKeyKind; exports.refusalReason = refusalReason; exports.noteIngestRefusal = noteIngestRefusal; exports.COMPAT_MANIFEST = COMPAT_MANIFEST; exports.INGESTER_VERSION_HEADER = INGESTER_VERSION_HEADER; exports.versionAtLeast = versionAtLeast; exports.featuresForBrowserEvents = featuresForBrowserEvents; exports.evaluateCompat = evaluateCompat; exports.SDK_IDENTITY = SDK_IDENTITY; exports.compatRequest = compatRequest; exports.fetchIngesterCompat = fetchIngesterCompat; exports.configureCompatCheck = configureCompatCheck; exports.ensureCompatConfigured = ensureCompatConfigured; exports.noteCompatFeature = noteCompatFeature; exports.observeIngesterResponse = observeIngesterResponse; exports.noteRelayRejection = noteRelayRejection;
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
var _chunkS6WJ5YLPcjs = require('./chunk-S6WJ5YLP.cjs');
|
|
6
9
|
|
|
7
10
|
// ../runtime-core/dist/index.js
|
|
8
11
|
var SENSITIVE_KEY_PATTERNS = [
|
|
@@ -1222,7 +1225,7 @@ var _util = require('util');
|
|
|
1222
1225
|
var _requireinthemiddle = require('require-in-the-middle');
|
|
1223
1226
|
|
|
1224
1227
|
// src/captured.ts
|
|
1225
|
-
var CAPTURED =
|
|
1228
|
+
var CAPTURED = _chunkS6WJ5YLPcjs.sharedSlot.call(void 0, "requests", () => ({ captured: /* @__PURE__ */ new WeakSet() })).captured;
|
|
1226
1229
|
function markCaptured(error) {
|
|
1227
1230
|
if (error && typeof error === "object") CAPTURED.add(error);
|
|
1228
1231
|
}
|
|
@@ -1231,7 +1234,7 @@ function isCaptured(error) {
|
|
|
1231
1234
|
}
|
|
1232
1235
|
|
|
1233
1236
|
// src/caught-errors.ts
|
|
1234
|
-
var S =
|
|
1237
|
+
var S = _chunkS6WJ5YLPcjs.sharedSlot.call(void 0, "handled", () => ({
|
|
1235
1238
|
sources: null,
|
|
1236
1239
|
capture: null,
|
|
1237
1240
|
tail: null,
|
|
@@ -1485,7 +1488,7 @@ function stopLoggedErrorCapture() {
|
|
|
1485
1488
|
|
|
1486
1489
|
var _fs = require('fs');
|
|
1487
1490
|
|
|
1488
|
-
var sinkStats =
|
|
1491
|
+
var sinkStats = _chunkS6WJ5YLPcjs.sharedSlot.call(void 0, "sinks", () => ({ dropped: 0 }));
|
|
1489
1492
|
function otlpSink(options = {}) {
|
|
1490
1493
|
let context = null;
|
|
1491
1494
|
let queue = [];
|
|
@@ -1556,16 +1559,24 @@ function otlpSink(options = {}) {
|
|
|
1556
1559
|
method: "POST",
|
|
1557
1560
|
headers: {
|
|
1558
1561
|
"content-type": "application/json",
|
|
1559
|
-
|
|
1562
|
+
..._chunkS6WJ5YLPcjs.authHeaders.call(void 0, _chunkS6WJ5YLPcjs.resolveIngestKey.call(void 0, apiKey, {}))
|
|
1560
1563
|
},
|
|
1561
1564
|
body,
|
|
1562
1565
|
signal: AbortSignal.timeout(
|
|
1563
1566
|
Math.max(1, Math.min(3e3, deadline - Date.now()))
|
|
1564
1567
|
)
|
|
1565
1568
|
});
|
|
1566
|
-
|
|
1569
|
+
_chunkS6WJ5YLPcjs.observeIngesterResponse.call(void 0, { status: response.status, headers: response.headers, route: "/v1/logs" });
|
|
1567
1570
|
if (!response.ok) {
|
|
1568
1571
|
failure = new Error(`Runtime log export failed (${response.status})`);
|
|
1572
|
+
if (response.status !== 429 && response.status < 500) {
|
|
1573
|
+
_chunkS6WJ5YLPcjs.noteIngestRefusal.call(void 0, {
|
|
1574
|
+
status: response.status,
|
|
1575
|
+
route: "/v1/logs",
|
|
1576
|
+
endpoint,
|
|
1577
|
+
body: await response.text().catch(() => "")
|
|
1578
|
+
});
|
|
1579
|
+
}
|
|
1569
1580
|
if (response.status !== 429 && response.status < 500) break;
|
|
1570
1581
|
retryAfter = response.headers.get("retry-after");
|
|
1571
1582
|
} else {
|
|
@@ -1623,7 +1634,7 @@ function otlpSink(options = {}) {
|
|
|
1623
1634
|
}
|
|
1624
1635
|
queue.push({ record, bytes });
|
|
1625
1636
|
queueBytes += bytes;
|
|
1626
|
-
|
|
1637
|
+
_chunkS6WJ5YLPcjs.noteCompatFeature.call(void 0, "operation_logging");
|
|
1627
1638
|
scheduleFlush();
|
|
1628
1639
|
},
|
|
1629
1640
|
flush,
|
|
@@ -1866,7 +1877,7 @@ function memorySink() {
|
|
|
1866
1877
|
}
|
|
1867
1878
|
|
|
1868
1879
|
// src/logger.ts
|
|
1869
|
-
var shared =
|
|
1880
|
+
var shared = _chunkS6WJ5YLPcjs.sharedSlot.call(void 0, "logger", () => ({
|
|
1870
1881
|
local: new (0, _async_hooks.AsyncLocalStorage)(),
|
|
1871
1882
|
config: null,
|
|
1872
1883
|
sinks: [],
|
|
@@ -2366,7 +2377,7 @@ setHandledTail(
|
|
|
2366
2377
|
// src/breadcrumbs.ts
|
|
2367
2378
|
var INVALID_TRACE_ID = "00000000000000000000000000000000";
|
|
2368
2379
|
var MAX_TRACE_TRAILS = 200;
|
|
2369
|
-
var trails =
|
|
2380
|
+
var trails = _chunkS6WJ5YLPcjs.sharedSlot.call(void 0, "breadcrumbs", () => ({
|
|
2370
2381
|
byTrace: /* @__PURE__ */ new Map(),
|
|
2371
2382
|
process: new BreadcrumbTrail(),
|
|
2372
2383
|
thrown: /* @__PURE__ */ new WeakMap()
|
|
@@ -2402,7 +2413,7 @@ function addBreadcrumb(input) {
|
|
|
2402
2413
|
const crumb = normalizeBreadcrumb(input, redact2);
|
|
2403
2414
|
if (!crumb) return;
|
|
2404
2415
|
_optionalChain([trailFor, 'call', _122 => _122(true), 'optionalAccess', _123 => _123.add, 'call', _124 => _124(crumb)]);
|
|
2405
|
-
|
|
2416
|
+
_chunkS6WJ5YLPcjs.noteCompatFeature.call(void 0, "breadcrumbs");
|
|
2406
2417
|
} catch (e39) {
|
|
2407
2418
|
}
|
|
2408
2419
|
}
|