evergreen-sdk 1.0.0 → 1.0.2
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.js +21 -1
- package/package.json +2 -2
- package/src/index.ts +26 -1
- package/src/index.js +0 -160
package/dist/index.js
CHANGED
|
@@ -10,15 +10,35 @@ const traceStorage = new node_async_hooks_1.AsyncLocalStorage();
|
|
|
10
10
|
let ws = null;
|
|
11
11
|
let isConnected = false;
|
|
12
12
|
const pendingTelemetry = [];
|
|
13
|
+
function getTelemetryWsUrl() {
|
|
14
|
+
const explicit = process.env.EVERGREEN_TRACE_WS_URL;
|
|
15
|
+
if (explicit && explicit.trim())
|
|
16
|
+
return explicit.trim();
|
|
17
|
+
return "ws://localhost:4321";
|
|
18
|
+
}
|
|
19
|
+
function shouldDebugTelemetryConnection() {
|
|
20
|
+
return (process.env.EVERGREEN_TRACE_DEBUG === "1" ||
|
|
21
|
+
process.env.EVERGREEN_TRACE_DEBUG === "true");
|
|
22
|
+
}
|
|
13
23
|
// Connect to the VS Code Extension Hub
|
|
14
24
|
function connect() {
|
|
15
25
|
if (ws &&
|
|
16
26
|
(ws.readyState === ws_1.default.CONNECTING || ws.readyState === ws_1.default.OPEN))
|
|
17
27
|
return;
|
|
28
|
+
const url = getTelemetryWsUrl();
|
|
29
|
+
const debug = shouldDebugTelemetryConnection();
|
|
18
30
|
try {
|
|
19
|
-
|
|
31
|
+
if (debug) {
|
|
32
|
+
// eslint-disable-next-line no-console
|
|
33
|
+
console.log(`🌲 Evergreen Trace: connecting to ${url}`);
|
|
34
|
+
}
|
|
35
|
+
ws = new ws_1.default(url);
|
|
20
36
|
ws.on("open", () => {
|
|
21
37
|
isConnected = true;
|
|
38
|
+
if (debug) {
|
|
39
|
+
// eslint-disable-next-line no-console
|
|
40
|
+
console.log(`🌲 Evergreen Trace: connected to ${url}`);
|
|
41
|
+
}
|
|
22
42
|
// Flush any telemetry that was emitted before the socket became ready.
|
|
23
43
|
while (pendingTelemetry.length > 0) {
|
|
24
44
|
const queued = pendingTelemetry.shift();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "evergreen-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Evergreen Trace Backend SDK",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -17,4 +17,4 @@
|
|
|
17
17
|
"@types/ws": "^8.5.5",
|
|
18
18
|
"typescript": "^5.0.0"
|
|
19
19
|
}
|
|
20
|
-
}
|
|
20
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -23,6 +23,19 @@ let ws: WebSocket | null = null;
|
|
|
23
23
|
let isConnected = false;
|
|
24
24
|
const pendingTelemetry: any[] = [];
|
|
25
25
|
|
|
26
|
+
function getTelemetryWsUrl(): string {
|
|
27
|
+
const explicit = process.env.EVERGREEN_TRACE_WS_URL;
|
|
28
|
+
if (explicit && explicit.trim()) return explicit.trim();
|
|
29
|
+
return "ws://localhost:4321";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function shouldDebugTelemetryConnection(): boolean {
|
|
33
|
+
return (
|
|
34
|
+
process.env.EVERGREEN_TRACE_DEBUG === "1" ||
|
|
35
|
+
process.env.EVERGREEN_TRACE_DEBUG === "true"
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
26
39
|
// Connect to the VS Code Extension Hub
|
|
27
40
|
function connect() {
|
|
28
41
|
if (
|
|
@@ -31,11 +44,23 @@ function connect() {
|
|
|
31
44
|
)
|
|
32
45
|
return;
|
|
33
46
|
|
|
47
|
+
const url = getTelemetryWsUrl();
|
|
48
|
+
const debug = shouldDebugTelemetryConnection();
|
|
49
|
+
|
|
34
50
|
try {
|
|
35
|
-
|
|
51
|
+
if (debug) {
|
|
52
|
+
// eslint-disable-next-line no-console
|
|
53
|
+
console.log(`🌲 Evergreen Trace: connecting to ${url}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
ws = new WebSocket(url);
|
|
36
57
|
|
|
37
58
|
ws.on("open", () => {
|
|
38
59
|
isConnected = true;
|
|
60
|
+
if (debug) {
|
|
61
|
+
// eslint-disable-next-line no-console
|
|
62
|
+
console.log(`🌲 Evergreen Trace: connected to ${url}`);
|
|
63
|
+
}
|
|
39
64
|
// Flush any telemetry that was emitted before the socket became ready.
|
|
40
65
|
while (pendingTelemetry.length > 0) {
|
|
41
66
|
const queued = pendingTelemetry.shift();
|
package/src/index.js
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.evergreen = void 0;
|
|
4
|
-
const node_async_hooks_1 = require("node:async_hooks");
|
|
5
|
-
const ws_1 = require("ws");
|
|
6
|
-
const traceStorage = new node_async_hooks_1.AsyncLocalStorage();
|
|
7
|
-
let ws = null;
|
|
8
|
-
let isConnected = false;
|
|
9
|
-
// Connect to the VS Code Extension Hub
|
|
10
|
-
function connect() {
|
|
11
|
-
if (ws && (ws.readyState === ws_1.default.CONNECTING || ws.readyState === ws_1.default.OPEN))
|
|
12
|
-
return;
|
|
13
|
-
try {
|
|
14
|
-
ws = new ws_1.default('ws://localhost:3000');
|
|
15
|
-
ws.on('open', () => {
|
|
16
|
-
isConnected = true;
|
|
17
|
-
});
|
|
18
|
-
ws.on('close', () => {
|
|
19
|
-
isConnected = false;
|
|
20
|
-
// Optionally implement reconnect logic here
|
|
21
|
-
});
|
|
22
|
-
ws.on('error', () => {
|
|
23
|
-
isConnected = false;
|
|
24
|
-
// Silently fail if extension is not running
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
catch (error) {
|
|
28
|
-
isConnected = false;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
// Ensure connection is attempted
|
|
32
|
-
connect();
|
|
33
|
-
function sanitizePayload(obj, maxDepth = 3) {
|
|
34
|
-
if (maxDepth <= 0)
|
|
35
|
-
return '[Max Depth Reached]';
|
|
36
|
-
if (obj === null || obj === undefined)
|
|
37
|
-
return obj;
|
|
38
|
-
if (typeof obj !== 'object')
|
|
39
|
-
return obj;
|
|
40
|
-
if (obj instanceof Error) {
|
|
41
|
-
return { name: obj.name, message: obj.message, stack: obj.stack };
|
|
42
|
-
}
|
|
43
|
-
if (Array.isArray(obj)) {
|
|
44
|
-
return obj.map(item => sanitizePayload(item, maxDepth - 1));
|
|
45
|
-
}
|
|
46
|
-
const sanitized = {};
|
|
47
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
48
|
-
sanitized[key] = sanitizePayload(value, maxDepth - 1);
|
|
49
|
-
}
|
|
50
|
-
return sanitized;
|
|
51
|
-
}
|
|
52
|
-
function sendTelemetry(payload) {
|
|
53
|
-
if (!isConnected || !ws || ws.readyState !== ws_1.default.OPEN)
|
|
54
|
-
return;
|
|
55
|
-
try {
|
|
56
|
-
ws.send(JSON.stringify(payload));
|
|
57
|
-
}
|
|
58
|
-
catch (error) {
|
|
59
|
-
// Fire and forget, silently ignore send errors
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
function extractLocationFromStack(stack) {
|
|
63
|
-
if (!stack)
|
|
64
|
-
return undefined;
|
|
65
|
-
// The stack will look like:
|
|
66
|
-
// Error
|
|
67
|
-
// at Object.step (/path/to/sdk/index.ts:...)
|
|
68
|
-
// at myBusinessLogic (/path/to/business/logic.ts:42:15)
|
|
69
|
-
const lines = stack.split('\n');
|
|
70
|
-
if (lines.length >= 3) {
|
|
71
|
-
const callerLine = lines[2];
|
|
72
|
-
const match = callerLine.match(/\((.+):(\d+):\d+\)/) || callerLine.match(/at (.+):(\d+):\d+/);
|
|
73
|
-
if (match && match.length >= 3) {
|
|
74
|
-
return { file: match[1], line: parseInt(match[2], 10) };
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return undefined;
|
|
78
|
-
}
|
|
79
|
-
exports.evergreen = {
|
|
80
|
-
clear() {
|
|
81
|
-
sendTelemetry({ type: 'CLEAR' });
|
|
82
|
-
},
|
|
83
|
-
run(jobId, fn) {
|
|
84
|
-
return traceStorage.run({ jobId }, fn);
|
|
85
|
-
},
|
|
86
|
-
async step(name, fn, options) {
|
|
87
|
-
const store = traceStorage.getStore();
|
|
88
|
-
// If no context, just execute the function normally without tracing
|
|
89
|
-
if (!store) {
|
|
90
|
-
return await fn();
|
|
91
|
-
}
|
|
92
|
-
const { jobId, previousStep } = store;
|
|
93
|
-
// Capture location using a throwaway error
|
|
94
|
-
const locationError = new Error();
|
|
95
|
-
const location = extractLocationFromStack(locationError.stack) || { file: 'unknown', line: 0 };
|
|
96
|
-
const timestamp = Date.now();
|
|
97
|
-
const startTime = process.hrtime.bigint();
|
|
98
|
-
// Emit NODE_STARTED
|
|
99
|
-
sendTelemetry({
|
|
100
|
-
event: 'NODE_STARTED',
|
|
101
|
-
status: 'PENDING',
|
|
102
|
-
jobId,
|
|
103
|
-
stepName: name,
|
|
104
|
-
previousStep,
|
|
105
|
-
location,
|
|
106
|
-
timestamp
|
|
107
|
-
});
|
|
108
|
-
// We must ensure subsequent nested steps know this step is their parent.
|
|
109
|
-
// To do this, we run the inner function in a nested context.
|
|
110
|
-
const newContext = { ...store, previousStep: name };
|
|
111
|
-
try {
|
|
112
|
-
// Execute the business logic within the new nested context
|
|
113
|
-
const result = await traceStorage.run(newContext, async () => {
|
|
114
|
-
return await fn();
|
|
115
|
-
});
|
|
116
|
-
const endTime = process.hrtime.bigint();
|
|
117
|
-
const durationMs = Number(endTime - startTime) / 1000000;
|
|
118
|
-
// Emit NODE_COMPLETED
|
|
119
|
-
sendTelemetry({
|
|
120
|
-
event: 'NODE_COMPLETED',
|
|
121
|
-
status: 'SUCCESS',
|
|
122
|
-
jobId,
|
|
123
|
-
stepName: name,
|
|
124
|
-
previousStep,
|
|
125
|
-
payload: { input: sanitizePayload(options?.input), output: sanitizePayload(result) },
|
|
126
|
-
context: {
|
|
127
|
-
input: sanitizePayload(options?.input),
|
|
128
|
-
output: sanitizePayload(result),
|
|
129
|
-
duration: durationMs
|
|
130
|
-
},
|
|
131
|
-
location,
|
|
132
|
-
timestamp: Date.now()
|
|
133
|
-
});
|
|
134
|
-
return result;
|
|
135
|
-
}
|
|
136
|
-
catch (error) {
|
|
137
|
-
const endTime = process.hrtime.bigint();
|
|
138
|
-
const durationMs = Number(endTime - startTime) / 1000000;
|
|
139
|
-
// Emit NODE_FAILED
|
|
140
|
-
sendTelemetry({
|
|
141
|
-
event: 'NODE_FAILED',
|
|
142
|
-
status: 'FAILED',
|
|
143
|
-
jobId,
|
|
144
|
-
stepName: name,
|
|
145
|
-
previousStep,
|
|
146
|
-
payload: { input: sanitizePayload(options?.input), error: sanitizePayload(error) },
|
|
147
|
-
context: {
|
|
148
|
-
input: sanitizePayload(options?.input),
|
|
149
|
-
error: sanitizePayload(error),
|
|
150
|
-
duration: durationMs
|
|
151
|
-
},
|
|
152
|
-
location,
|
|
153
|
-
timestamp: Date.now()
|
|
154
|
-
});
|
|
155
|
-
// Re-throw so backend logic behaves normally
|
|
156
|
-
throw error;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
//# sourceMappingURL=index.js.map
|