@runtimescope/server-sdk 0.6.1 → 0.7.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/index.cjs CHANGED
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ HttpTransport: () => HttpTransport,
33
34
  RuntimeScope: () => RuntimeScope,
34
35
  Sampler: () => Sampler,
35
36
  _log: () => _log,
@@ -176,6 +177,94 @@ var ServerTransport = class {
176
177
  }
177
178
  };
178
179
 
180
+ // src/http-transport.ts
181
+ var HttpTransport = class {
182
+ queue = [];
183
+ url;
184
+ sessionId;
185
+ appName;
186
+ sdkVersion;
187
+ authToken;
188
+ maxQueueSize;
189
+ maxRetries;
190
+ retryDelayMs;
191
+ flushTimer = null;
192
+ _droppedCount = 0;
193
+ sessionRegistered = false;
194
+ constructor(options) {
195
+ this.url = options.url;
196
+ this.sessionId = options.sessionId;
197
+ this.appName = options.appName;
198
+ this.sdkVersion = options.sdkVersion;
199
+ this.authToken = options.authToken;
200
+ this.maxQueueSize = options.maxQueueSize ?? 1e4;
201
+ this.maxRetries = options.maxRetries ?? 2;
202
+ this.retryDelayMs = options.retryDelayMs ?? 500;
203
+ const intervalMs = options.flushIntervalMs ?? 1e3;
204
+ this.flushTimer = setInterval(() => this.flush(), intervalMs);
205
+ if (this.flushTimer && typeof this.flushTimer === "object" && "unref" in this.flushTimer) {
206
+ this.flushTimer.unref();
207
+ }
208
+ }
209
+ get droppedCount() {
210
+ return this._droppedCount;
211
+ }
212
+ sendEvent(event) {
213
+ if (this.queue.length >= this.maxQueueSize) {
214
+ this.queue.shift();
215
+ this._droppedCount++;
216
+ }
217
+ this.queue.push(event);
218
+ }
219
+ async flush() {
220
+ if (this.queue.length === 0) return;
221
+ const batch = this.queue.splice(0);
222
+ const payload = {
223
+ sessionId: this.sessionId,
224
+ events: batch
225
+ };
226
+ if (!this.sessionRegistered) {
227
+ payload.appName = this.appName;
228
+ payload.sdkVersion = this.sdkVersion;
229
+ }
230
+ const body = JSON.stringify(payload);
231
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
232
+ try {
233
+ const headers = {
234
+ "Content-Type": "application/json"
235
+ };
236
+ if (this.authToken) {
237
+ headers["Authorization"] = `Bearer ${this.authToken}`;
238
+ }
239
+ const response = await fetch(this.url, {
240
+ method: "POST",
241
+ headers,
242
+ body,
243
+ signal: AbortSignal.timeout(5e3)
244
+ });
245
+ if (response.ok) {
246
+ this.sessionRegistered = true;
247
+ return;
248
+ }
249
+ if (response.status === 401 || response.status === 400) {
250
+ return;
251
+ }
252
+ } catch {
253
+ }
254
+ if (attempt < this.maxRetries) {
255
+ await new Promise((r) => setTimeout(r, this.retryDelayMs));
256
+ }
257
+ }
258
+ }
259
+ async disconnect() {
260
+ if (this.flushTimer) {
261
+ clearInterval(this.flushTimer);
262
+ this.flushTimer = null;
263
+ }
264
+ await this.flush();
265
+ }
266
+ };
267
+
179
268
  // src/utils/id.ts
180
269
  var import_node_crypto = require("crypto");
