@blaxel/telemetry 0.2.0-dev1 → 0.2.0-dev11
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/index.d.ts +1 -2
- package/dist/index.js +10 -3
- package/dist/json_logger.d.ts +8 -0
- package/dist/json_logger.js +53 -0
- package/dist/legacy_logger.d.ts +8 -0
- package/dist/legacy_logger.js +58 -0
- package/dist/logger.d.ts +1 -0
- package/dist/logger.js +28 -25
- package/dist/telemetry.d.ts +2 -2
- package/dist/telemetry.js +16 -14
- package/dist/telemetry_provider.d.ts +4 -0
- package/dist/telemetry_provider.js +52 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
const
|
|
5
|
-
|
|
3
|
+
exports.blaxelTelemetry = void 0;
|
|
4
|
+
const core_1 = require("@blaxel/core");
|
|
5
|
+
const json_logger_1 = require("./json_logger");
|
|
6
|
+
const legacy_logger_1 = require("./legacy_logger");
|
|
6
7
|
const telemetry_1 = require("./telemetry");
|
|
7
8
|
Object.defineProperty(exports, "blaxelTelemetry", { enumerable: true, get: function () { return telemetry_1.blaxelTelemetry; } });
|
|
8
9
|
telemetry_1.blaxelTelemetry.initialize();
|
|
10
|
+
if (core_1.settings.loggerType === "http") {
|
|
11
|
+
(0, legacy_logger_1.setLegacyLogger)();
|
|
12
|
+
}
|
|
13
|
+
else if (core_1.settings.loggerType === "json") {
|
|
14
|
+
(0, json_logger_1.setJsonLogger)();
|
|
15
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function setJsonLogger(): void;
|
|
2
|
+
export declare const originalLogger: {
|
|
3
|
+
info: (message?: any, ...optionalParams: any[]) => void;
|
|
4
|
+
error: (message?: any, ...optionalParams: any[]) => void;
|
|
5
|
+
warn: (message?: any, ...optionalParams: any[]) => void;
|
|
6
|
+
debug: (message?: any, ...optionalParams: any[]) => void;
|
|
7
|
+
log: (message?: any, ...optionalParams: any[]) => void;
|
|
8
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint-disable no-console */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.originalLogger = void 0;
|
|
5
|
+
exports.setJsonLogger = setJsonLogger;
|
|
6
|
+
const core_1 = require("@blaxel/core");
|
|
7
|
+
const api_1 = require("@opentelemetry/api");
|
|
8
|
+
function setJsonLogger() {
|
|
9
|
+
console.debug = (message, ...args) => {
|
|
10
|
+
const msg = formatLogMessage("DEBUG", message, args);
|
|
11
|
+
exports.originalLogger.log(msg);
|
|
12
|
+
};
|
|
13
|
+
console.log = (message, ...args) => {
|
|
14
|
+
const msg = formatLogMessage("INFO", message, args);
|
|
15
|
+
exports.originalLogger.log(msg);
|
|
16
|
+
};
|
|
17
|
+
console.info = (message, ...args) => {
|
|
18
|
+
const msg = formatLogMessage("INFO", message, args);
|
|
19
|
+
exports.originalLogger.log(msg);
|
|
20
|
+
};
|
|
21
|
+
console.warn = (message, ...args) => {
|
|
22
|
+
const msg = formatLogMessage("WARN", message, args);
|
|
23
|
+
exports.originalLogger.log(msg);
|
|
24
|
+
};
|
|
25
|
+
console.error = (message, ...args) => {
|
|
26
|
+
const msg = formatLogMessage("ERROR", message, args);
|
|
27
|
+
exports.originalLogger.log(msg);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
exports.originalLogger = {
|
|
31
|
+
info: console.info,
|
|
32
|
+
error: console.error,
|
|
33
|
+
warn: console.warn,
|
|
34
|
+
debug: console.debug,
|
|
35
|
+
log: console.log,
|
|
36
|
+
};
|
|
37
|
+
// Format a log message with appropriate color and prefix
|
|
38
|
+
function formatLogMessage(severity, message, args) {
|
|
39
|
+
const messageStr = typeof message === "string" ? message : (0, core_1.stringify)(message, 2);
|
|
40
|
+
const argsStr = args.map(arg => typeof arg === "string" ? arg : (0, core_1.stringify)(arg, 2)).join(" ");
|
|
41
|
+
let msg = `${messageStr}${argsStr ? " " + argsStr : ""}`;
|
|
42
|
+
const logEntry = {
|
|
43
|
+
body: msg,
|
|
44
|
+
severity
|
|
45
|
+
};
|
|
46
|
+
const currentSpan = api_1.trace.getActiveSpan();
|
|
47
|
+
if (currentSpan) {
|
|
48
|
+
const { traceId, spanId } = currentSpan.spanContext();
|
|
49
|
+
logEntry["trace_id"] = traceId;
|
|
50
|
+
logEntry["span_id"] = spanId;
|
|
51
|
+
}
|
|
52
|
+
return JSON.stringify(logEntry);
|
|
53
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function setLegacyLogger(): void;
|
|
2
|
+
export declare const originalLogger: {
|
|
3
|
+
info: (message?: any, ...optionalParams: any[]) => void;
|
|
4
|
+
error: (message?: any, ...optionalParams: any[]) => void;
|
|
5
|
+
warn: (message?: any, ...optionalParams: any[]) => void;
|
|
6
|
+
debug: (message?: any, ...optionalParams: any[]) => void;
|
|
7
|
+
log: (message?: any, ...optionalParams: any[]) => void;
|
|
8
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.originalLogger = void 0;
|
|
4
|
+
exports.setLegacyLogger = setLegacyLogger;
|
|
5
|
+
/* eslint-disable no-console */
|
|
6
|
+
const core_1 = require("@blaxel/core");
|
|
7
|
+
const api_logs_1 = require("@opentelemetry/api-logs");
|
|
8
|
+
const telemetry_1 = require("./telemetry");
|
|
9
|
+
function setLegacyLogger() {
|
|
10
|
+
console.debug = (message, ...args) => {
|
|
11
|
+
const msg = formatLogMessage(message, args);
|
|
12
|
+
exports.originalLogger.log(msg);
|
|
13
|
+
emitLogSync(api_logs_1.SeverityNumber.DEBUG, msg);
|
|
14
|
+
};
|
|
15
|
+
console.log = (message, ...args) => {
|
|
16
|
+
const msg = formatLogMessage(message, args);
|
|
17
|
+
exports.originalLogger.log(msg);
|
|
18
|
+
emitLogSync(api_logs_1.SeverityNumber.INFO, msg);
|
|
19
|
+
};
|
|
20
|
+
console.info = (message, ...args) => {
|
|
21
|
+
const msg = formatLogMessage(message, args);
|
|
22
|
+
exports.originalLogger.log(msg);
|
|
23
|
+
emitLogSync(api_logs_1.SeverityNumber.INFO, msg);
|
|
24
|
+
};
|
|
25
|
+
console.error = (message, ...args) => {
|
|
26
|
+
const msg = formatLogMessage(message, args);
|
|
27
|
+
exports.originalLogger.log(msg);
|
|
28
|
+
emitLogSync(api_logs_1.SeverityNumber.ERROR, msg);
|
|
29
|
+
};
|
|
30
|
+
console.warn = (message, ...args) => {
|
|
31
|
+
const msg = formatLogMessage(message, args);
|
|
32
|
+
exports.originalLogger.log(msg);
|
|
33
|
+
emitLogSync(api_logs_1.SeverityNumber.WARN, msg);
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
exports.originalLogger = {
|
|
37
|
+
info: console.info,
|
|
38
|
+
error: console.error,
|
|
39
|
+
warn: console.warn,
|
|
40
|
+
debug: console.debug,
|
|
41
|
+
log: console.log,
|
|
42
|
+
};
|
|
43
|
+
// Format a log message with appropriate color and prefix
|
|
44
|
+
function formatLogMessage(message, args) {
|
|
45
|
+
const messageStr = typeof message === "string" ? message : (0, core_1.stringify)(message, 2);
|
|
46
|
+
const argsStr = args.map(arg => typeof arg === "string" ? arg : (0, core_1.stringify)(arg, 2)).join(" ");
|
|
47
|
+
return `${messageStr}${argsStr ? " " + argsStr : ""}`;
|
|
48
|
+
}
|
|
49
|
+
async function emitLog(severityNumber, message) {
|
|
50
|
+
const loggerInstance = await telemetry_1.blaxelTelemetry.getLogger();
|
|
51
|
+
loggerInstance.emit({
|
|
52
|
+
severityNumber: severityNumber,
|
|
53
|
+
body: message,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function emitLogSync(severityNumber, message) {
|
|
57
|
+
emitLog(severityNumber, message).catch(() => { });
|
|
58
|
+
}
|
package/dist/logger.d.ts
CHANGED
package/dist/logger.js
CHANGED
|
@@ -1,10 +1,38 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.originalLogger = void 0;
|
|
4
|
+
exports.overrideConsole = overrideConsole;
|
|
4
5
|
exports.stringify = stringify;
|
|
5
6
|
/* eslint-disable no-console */
|
|
6
7
|
const api_logs_1 = require("@opentelemetry/api-logs");
|
|
7
8
|
const telemetry_1 = require("./telemetry");
|
|
9
|
+
function overrideConsole() {
|
|
10
|
+
console.debug = (message, ...args) => {
|
|
11
|
+
const msg = formatLogMessage(message, args);
|
|
12
|
+
exports.originalLogger.log(msg);
|
|
13
|
+
emitLogSync(api_logs_1.SeverityNumber.DEBUG, msg);
|
|
14
|
+
};
|
|
15
|
+
console.log = (message, ...args) => {
|
|
16
|
+
const msg = formatLogMessage(message, args);
|
|
17
|
+
exports.originalLogger.log(msg);
|
|
18
|
+
emitLogSync(api_logs_1.SeverityNumber.INFO, msg);
|
|
19
|
+
};
|
|
20
|
+
console.info = (message, ...args) => {
|
|
21
|
+
const msg = formatLogMessage(message, args);
|
|
22
|
+
exports.originalLogger.log(msg);
|
|
23
|
+
emitLogSync(api_logs_1.SeverityNumber.INFO, msg);
|
|
24
|
+
};
|
|
25
|
+
console.error = (message, ...args) => {
|
|
26
|
+
const msg = formatLogMessage(message, args);
|
|
27
|
+
exports.originalLogger.log(msg);
|
|
28
|
+
emitLogSync(api_logs_1.SeverityNumber.ERROR, msg);
|
|
29
|
+
};
|
|
30
|
+
console.warn = (message, ...args) => {
|
|
31
|
+
const msg = formatLogMessage(message, args);
|
|
32
|
+
exports.originalLogger.log(msg);
|
|
33
|
+
emitLogSync(api_logs_1.SeverityNumber.WARN, msg);
|
|
34
|
+
};
|
|
35
|
+
}
|
|
8
36
|
exports.originalLogger = {
|
|
9
37
|
info: console.info,
|
|
10
38
|
error: console.error,
|
|
@@ -54,28 +82,3 @@ async function emitLog(severityNumber, message) {
|
|
|
54
82
|
function emitLogSync(severityNumber, message) {
|
|
55
83
|
emitLog(severityNumber, message).catch(() => { });
|
|
56
84
|
}
|
|
57
|
-
console.debug = (message, ...args) => {
|
|
58
|
-
const msg = formatLogMessage(message, args);
|
|
59
|
-
exports.originalLogger.log(msg);
|
|
60
|
-
emitLogSync(api_logs_1.SeverityNumber.DEBUG, msg);
|
|
61
|
-
};
|
|
62
|
-
console.log = (message, ...args) => {
|
|
63
|
-
const msg = formatLogMessage(message, args);
|
|
64
|
-
exports.originalLogger.log(msg);
|
|
65
|
-
emitLogSync(api_logs_1.SeverityNumber.INFO, msg);
|
|
66
|
-
};
|
|
67
|
-
console.info = (message, ...args) => {
|
|
68
|
-
const msg = formatLogMessage(message, args);
|
|
69
|
-
exports.originalLogger.log(msg);
|
|
70
|
-
emitLogSync(api_logs_1.SeverityNumber.INFO, msg);
|
|
71
|
-
};
|
|
72
|
-
console.error = (message, ...args) => {
|
|
73
|
-
const msg = formatLogMessage(message, args);
|
|
74
|
-
exports.originalLogger.log(msg);
|
|
75
|
-
emitLogSync(api_logs_1.SeverityNumber.ERROR, msg);
|
|
76
|
-
};
|
|
77
|
-
console.warn = (message, ...args) => {
|
|
78
|
-
const msg = formatLogMessage(message, args);
|
|
79
|
-
exports.originalLogger.log(msg);
|
|
80
|
-
emitLogSync(api_logs_1.SeverityNumber.WARN, msg);
|
|
81
|
-
};
|
package/dist/telemetry.d.ts
CHANGED
|
@@ -43,7 +43,7 @@ declare class TelemetryManager {
|
|
|
43
43
|
/**
|
|
44
44
|
* Get resource attributes for OpenTelemetry.
|
|
45
45
|
*/
|
|
46
|
-
|
|
46
|
+
get resourceAttributes(): Record<string, string>;
|
|
47
47
|
/**
|
|
48
48
|
* Initialize and return the OTLP Metric Exporter.
|
|
49
49
|
*/
|
|
@@ -57,7 +57,7 @@ declare class TelemetryManager {
|
|
|
57
57
|
*/
|
|
58
58
|
getLogExporter(): OTLPLogExporter;
|
|
59
59
|
instrumentApp(): void;
|
|
60
|
-
setExporters():
|
|
60
|
+
setExporters(): void;
|
|
61
61
|
shutdownApp(): Promise<void>;
|
|
62
62
|
}
|
|
63
63
|
export declare const blaxelTelemetry: TelemetryManager;
|
package/dist/telemetry.js
CHANGED
|
@@ -14,6 +14,7 @@ const resources_1 = require("@opentelemetry/resources");
|
|
|
14
14
|
const sdk_logs_1 = require("@opentelemetry/sdk-logs");
|
|
15
15
|
const sdk_metrics_1 = require("@opentelemetry/sdk-metrics");
|
|
16
16
|
const sdk_trace_node_1 = require("@opentelemetry/sdk-trace-node");
|
|
17
|
+
const telemetry_provider_1 = require("./telemetry_provider");
|
|
17
18
|
class BlaxelResource {
|
|
18
19
|
attributes;
|
|
19
20
|
constructor(attributes) {
|
|
@@ -82,7 +83,7 @@ class TelemetryManager {
|
|
|
82
83
|
this.setupSignalHandler();
|
|
83
84
|
this.initialized = true;
|
|
84
85
|
this.setConfiguration().catch((error) => {
|
|
85
|
-
|
|
86
|
+
core_1.logger.error("Error setting configuration:", error);
|
|
86
87
|
});
|
|
87
88
|
}
|
|
88
89
|
async setConfiguration() {
|
|
@@ -90,9 +91,9 @@ class TelemetryManager {
|
|
|
90
91
|
return;
|
|
91
92
|
}
|
|
92
93
|
await (0, core_1.authenticate)();
|
|
93
|
-
|
|
94
|
+
this.setExporters();
|
|
94
95
|
this.otelLogger = api_logs_1.logs.getLogger("blaxel");
|
|
95
|
-
|
|
96
|
+
core_1.logger.debug("Telemetry ready");
|
|
96
97
|
this.configured = true;
|
|
97
98
|
}
|
|
98
99
|
get enabled() {
|
|
@@ -123,10 +124,10 @@ class TelemetryManager {
|
|
|
123
124
|
for (const signal of signals) {
|
|
124
125
|
process.on(signal, (error) => {
|
|
125
126
|
if (signal !== "exit") {
|
|
126
|
-
core_1.logger.error(error
|
|
127
|
+
core_1.logger.error(error);
|
|
127
128
|
}
|
|
128
129
|
this.shutdownApp().catch((error) => {
|
|
129
|
-
|
|
130
|
+
core_1.logger.debug("Fatal error during shutdown:", error);
|
|
130
131
|
process.exit(0);
|
|
131
132
|
});
|
|
132
133
|
});
|
|
@@ -135,8 +136,8 @@ class TelemetryManager {
|
|
|
135
136
|
/**
|
|
136
137
|
* Get resource attributes for OpenTelemetry.
|
|
137
138
|
*/
|
|
138
|
-
|
|
139
|
-
const resource =
|
|
139
|
+
get resourceAttributes() {
|
|
140
|
+
const resource = resources_1.envDetector.detect();
|
|
140
141
|
const attributes = resource.attributes || {};
|
|
141
142
|
if (core_1.settings.name) {
|
|
142
143
|
attributes["service.name"] = core_1.settings.name;
|
|
@@ -181,6 +182,7 @@ class TelemetryManager {
|
|
|
181
182
|
});
|
|
182
183
|
}
|
|
183
184
|
instrumentApp() {
|
|
185
|
+
core_1.telemetryRegistry.registerProvider(new telemetry_provider_1.OtelTelemetryProvider());
|
|
184
186
|
const httpInstrumentation = new instrumentation_http_1.HttpInstrumentation({
|
|
185
187
|
requireParentforOutgoingSpans: true,
|
|
186
188
|
});
|
|
@@ -188,8 +190,8 @@ class TelemetryManager {
|
|
|
188
190
|
instrumentations: [httpInstrumentation],
|
|
189
191
|
});
|
|
190
192
|
}
|
|
191
|
-
|
|
192
|
-
const resource = new BlaxelResource(
|
|
193
|
+
setExporters() {
|
|
194
|
+
const resource = new BlaxelResource(this.resourceAttributes);
|
|
193
195
|
const logExporter = this.getLogExporter();
|
|
194
196
|
this.loggerProvider = new sdk_logs_1.LoggerProvider({
|
|
195
197
|
resource,
|
|
@@ -234,28 +236,28 @@ class TelemetryManager {
|
|
|
234
236
|
if (this.nodeTracerProvider) {
|
|
235
237
|
shutdownPromises.push(this.nodeTracerProvider
|
|
236
238
|
.shutdown()
|
|
237
|
-
.catch((error) =>
|
|
239
|
+
.catch((error) => core_1.logger.debug("Error shutting down tracer provider:", error)));
|
|
238
240
|
}
|
|
239
241
|
if (this.meterProvider) {
|
|
240
242
|
shutdownPromises.push(this.meterProvider
|
|
241
243
|
.shutdown()
|
|
242
|
-
.catch((error) =>
|
|
244
|
+
.catch((error) => core_1.logger.debug("Error shutting down meter provider:", error)));
|
|
243
245
|
}
|
|
244
246
|
if (this.loggerProvider) {
|
|
245
247
|
shutdownPromises.push(this.loggerProvider
|
|
246
248
|
.shutdown()
|
|
247
|
-
.catch((error) =>
|
|
249
|
+
.catch((error) => core_1.logger.debug("Error shutting down logger provider:", error)));
|
|
248
250
|
}
|
|
249
251
|
// Wait for all providers to shutdown with a timeout
|
|
250
252
|
await Promise.race([
|
|
251
253
|
Promise.all(shutdownPromises),
|
|
252
254
|
new Promise((resolve) => setTimeout(resolve, 5000)), // 5 second timeout
|
|
253
255
|
]);
|
|
254
|
-
|
|
256
|
+
core_1.logger.debug("Instrumentation shutdown complete");
|
|
255
257
|
process.exit(0);
|
|
256
258
|
}
|
|
257
259
|
catch (error) {
|
|
258
|
-
|
|
260
|
+
core_1.logger.error("Error during shutdown:", error);
|
|
259
261
|
process.exit(1);
|
|
260
262
|
}
|
|
261
263
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OtelTelemetryProvider = void 0;
|
|
4
|
+
const api_1 = require("@opentelemetry/api");
|
|
5
|
+
class OtelSpan {
|
|
6
|
+
span;
|
|
7
|
+
constructor(span) {
|
|
8
|
+
this.span = span;
|
|
9
|
+
}
|
|
10
|
+
setAttribute(key, value) {
|
|
11
|
+
this.span.setAttribute(key, value);
|
|
12
|
+
}
|
|
13
|
+
setAttributes(attributes) {
|
|
14
|
+
Object.entries(attributes).forEach(([k, v]) => this.span.setAttribute(k, v));
|
|
15
|
+
}
|
|
16
|
+
recordException(error) {
|
|
17
|
+
this.span.recordException(error);
|
|
18
|
+
this.span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: error.message });
|
|
19
|
+
}
|
|
20
|
+
setStatus(status, message) {
|
|
21
|
+
this.span.setStatus({
|
|
22
|
+
code: status === 'ok' ? api_1.SpanStatusCode.OK : api_1.SpanStatusCode.ERROR,
|
|
23
|
+
message,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
end() {
|
|
27
|
+
this.span.end();
|
|
28
|
+
}
|
|
29
|
+
getContext() {
|
|
30
|
+
return this.span.spanContext();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
class OtelTelemetryProvider {
|
|
34
|
+
startSpan(name, options) {
|
|
35
|
+
// Use the tracer from the registered NodeTracerProvider
|
|
36
|
+
const tracer = api_1.trace.getTracer("blaxel");
|
|
37
|
+
// Prepare OpenTelemetry span options
|
|
38
|
+
const otelOptions = {
|
|
39
|
+
attributes: options?.attributes,
|
|
40
|
+
root: options?.isRoot,
|
|
41
|
+
};
|
|
42
|
+
// Handle parent context if provided
|
|
43
|
+
let ctx = api_1.context.active();
|
|
44
|
+
if (options?.parentContext) {
|
|
45
|
+
ctx = options.parentContext;
|
|
46
|
+
}
|
|
47
|
+
// Start the span
|
|
48
|
+
const span = tracer.startSpan(name, otelOptions, ctx);
|
|
49
|
+
return new OtelSpan(span);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.OtelTelemetryProvider = OtelTelemetryProvider;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blaxel/telemetry",
|
|
3
|
-
"version": "0.2.0-
|
|
3
|
+
"version": "0.2.0-dev11",
|
|
4
4
|
"description": "Blaxel SDK for TypeScript",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Blaxel, INC (https://blaxel.ai)",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"@opentelemetry/sdk-trace-base": "^2.0.0",
|
|
72
72
|
"@opentelemetry/sdk-trace-node": "^2.0.0",
|
|
73
73
|
"ai": "^4.3.13",
|
|
74
|
-
"@blaxel/core": "0.2.0-
|
|
74
|
+
"@blaxel/core": "0.2.0-dev11"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
77
|
"@eslint/js": "^9.26.0",
|