@remote-logger/sdk 0.1.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 +177 -0
- package/SKILL.md +121 -0
- package/dist/index.d.mts +130 -0
- package/dist/index.d.ts +130 -0
- package/dist/index.js +548 -0
- package/dist/index.mjs +522 -0
- package/dist/init.js +34 -0
- package/package.json +38 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var LEVEL_ORDER = {
|
|
3
|
+
DEBUG: 0,
|
|
4
|
+
INFO: 1,
|
|
5
|
+
WARN: 2,
|
|
6
|
+
ERROR: 3,
|
|
7
|
+
FATAL: 4
|
|
8
|
+
};
|
|
9
|
+
var SDK_VERSION = "0.1.0";
|
|
10
|
+
var DEFAULT_BASE_URL = "https://log.terodato.com";
|
|
11
|
+
var BATCH_SIZE = 50;
|
|
12
|
+
var MAX_BUFFER_SIZE = 1e3;
|
|
13
|
+
var FLUSH_INTERVAL_MS = 1e3;
|
|
14
|
+
var FETCH_TIMEOUT_MS = 1e4;
|
|
15
|
+
var batchCounter = 0;
|
|
16
|
+
var generateBatchId = () => `${Date.now()}-${++batchCounter}`;
|
|
17
|
+
var originalConsole = {
|
|
18
|
+
log: console.log.bind(console),
|
|
19
|
+
info: console.info.bind(console),
|
|
20
|
+
warn: console.warn.bind(console),
|
|
21
|
+
error: console.error.bind(console),
|
|
22
|
+
debug: console.debug.bind(console)
|
|
23
|
+
};
|
|
24
|
+
function resolveLevel(level) {
|
|
25
|
+
if (!level) return "DEBUG";
|
|
26
|
+
if (level in LEVEL_ORDER) return level;
|
|
27
|
+
return "INFO";
|
|
28
|
+
}
|
|
29
|
+
function safeStringify(value) {
|
|
30
|
+
try {
|
|
31
|
+
return JSON.stringify(value);
|
|
32
|
+
} catch {
|
|
33
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
34
|
+
return JSON.stringify(value, (_key, val) => {
|
|
35
|
+
if (typeof val === "object" && val !== null) {
|
|
36
|
+
if (seen.has(val)) return "[Circular]";
|
|
37
|
+
seen.add(val);
|
|
38
|
+
}
|
|
39
|
+
return val;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
var _Logger = class _Logger {
|
|
44
|
+
constructor(config) {
|
|
45
|
+
this.buffer = [];
|
|
46
|
+
this.flushTimer = null;
|
|
47
|
+
this.flushPromise = null;
|
|
48
|
+
this.consoleIntercepted = false;
|
|
49
|
+
this.httpBackoffMs = 0;
|
|
50
|
+
this.maxBackoffMs = 6e4;
|
|
51
|
+
this.consecutiveHttpFailures = 0;
|
|
52
|
+
// Error capture via window event listeners (no console.error wrapping to
|
|
53
|
+
// avoid polluting stack traces shown in Next.js / React error overlays)
|
|
54
|
+
this._onError = null;
|
|
55
|
+
this._onUnhandledRejection = null;
|
|
56
|
+
// WebSocket state
|
|
57
|
+
this.ws = null;
|
|
58
|
+
this.wsState = "disconnected";
|
|
59
|
+
this.wsReconnectTimer = null;
|
|
60
|
+
this.wsReconnectAttempts = 0;
|
|
61
|
+
this.maxWsReconnectAttempts = 5;
|
|
62
|
+
// Browser lifecycle hooks for best-effort flush on page unload
|
|
63
|
+
this._onVisibilityChange = null;
|
|
64
|
+
this._onBeforeUnload = null;
|
|
65
|
+
this.config = {
|
|
66
|
+
logId: config.logId,
|
|
67
|
+
apiKey: config.apiKey,
|
|
68
|
+
group: config.group,
|
|
69
|
+
level: resolveLevel(config.level),
|
|
70
|
+
onError: config.onError,
|
|
71
|
+
traceId: config.traceId,
|
|
72
|
+
silent: config.silent ?? false,
|
|
73
|
+
debug: config.debug ?? false
|
|
74
|
+
};
|
|
75
|
+
this.proxyMode = !!config._ingestUrl;
|
|
76
|
+
const baseUrl = config._endpoint ?? DEFAULT_BASE_URL;
|
|
77
|
+
this.baseUrl = baseUrl;
|
|
78
|
+
const wsBaseUrl = config._wsEndpoint ?? baseUrl;
|
|
79
|
+
this.httpIngestUrl = config._ingestUrl ?? `${baseUrl}/ingest/http`;
|
|
80
|
+
this.wsIngestUrl = `${wsBaseUrl.replace(/^http/, "ws")}/ingest/ws`;
|
|
81
|
+
this.startFlushTimer();
|
|
82
|
+
if (!this.proxyMode && typeof WebSocket !== "undefined") {
|
|
83
|
+
this.connectWebSocket();
|
|
84
|
+
} else if (this.proxyMode) {
|
|
85
|
+
this.debugLog("Proxy mode enabled, using HTTP transport only");
|
|
86
|
+
} else {
|
|
87
|
+
this.debugLog("WebSocket not available, using HTTP transport only");
|
|
88
|
+
}
|
|
89
|
+
this.registerLifecycleHooks();
|
|
90
|
+
}
|
|
91
|
+
debugLog(msg, ...args) {
|
|
92
|
+
if (this.config.debug) {
|
|
93
|
+
originalConsole.debug("[RemoteLogger]", msg, ...args);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
connectWebSocket() {
|
|
97
|
+
if (this.wsState === "connecting" || this.wsState === "connected") {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
this.wsState = "connecting";
|
|
101
|
+
this.debugLog("WebSocket connecting to", this.wsIngestUrl);
|
|
102
|
+
try {
|
|
103
|
+
this.ws = new WebSocket(this.wsIngestUrl, ["api_key", this.config.apiKey]);
|
|
104
|
+
this.ws.onopen = () => {
|
|
105
|
+
this.wsReconnectAttempts = 0;
|
|
106
|
+
this.wsState = "connected";
|
|
107
|
+
this.debugLog("WebSocket connected");
|
|
108
|
+
};
|
|
109
|
+
this.ws.onmessage = (event) => {
|
|
110
|
+
try {
|
|
111
|
+
const msg = JSON.parse(event.data);
|
|
112
|
+
if (!msg.success) {
|
|
113
|
+
originalConsole.error("[RemoteLogger] WebSocket log ingestion failed:", msg.error);
|
|
114
|
+
}
|
|
115
|
+
} catch {
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
this.ws.onerror = () => {
|
|
119
|
+
};
|
|
120
|
+
this.ws.onclose = () => {
|
|
121
|
+
this.wsState = "disconnected";
|
|
122
|
+
this.ws = null;
|
|
123
|
+
if (this.wsReconnectAttempts < this.maxWsReconnectAttempts) {
|
|
124
|
+
const delay = Math.min(1e3 * Math.pow(2, this.wsReconnectAttempts), 3e4);
|
|
125
|
+
this.wsReconnectAttempts++;
|
|
126
|
+
this.debugLog(`WebSocket disconnected, reconnecting in ${delay}ms (attempt ${this.wsReconnectAttempts}/${this.maxWsReconnectAttempts})`);
|
|
127
|
+
this.wsReconnectTimer = setTimeout(() => this.connectWebSocket(), delay);
|
|
128
|
+
} else {
|
|
129
|
+
this.debugLog("WebSocket max reconnect attempts reached, using HTTP only");
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
} catch {
|
|
133
|
+
this.wsState = "failed";
|
|
134
|
+
this.debugLog("WebSocket connection failed");
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
startFlushTimer() {
|
|
138
|
+
if (this.flushTimer) {
|
|
139
|
+
clearInterval(this.flushTimer);
|
|
140
|
+
}
|
|
141
|
+
this.flushTimer = setInterval(() => {
|
|
142
|
+
this.flush();
|
|
143
|
+
}, FLUSH_INTERVAL_MS);
|
|
144
|
+
}
|
|
145
|
+
shouldLog(level) {
|
|
146
|
+
return LEVEL_ORDER[level] >= LEVEL_ORDER[this.config.level];
|
|
147
|
+
}
|
|
148
|
+
formatArgs(args) {
|
|
149
|
+
return args.map((arg) => {
|
|
150
|
+
if (typeof arg === "string") return arg;
|
|
151
|
+
if (arg instanceof Error) return `${arg.message}
|
|
152
|
+
${arg.stack}`;
|
|
153
|
+
try {
|
|
154
|
+
return JSON.stringify(arg, null, 2);
|
|
155
|
+
} catch {
|
|
156
|
+
return String(arg);
|
|
157
|
+
}
|
|
158
|
+
}).join(" ");
|
|
159
|
+
}
|
|
160
|
+
log(level, message, options) {
|
|
161
|
+
if (!this.shouldLog(level)) return;
|
|
162
|
+
if (!this.config.silent) {
|
|
163
|
+
const method = _Logger.CONSOLE_METHOD[level];
|
|
164
|
+
const consoleArgs = [message];
|
|
165
|
+
if (options?.metadata && Object.keys(options.metadata).length > 0) {
|
|
166
|
+
consoleArgs.push(options.metadata);
|
|
167
|
+
}
|
|
168
|
+
if (options?.stack) {
|
|
169
|
+
consoleArgs.push("\n" + options.stack);
|
|
170
|
+
}
|
|
171
|
+
originalConsole[method](...consoleArgs);
|
|
172
|
+
}
|
|
173
|
+
const entry = {
|
|
174
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
175
|
+
level,
|
|
176
|
+
message,
|
|
177
|
+
logId: this.config.logId,
|
|
178
|
+
group: options?.group ?? this.config.group,
|
|
179
|
+
trace_id: options?.traceId ?? this.config.traceId,
|
|
180
|
+
stack: options?.stack,
|
|
181
|
+
error_type: options?.errorType,
|
|
182
|
+
metadata: options?.metadata
|
|
183
|
+
};
|
|
184
|
+
this.buffer.push(entry);
|
|
185
|
+
if (this.buffer.length > MAX_BUFFER_SIZE) {
|
|
186
|
+
const dropped = this.buffer.length - MAX_BUFFER_SIZE;
|
|
187
|
+
this.buffer.splice(0, dropped);
|
|
188
|
+
this.debugLog(`Buffer full, dropped ${dropped} oldest logs`);
|
|
189
|
+
}
|
|
190
|
+
if (this.buffer.length >= BATCH_SIZE) {
|
|
191
|
+
this.flush();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
parseArgs(msgOrObj, metadata) {
|
|
195
|
+
if (typeof msgOrObj === "object") {
|
|
196
|
+
return {
|
|
197
|
+
message: msgOrObj.message,
|
|
198
|
+
options: { traceId: msgOrObj.traceId, group: msgOrObj.group, metadata: msgOrObj.metadata }
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
if (metadata !== void 0) {
|
|
202
|
+
return { message: msgOrObj, options: { metadata } };
|
|
203
|
+
}
|
|
204
|
+
return { message: msgOrObj };
|
|
205
|
+
}
|
|
206
|
+
debug(msgOrObj, metadata) {
|
|
207
|
+
const { message, options } = this.parseArgs(msgOrObj, metadata);
|
|
208
|
+
this.log("DEBUG", message, options);
|
|
209
|
+
}
|
|
210
|
+
info(msgOrObj, metadata) {
|
|
211
|
+
const { message, options } = this.parseArgs(msgOrObj, metadata);
|
|
212
|
+
this.log("INFO", message, options);
|
|
213
|
+
}
|
|
214
|
+
warn(msgOrObj, metadata) {
|
|
215
|
+
const { message, options } = this.parseArgs(msgOrObj, metadata);
|
|
216
|
+
this.log("WARN", message, options);
|
|
217
|
+
}
|
|
218
|
+
error(msgOrObj, metadata) {
|
|
219
|
+
if (msgOrObj instanceof Error) {
|
|
220
|
+
this.log("ERROR", msgOrObj.message, {
|
|
221
|
+
metadata,
|
|
222
|
+
stack: msgOrObj.stack,
|
|
223
|
+
errorType: msgOrObj.name
|
|
224
|
+
});
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const { message, options } = this.parseArgs(msgOrObj, metadata);
|
|
228
|
+
this.log("ERROR", message, options);
|
|
229
|
+
}
|
|
230
|
+
fatal(msgOrObj, metadata) {
|
|
231
|
+
if (msgOrObj instanceof Error) {
|
|
232
|
+
this.log("FATAL", msgOrObj.message, {
|
|
233
|
+
metadata,
|
|
234
|
+
stack: msgOrObj.stack,
|
|
235
|
+
errorType: msgOrObj.name
|
|
236
|
+
});
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const { message, options } = this.parseArgs(msgOrObj, metadata);
|
|
240
|
+
this.log("FATAL", message, options);
|
|
241
|
+
}
|
|
242
|
+
withTraceId(traceId) {
|
|
243
|
+
return new TraceLogger(this, traceId);
|
|
244
|
+
}
|
|
245
|
+
withGroup(group) {
|
|
246
|
+
return new GroupLogger(this, group);
|
|
247
|
+
}
|
|
248
|
+
setTraceId(traceId) {
|
|
249
|
+
this.config.traceId = traceId;
|
|
250
|
+
}
|
|
251
|
+
setGroup(group) {
|
|
252
|
+
this.config.group = group;
|
|
253
|
+
}
|
|
254
|
+
interceptConsole(group) {
|
|
255
|
+
if (this.consoleIntercepted) return;
|
|
256
|
+
this.consoleIntercepted = true;
|
|
257
|
+
const self = this;
|
|
258
|
+
const logGroup = group ?? "console";
|
|
259
|
+
const wrap = (original, level) => {
|
|
260
|
+
const wrapped = (...args) => {
|
|
261
|
+
queueMicrotask(() => {
|
|
262
|
+
self.log(level, self.formatArgs(args), { group: logGroup });
|
|
263
|
+
});
|
|
264
|
+
return original.apply(console, args);
|
|
265
|
+
};
|
|
266
|
+
Object.defineProperty(wrapped, "name", { value: original.name });
|
|
267
|
+
return wrapped;
|
|
268
|
+
};
|
|
269
|
+
console.debug = wrap(originalConsole.debug, "DEBUG");
|
|
270
|
+
console.log = wrap(originalConsole.log, "INFO");
|
|
271
|
+
console.info = wrap(originalConsole.info, "INFO");
|
|
272
|
+
console.warn = wrap(originalConsole.warn, "WARN");
|
|
273
|
+
if (typeof window !== "undefined") {
|
|
274
|
+
this._onError = (event) => {
|
|
275
|
+
self.log("ERROR", event.message, {
|
|
276
|
+
group: logGroup,
|
|
277
|
+
stack: event.error?.stack,
|
|
278
|
+
errorType: event.error?.name
|
|
279
|
+
});
|
|
280
|
+
};
|
|
281
|
+
this._onUnhandledRejection = (event) => {
|
|
282
|
+
const err = event.reason;
|
|
283
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
284
|
+
self.log("ERROR", message, {
|
|
285
|
+
group: logGroup,
|
|
286
|
+
stack: err instanceof Error ? err.stack : void 0,
|
|
287
|
+
errorType: err instanceof Error ? err.name : "UnhandledRejection"
|
|
288
|
+
});
|
|
289
|
+
};
|
|
290
|
+
window.addEventListener("error", this._onError);
|
|
291
|
+
window.addEventListener("unhandledrejection", this._onUnhandledRejection);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
restoreConsole() {
|
|
295
|
+
if (!this.consoleIntercepted) return;
|
|
296
|
+
this.consoleIntercepted = false;
|
|
297
|
+
console.debug = originalConsole.debug;
|
|
298
|
+
console.log = originalConsole.log;
|
|
299
|
+
console.info = originalConsole.info;
|
|
300
|
+
console.warn = originalConsole.warn;
|
|
301
|
+
if (typeof window !== "undefined") {
|
|
302
|
+
if (this._onError) {
|
|
303
|
+
window.removeEventListener("error", this._onError);
|
|
304
|
+
this._onError = null;
|
|
305
|
+
}
|
|
306
|
+
if (this._onUnhandledRejection) {
|
|
307
|
+
window.removeEventListener("unhandledrejection", this._onUnhandledRejection);
|
|
308
|
+
this._onUnhandledRejection = null;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
async flush() {
|
|
313
|
+
if (this.flushPromise) {
|
|
314
|
+
await this.flushPromise;
|
|
315
|
+
}
|
|
316
|
+
if (this.buffer.length === 0) return;
|
|
317
|
+
const logs = this.buffer.splice(0, this.buffer.length);
|
|
318
|
+
this.flushPromise = this.doFlush(logs);
|
|
319
|
+
try {
|
|
320
|
+
await this.flushPromise;
|
|
321
|
+
} finally {
|
|
322
|
+
this.flushPromise = null;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
async doFlush(logs) {
|
|
326
|
+
const batchId = generateBatchId();
|
|
327
|
+
if (this.wsState === "connected" && typeof WebSocket !== "undefined") {
|
|
328
|
+
const sent = this.sendViaWebSocket(logs, batchId);
|
|
329
|
+
if (sent) return;
|
|
330
|
+
}
|
|
331
|
+
await this.sendViaHttp(logs, batchId);
|
|
332
|
+
}
|
|
333
|
+
sendViaWebSocket(logs, batchId) {
|
|
334
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
try {
|
|
338
|
+
this.ws.send(safeStringify({ type: "logs", batchId, logs }));
|
|
339
|
+
this.debugLog(`Flushed ${logs.length} logs via WebSocket (batch ${batchId})`);
|
|
340
|
+
return true;
|
|
341
|
+
} catch {
|
|
342
|
+
this.debugLog("WebSocket send failed, falling back to HTTP");
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
async sendViaHttp(logs, batchId) {
|
|
347
|
+
if (this.httpBackoffMs > 0) {
|
|
348
|
+
await new Promise((resolve) => setTimeout(resolve, this.httpBackoffMs));
|
|
349
|
+
}
|
|
350
|
+
const controller = new AbortController();
|
|
351
|
+
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
352
|
+
try {
|
|
353
|
+
const headers = {
|
|
354
|
+
"Content-Type": "application/json",
|
|
355
|
+
"X-Batch-Id": batchId,
|
|
356
|
+
"X-SDK-Version": SDK_VERSION
|
|
357
|
+
};
|
|
358
|
+
if (this.config.apiKey) {
|
|
359
|
+
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
360
|
+
}
|
|
361
|
+
const response = await fetch(this.httpIngestUrl, {
|
|
362
|
+
method: "POST",
|
|
363
|
+
headers,
|
|
364
|
+
body: safeStringify(logs),
|
|
365
|
+
signal: controller.signal
|
|
366
|
+
});
|
|
367
|
+
if (!response.ok) {
|
|
368
|
+
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
369
|
+
throw new Error(error.error || `HTTP ${response.status}`);
|
|
370
|
+
}
|
|
371
|
+
this.httpBackoffMs = 0;
|
|
372
|
+
this.consecutiveHttpFailures = 0;
|
|
373
|
+
this.debugLog(`Flushed ${logs.length} logs via HTTP (batch ${batchId})`);
|
|
374
|
+
} catch (err) {
|
|
375
|
+
this.buffer.unshift(...logs);
|
|
376
|
+
this.consecutiveHttpFailures++;
|
|
377
|
+
this.httpBackoffMs = Math.min(
|
|
378
|
+
Math.pow(2, this.consecutiveHttpFailures - 1) * 1e3,
|
|
379
|
+
this.maxBackoffMs
|
|
380
|
+
);
|
|
381
|
+
this.debugLog(`HTTP flush failed (retry in ${this.httpBackoffMs / 1e3}s):`, err);
|
|
382
|
+
if (this.config.onError) {
|
|
383
|
+
this.config.onError(err, logs);
|
|
384
|
+
} else {
|
|
385
|
+
originalConsole.error(
|
|
386
|
+
`[RemoteLogger] Failed to flush logs (retry in ${this.httpBackoffMs / 1e3}s):`,
|
|
387
|
+
err
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
} finally {
|
|
391
|
+
clearTimeout(timeout);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
/** Fire-and-forget flush using fetch with keepalive (works during page unload) */
|
|
395
|
+
flushSync() {
|
|
396
|
+
if (this.buffer.length === 0) return;
|
|
397
|
+
const logs = this.buffer.splice(0, this.buffer.length);
|
|
398
|
+
try {
|
|
399
|
+
const headers = {
|
|
400
|
+
"Content-Type": "application/json",
|
|
401
|
+
"X-Batch-Id": generateBatchId(),
|
|
402
|
+
"X-SDK-Version": SDK_VERSION
|
|
403
|
+
};
|
|
404
|
+
if (this.config.apiKey) {
|
|
405
|
+
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
406
|
+
}
|
|
407
|
+
fetch(this.httpIngestUrl, {
|
|
408
|
+
method: "POST",
|
|
409
|
+
headers,
|
|
410
|
+
body: safeStringify(logs),
|
|
411
|
+
keepalive: true
|
|
412
|
+
});
|
|
413
|
+
} catch {
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
registerLifecycleHooks() {
|
|
417
|
+
if (typeof window !== "undefined") {
|
|
418
|
+
this._onVisibilityChange = () => {
|
|
419
|
+
if (document.visibilityState === "hidden") {
|
|
420
|
+
this.flushSync();
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
document.addEventListener("visibilitychange", this._onVisibilityChange);
|
|
424
|
+
this._onBeforeUnload = () => {
|
|
425
|
+
this.flushSync();
|
|
426
|
+
};
|
|
427
|
+
window.addEventListener("beforeunload", this._onBeforeUnload);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/** Check server connectivity and API key validity */
|
|
431
|
+
async ping() {
|
|
432
|
+
const start = Date.now();
|
|
433
|
+
const controller = new AbortController();
|
|
434
|
+
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
435
|
+
try {
|
|
436
|
+
const headers = {};
|
|
437
|
+
if (this.config.apiKey) {
|
|
438
|
+
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
439
|
+
}
|
|
440
|
+
const response = await fetch(`${this.baseUrl}/health`, {
|
|
441
|
+
headers,
|
|
442
|
+
signal: controller.signal
|
|
443
|
+
});
|
|
444
|
+
const latencyMs = Date.now() - start;
|
|
445
|
+
if (!response.ok) {
|
|
446
|
+
const body = await response.json().catch(() => ({ error: `HTTP ${response.status}` }));
|
|
447
|
+
return { ok: false, latencyMs, error: body.error || `HTTP ${response.status}` };
|
|
448
|
+
}
|
|
449
|
+
return { ok: true, latencyMs };
|
|
450
|
+
} catch (err) {
|
|
451
|
+
return { ok: false, latencyMs: Date.now() - start, error: err.message };
|
|
452
|
+
} finally {
|
|
453
|
+
clearTimeout(timeout);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
/** Get current connection status */
|
|
457
|
+
getConnectionStatus() {
|
|
458
|
+
if (this.wsState === "connected") {
|
|
459
|
+
return { transport: "websocket", state: "connected" };
|
|
460
|
+
}
|
|
461
|
+
return { transport: "http", state: "connected" };
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
_Logger.CONSOLE_METHOD = {
|
|
465
|
+
DEBUG: "debug",
|
|
466
|
+
INFO: "info",
|
|
467
|
+
WARN: "warn",
|
|
468
|
+
ERROR: "error",
|
|
469
|
+
FATAL: "error"
|
|
470
|
+
};
|
|
471
|
+
var Logger = _Logger;
|
|
472
|
+
var TraceLogger = class {
|
|
473
|
+
constructor(parent, traceId) {
|
|
474
|
+
this.parent = parent;
|
|
475
|
+
this.traceId = traceId;
|
|
476
|
+
}
|
|
477
|
+
debug(message, metadata) {
|
|
478
|
+
this.parent.debug({ message, traceId: this.traceId, metadata });
|
|
479
|
+
}
|
|
480
|
+
info(message, metadata) {
|
|
481
|
+
this.parent.info({ message, traceId: this.traceId, metadata });
|
|
482
|
+
}
|
|
483
|
+
warn(message, metadata) {
|
|
484
|
+
this.parent.warn({ message, traceId: this.traceId, metadata });
|
|
485
|
+
}
|
|
486
|
+
error(message, metadata) {
|
|
487
|
+
this.parent.error({ message, traceId: this.traceId, metadata });
|
|
488
|
+
}
|
|
489
|
+
fatal(message, metadata) {
|
|
490
|
+
this.parent.fatal({ message, traceId: this.traceId, metadata });
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
var GroupLogger = class {
|
|
494
|
+
constructor(parent, group) {
|
|
495
|
+
this.parent = parent;
|
|
496
|
+
this.group = group;
|
|
497
|
+
}
|
|
498
|
+
debug(message, metadata) {
|
|
499
|
+
this.parent.debug({ message, group: this.group, metadata });
|
|
500
|
+
}
|
|
501
|
+
info(message, metadata) {
|
|
502
|
+
this.parent.info({ message, group: this.group, metadata });
|
|
503
|
+
}
|
|
504
|
+
warn(message, metadata) {
|
|
505
|
+
this.parent.warn({ message, group: this.group, metadata });
|
|
506
|
+
}
|
|
507
|
+
error(message, metadata) {
|
|
508
|
+
this.parent.error({ message, group: this.group, metadata });
|
|
509
|
+
}
|
|
510
|
+
fatal(message, metadata) {
|
|
511
|
+
this.parent.fatal({ message, group: this.group, metadata });
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
function createLogger(config) {
|
|
515
|
+
return new Logger(config);
|
|
516
|
+
}
|
|
517
|
+
var index_default = createLogger;
|
|
518
|
+
export {
|
|
519
|
+
Logger,
|
|
520
|
+
createLogger,
|
|
521
|
+
index_default as default
|
|
522
|
+
};
|
package/dist/init.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// src/init.ts
|
|
5
|
+
var import_fs = require("fs");
|
|
6
|
+
var import_path = require("path");
|
|
7
|
+
var CLAUDE_MD = "CLAUDE.md";
|
|
8
|
+
var CURSORRULES = ".cursorrules";
|
|
9
|
+
var SKILL_REF = "> See node_modules/@remote-logger/sdk/SKILL.md for the @remote-logger/sdk logging workflow.";
|
|
10
|
+
var cwd = process.cwd();
|
|
11
|
+
function appendIfMissing(filename) {
|
|
12
|
+
const filepath = (0, import_path.resolve)(cwd, filename);
|
|
13
|
+
const marker = "node_modules/@remote-logger/sdk/SKILL.md";
|
|
14
|
+
let content = "";
|
|
15
|
+
if ((0, import_fs.existsSync)(filepath)) {
|
|
16
|
+
content = (0, import_fs.readFileSync)(filepath, "utf-8");
|
|
17
|
+
if (content.includes(marker)) {
|
|
18
|
+
console.log(` ${filename}: already configured, skipping`);
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const separator = content.length > 0 && !content.endsWith("\n") ? "\n\n" : content.length > 0 ? "\n" : "";
|
|
23
|
+
(0, import_fs.writeFileSync)(filepath, content + separator + SKILL_REF + "\n", "utf-8");
|
|
24
|
+
console.log(` ${filename}: ${content.length > 0 ? "updated" : "created"}`);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
console.log("\n@remote-logger/sdk init\n");
|
|
28
|
+
var claudeUpdated = appendIfMissing(CLAUDE_MD);
|
|
29
|
+
var cursorUpdated = appendIfMissing(CURSORRULES);
|
|
30
|
+
if (!claudeUpdated && !cursorUpdated) {
|
|
31
|
+
console.log("\nAlready set up \u2014 nothing to do.\n");
|
|
32
|
+
} else {
|
|
33
|
+
console.log("\nDone. Your AI assistant now has access to the Remote Logger workflow guide.\n");
|
|
34
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@remote-logger/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "JavaScript SDK for Remote Logger",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"import": "./dist/index.mjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"remote-logger-init": "dist/init.js"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"SKILL.md"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup src/index.ts --format cjs,esm --dts && tsup src/init.ts --format cjs --no-dts",
|
|
24
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
25
|
+
"test": "tsx test/test.ts"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"tsup": "^8.0.0",
|
|
29
|
+
"tsx": "^4.7.0",
|
|
30
|
+
"typescript": "^5.3.0"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"logging",
|
|
34
|
+
"remote-logger",
|
|
35
|
+
"observability"
|
|
36
|
+
],
|
|
37
|
+
"license": "MIT"
|
|
38
|
+
}
|