181
270
  function generateId() {
@@ -1399,26 +1488,51 @@ function runtimeScopeMiddleware(emit, sessionId, options) {
1399
1488
  }
1400
1489
 
1401
1490
  // src/index.ts
1402
- var SDK_VERSION = "0.6.1";
1491
+ var SDK_VERSION = "0.7.0";
1403
1492
  var RuntimeScopeServer = class {
1404
1493
  transport = null;
1494
+ httpTransport = null;
1405
1495
  sessionId = "";
1406
1496
  config = {};
1407
1497
  sampler = null;
1408
1498
  restoreFunctions = [];
1499
+ /** Alias for `connect` — mirrors the browser SDK's init() API */
1500
+ init(config = {}) {
1501
+ return this.connect(config);
1502
+ }
1409
1503
  connect(config = {}) {
1410
1504
  this.config = config;
1411
1505
  this.sessionId = config.sessionId ?? generateSessionId();
1412
- const serverUrl = config.serverUrl ?? "ws://127.0.0.1:9090";
1413
- this.transport = new ServerTransport({
1414
- url: serverUrl,
1415
- sessionId: this.sessionId,
1416
- appName: config.appName ?? "server-app",
1417
- sdkVersion: SDK_VERSION,
1418
- authToken: config.authToken,
1419
- maxQueueSize: config.maxQueueSize
1420
- });
1421
- this.transport.connect();
1506
+ const serverUrl = config.serverUrl ?? config.endpoint ?? "ws://127.0.0.1:9090";
1507
+ if (config.transport === "http") {
1508
+ let httpUrl = config.httpEndpoint;
1509
+ if (!httpUrl) {
1510
+ const wsUrl = new URL(serverUrl);
1511
+ wsUrl.protocol = wsUrl.protocol === "wss:" ? "https:" : "http:";
1512
+ wsUrl.port = "9091";
1513
+ wsUrl.pathname = "/api/events";
1514
+ httpUrl = wsUrl.toString();
1515
+ }
1516
+ this.httpTransport = new HttpTransport({
1517
+ url: httpUrl,
1518
+ sessionId: this.sessionId,
1519
+ appName: config.appName ?? "server-app",
1520
+ sdkVersion: SDK_VERSION,
1521
+ authToken: config.authToken,
1522
+ maxQueueSize: config.maxQueueSize,
1523
+ flushIntervalMs: config.httpFlushIntervalMs
1524
+ });
1525
+ } else {
1526
+ this.transport = new ServerTransport({
1527
+ url: serverUrl,
1528
+ sessionId: this.sessionId,
1529
+ appName: config.appName ?? "server-app",
1530
+ sdkVersion: SDK_VERSION,
1531
+ authToken: config.authToken,
1532
+ maxQueueSize: config.maxQueueSize
1533
+ });
1534
+ this.transport.connect();
1535
+ }
1422
1536
  if (config.sampleRate !== void 0 || config.maxEventsPerSecond !== void 0) {
1423
1537
  this.sampler = new Sampler({
1424
1538
  sampleRate: config.sampleRate,
@@ -1447,7 +1561,7 @@ var RuntimeScopeServer = class {
1447
1561
  } catch {
1448
1562
  }
1449
1563
  }
1450
- if (config.captureHttp) {
1564
+ if (config.captureHttp !== false) {
1451
1565
  try {
1452
1566
  this.restoreFunctions.push(
1453
1567
  interceptHttp(emit, this.sessionId, {
@@ -1455,14 +1569,17 @@ var RuntimeScopeServer = class {
1455
1569
  maxBodySize: config.maxBodySize,
1456
1570
  redactHeaders: config.redactHeaders,
1457
1571
  // Auto-ignore the collector URL to prevent recursion
1458
- ignoreUrls: [serverUrl.replace("ws://", "").replace("wss://", "")],
1572
+ ignoreUrls: [
1573
+ serverUrl.replace("ws://", "").replace("wss://", ""),
1574
+ ...config.httpEndpoint ? [config.httpEndpoint.replace(/^https?:\/\//, "")] : []
1575
+ ],
1459
1576
  beforeSend: config.beforeSend
1460
1577
  })
1461
1578
  );
1462
1579
  } catch {
1463
1580
  }
1464
1581
  }
1465
- if (config.capturePerformance) {
1582
+ if (config.capturePerformance !== false) {
1466
1583
  try {
1467
1584
  this.restoreFunctions.push(
1468
1585
  startPerfMetrics(emit, this.sessionId, {
@@ -1483,6 +1600,10 @@ var RuntimeScopeServer = class {
1483
1600
  }
1484
1601
  this.restoreFunctions = [];
1485
1602
  this.sampler = null;
1603
+ if (this.httpTransport) {
1604
+ this.httpTransport.disconnect();
1605
+ this.httpTransport = null;
1606
+ }
1486
1607
  this.transport?.disconnect();
1487
1608
  this.transport = null;
1488
1609
  }
@@ -1491,12 +1612,12 @@ var RuntimeScopeServer = class {
1491
1612
  }
1492
1613
  emitEvent(event) {
1493
1614
  if (this.sampler && !this.sampler.shouldSample(event)) return;
1494
- if (this.config.beforeSend) {
1495
- const filtered = this.config.beforeSend(event);
1496
- if (!filtered) return;
1497
- this.transport?.sendEvent(filtered);
1615
+ const filtered = this.config.beforeSend ? this.config.beforeSend(event) : event;
1616
+ if (!filtered) return;
1617
+ if (this.httpTransport) {
1618
+ this.httpTransport.sendEvent(filtered);
1498
1619
  } else {
1499
- this.transport?.sendEvent(event);
1620
+ this.transport?.sendEvent(filtered);
1500
1621
  }
1501
1622
  }
1502
1623
  // --- Express/Connect Middleware ---
@@ -1618,6 +1739,7 @@ var RuntimeScopeServer = class {
1618
1739
  var RuntimeScope = new RuntimeScopeServer();
1619
1740
  // Annotate the CommonJS export names for ESM import in node:
1620
1741
  0 && (module.exports = {
1742
+ HttpTransport,
1621
1743
  RuntimeScope,
1622
1744
  Sampler,
1623
1745
  _log,