@rely-net/sdk 1.0.1 → 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.d.mts CHANGED
@@ -61,6 +61,16 @@ interface DeploymentPayload {
61
61
  deployment_url: string;
62
62
  metadata: Record<string, unknown>;
63
63
  }
64
+ interface VendorCallWindow {
65
+ window_start: string;
66
+ window_end: string;
67
+ hostname: string;
68
+ call_count: number;
69
+ error_count: number;
70
+ p50_ms: number;
71
+ p95_ms: number;
72
+ p99_ms: number;
73
+ }
64
74
  interface IngestPayload {
65
75
  version: string;
66
76
  timestamp: string;
@@ -69,6 +79,7 @@ interface IngestPayload {
69
79
  metrics?: MetricDatapoint[];
70
80
  runtime?: RuntimeStats;
71
81
  request_telemetry?: RequestWindowData;
82
+ vendor_calls?: VendorCallWindow[];
72
83
  }
73
84
  interface IngestResponse {
74
85
  received: boolean;
@@ -78,6 +89,7 @@ interface IngestResponse {
78
89
  metrics: number;
79
90
  runtime: boolean;
80
91
  request_telemetry: boolean;
92
+ vendor_calls: number;
81
93
  };
82
94
  warnings: string[];
83
95
  timestamp: string;
@@ -89,6 +101,7 @@ interface RelyClientOptions {
89
101
  flushInterval?: number;
90
102
  sanitizeErrors?: boolean;
91
103
  debug?: boolean;
104
+ instrumentFetch?: boolean;
92
105
  }
93
106
 
94
107
  declare class RelyClient {
@@ -98,12 +111,16 @@ declare class RelyClient {
98
111
  private readonly flushInterval;
99
112
  private readonly sanitizeErrors;
100
113
  private readonly debug;
114
+ private readonly instrumentFetchEnabled;
101
115
  private healthChecks;
102
116
  private pendingMetrics;
103
117
  private requestBuffer;
118
+ private vendorCallBuffer;
104
119
  private flushTimer;
105
120
  private deploymentSent;
106
121
  private isDestroyed;
122
+ private originalFetch;
123
+ private ingestHost;
107
124
  constructor(options: RelyClientOptions);
108
125
  healthCheck(name: string, fn: HealthCheckFn): this;
109
126
  metric(name: string, value: number, tags?: Record<string, string>): this;
@@ -119,6 +136,8 @@ declare class RelyClient {
119
136
  private sanitize;
120
137
  private sanitizeTags;
121
138
  private detectFrameworkVersion;
139
+ private installFetchInstrumentation;
140
+ private extractHostname;
122
141
  private log;
123
142
  }
124
143
 
package/dist/index.d.ts CHANGED
@@ -61,6 +61,16 @@ interface DeploymentPayload {
61
61
  deployment_url: string;
62
62
  metadata: Record<string, unknown>;
63
63
  }
64
+ interface VendorCallWindow {
65
+ window_start: string;
66
+ window_end: string;
67
+ hostname: string;
68
+ call_count: number;
69
+ error_count: number;
70
+ p50_ms: number;
71
+ p95_ms: number;
72
+ p99_ms: number;
73
+ }
64
74
  interface IngestPayload {
65
75
  version: string;
66
76
  timestamp: string;
@@ -69,6 +79,7 @@ interface IngestPayload {
69
79
  metrics?: MetricDatapoint[];
70
80
  runtime?: RuntimeStats;
71
81
  request_telemetry?: RequestWindowData;
82
+ vendor_calls?: VendorCallWindow[];
72
83
  }
73
84
  interface IngestResponse {
74
85
  received: boolean;
@@ -78,6 +89,7 @@ interface IngestResponse {
78
89
  metrics: number;
79
90
  runtime: boolean;
80
91
  request_telemetry: boolean;
92
+ vendor_calls: number;
81
93
  };
82
94
  warnings: string[];
83
95
  timestamp: string;
@@ -89,6 +101,7 @@ interface RelyClientOptions {
89
101
  flushInterval?: number;
90
102
  sanitizeErrors?: boolean;
91
103
  debug?: boolean;
104
+ instrumentFetch?: boolean;
92
105
  }
93
106
 
94
107
  declare class RelyClient {
@@ -98,12 +111,16 @@ declare class RelyClient {
98
111
  private readonly flushInterval;
99
112
  private readonly sanitizeErrors;
100
113
  private readonly debug;
114
+ private readonly instrumentFetchEnabled;
101
115
  private healthChecks;
102
116
  private pendingMetrics;
103
117
  private requestBuffer;
118
+ private vendorCallBuffer;
104
119
  private flushTimer;
105
120
  private deploymentSent;
106
121
  private isDestroyed;
122
+ private originalFetch;
123
+ private ingestHost;
107
124
  constructor(options: RelyClientOptions);
108
125
  healthCheck(name: string, fn: HealthCheckFn): this;
109
126
  metric(name: string, value: number, tags?: Record<string, string>): this;
@@ -119,6 +136,8 @@ declare class RelyClient {
119
136
  private sanitize;
120
137
  private sanitizeTags;
121
138
  private detectFrameworkVersion;
139
+ private installFetchInstrumentation;
140
+ private extractHostname;
122
141
  private log;
123
142
  }
124
143
 
package/dist/index.js CHANGED
@@ -110,6 +110,55 @@ var RequestBuffer = class {
110
110
  }
111
111
  };
112
112
 
113
+ // src/collectors/vendorCalls.ts
114
+ function percentile2(arr, p) {
115
+ if (arr.length === 0) return 0;
116
+ const sorted = [...arr].sort((a, b) => a - b);
117
+ const index = Math.ceil(p / 100 * sorted.length) - 1;
118
+ return sorted[Math.max(0, index)];
119
+ }
120
+ var VendorCallBuffer = class {
121
+ constructor() {
122
+ this.hosts = /* @__PURE__ */ new Map();
123
+ this.windowStart = /* @__PURE__ */ new Date();
124
+ }
125
+ record(hostname, durationMs, isError) {
126
+ if (!hostname) return;
127
+ let data = this.hosts.get(hostname);
128
+ if (!data) {
129
+ data = { durations: [], errors: 0 };
130
+ this.hosts.set(hostname, data);
131
+ }
132
+ data.durations.push(Math.max(0, durationMs));
133
+ if (isError) data.errors += 1;
134
+ }
135
+ get totalCalls() {
136
+ let total = 0;
137
+ for (const d of Array.from(this.hosts.values())) total += d.durations.length;
138
+ return total;
139
+ }
140
+ flush() {
141
+ const windowEnd = (/* @__PURE__ */ new Date()).toISOString();
142
+ const windowStart = this.windowStart.toISOString();
143
+ const out = [];
144
+ for (const [hostname, data] of Array.from(this.hosts.entries())) {
145
+ out.push({
146
+ window_start: windowStart,
147
+ window_end: windowEnd,
148
+ hostname,
149
+ call_count: data.durations.length,
150
+ error_count: data.errors,
151
+ p50_ms: percentile2(data.durations, 50),
152
+ p95_ms: percentile2(data.durations, 95),
153
+ p99_ms: percentile2(data.durations, 99)
154
+ });
155
+ }
156
+ this.hosts = /* @__PURE__ */ new Map();
157
+ this.windowStart = /* @__PURE__ */ new Date();
158
+ return out;
159
+ }
160
+ };
161
+
113
162
  // src/client.ts
114
163
  var SDK_VERSION = "1.0.0";
115
164
  var SECRET_PATTERNS = [
@@ -156,12 +205,20 @@ var RelyClient = class {
156
205
  );
157
206
  this.sanitizeErrors = options.sanitizeErrors ?? true;
158
207
  this.debug = options.debug ?? false;
208
+ this.instrumentFetchEnabled = options.instrumentFetch ?? true;
159
209
  this.healthChecks = /* @__PURE__ */ new Map();
160
210
  this.pendingMetrics = [];
161
211
  this.requestBuffer = new RequestBuffer();
212
+ this.vendorCallBuffer = new VendorCallBuffer();
162
213
  this.flushTimer = null;
163
214
  this.deploymentSent = false;
164
215
  this.isDestroyed = false;
216
+ this.originalFetch = null;
217
+ try {
218
+ this.ingestHost = new URL(this.baseUrl).hostname;
219
+ } catch {
220
+ this.ingestHost = "";
221
+ }
165
222
  this.log(`SDK initialized`);
166
223
  this.log(`Environment: ${this.environment}`);
167
224
  this.log(`Flush interval: ${this.flushInterval / 1e3}s`);
@@ -247,6 +304,9 @@ var RelyClient = class {
247
304
  if (this.requestBuffer.totalRequests > 0) {
248
305
  payload.request_telemetry = this.requestBuffer.flush();
249
306
  }
307
+ if (this.vendorCallBuffer.totalCalls > 0) {
308
+ payload.vendor_calls = this.vendorCallBuffer.flush();
309
+ }
250
310
  await this.sendPayload(payload);
251
311
  }
252
312
  // Destroy the client and stop all background activity.
@@ -258,9 +318,19 @@ var RelyClient = class {
258
318
  clearInterval(this.flushTimer);
259
319
  this.flushTimer = null;
260
320
  }
321
+ if (this.originalFetch && typeof globalThis !== "undefined") {
322
+ try {
323
+ globalThis.fetch = this.originalFetch;
324
+ } catch {
325
+ }
326
+ this.originalFetch = null;
327
+ }
261
328
  this.log("SDK destroyed");
262
329
  }
263
330
  initialize() {
331
+ if (this.instrumentFetchEnabled) {
332
+ this.installFetchInstrumentation();
333
+ }
264
334
  if (this.environment === "production" || this.environment === "staging") {
265
335
  this.sendDeploymentMarker();
266
336
  }
@@ -400,6 +470,48 @@ var RelyClient = class {
400
470
  return "unknown";
401
471
  }
402
472
  }
473
+ installFetchInstrumentation() {
474
+ if (typeof globalThis === "undefined" || typeof globalThis.fetch !== "function") {
475
+ this.log("Global fetch not available, skipping instrumentation");
476
+ return;
477
+ }
478
+ const original = globalThis.fetch;
479
+ this.originalFetch = original;
480
+ const self = this;
481
+ const wrapped = async function(input, init) {
482
+ const hostname = self.extractHostname(input);
483
+ if (!hostname || hostname === self.ingestHost) {
484
+ return original(input, init);
485
+ }
486
+ const start = Date.now();
487
+ try {
488
+ const res = await original(input, init);
489
+ self.vendorCallBuffer.record(
490
+ hostname,
491
+ Date.now() - start,
492
+ res.status >= 400
493
+ );
494
+ return res;
495
+ } catch (err) {
496
+ self.vendorCallBuffer.record(hostname, Date.now() - start, true);
497
+ throw err;
498
+ }
499
+ };
500
+ try {
501
+ globalThis.fetch = wrapped;
502
+ this.log("Fetch instrumentation installed");
503
+ } catch {
504
+ }
505
+ }
506
+ extractHostname(input) {
507
+ try {
508
+ if (typeof input === "string") return new URL(input).hostname;
509
+ if (input instanceof URL) return input.hostname;
510
+ return new URL(input.url).hostname;
511
+ } catch {
512
+ return "";
513
+ }
514
+ }
403
515
  log(message) {
404
516
  if (this.debug) {
405
517
  console.log(`[Rely] ${message}`);
package/dist/index.mjs CHANGED
@@ -76,6 +76,55 @@ var RequestBuffer = class {
76
76
  }
77
77
  };
78
78
 
79
+ // src/collectors/vendorCalls.ts
80
+ function percentile2(arr, p) {
81
+ if (arr.length === 0) return 0;
82
+ const sorted = [...arr].sort((a, b) => a - b);
83
+ const index = Math.ceil(p / 100 * sorted.length) - 1;
84
+ return sorted[Math.max(0, index)];
85
+ }
86
+ var VendorCallBuffer = class {
87
+ constructor() {
88
+ this.hosts = /* @__PURE__ */ new Map();
89
+ this.windowStart = /* @__PURE__ */ new Date();
90
+ }
91
+ record(hostname, durationMs, isError) {
92
+ if (!hostname) return;
93
+ let data = this.hosts.get(hostname);
94
+ if (!data) {
95
+ data = { durations: [], errors: 0 };
96
+ this.hosts.set(hostname, data);
97
+ }
98
+ data.durations.push(Math.max(0, durationMs));
99
+ if (isError) data.errors += 1;
100
+ }
101
+ get totalCalls() {
102
+ let total = 0;
103
+ for (const d of Array.from(this.hosts.values())) total += d.durations.length;
104
+ return total;
105
+ }
106
+ flush() {
107
+ const windowEnd = (/* @__PURE__ */ new Date()).toISOString();
108
+ const windowStart = this.windowStart.toISOString();
109
+ const out = [];
110
+ for (const [hostname, data] of Array.from(this.hosts.entries())) {
111
+ out.push({
112
+ window_start: windowStart,
113
+ window_end: windowEnd,
114
+ hostname,
115
+ call_count: data.durations.length,
116
+ error_count: data.errors,
117
+ p50_ms: percentile2(data.durations, 50),
118
+ p95_ms: percentile2(data.durations, 95),
119
+ p99_ms: percentile2(data.durations, 99)
120
+ });
121
+ }
122
+ this.hosts = /* @__PURE__ */ new Map();
123
+ this.windowStart = /* @__PURE__ */ new Date();
124
+ return out;
125
+ }
126
+ };
127
+
79
128
  // src/client.ts
80
129
  var SDK_VERSION = "1.0.0";
81
130
  var SECRET_PATTERNS = [
@@ -122,12 +171,20 @@ var RelyClient = class {
122
171
  );
123
172
  this.sanitizeErrors = options.sanitizeErrors ?? true;
124
173
  this.debug = options.debug ?? false;
174
+ this.instrumentFetchEnabled = options.instrumentFetch ?? true;
125
175
  this.healthChecks = /* @__PURE__ */ new Map();
126
176
  this.pendingMetrics = [];
127
177
  this.requestBuffer = new RequestBuffer();
178
+ this.vendorCallBuffer = new VendorCallBuffer();
128
179
  this.flushTimer = null;
129
180
  this.deploymentSent = false;
130
181
  this.isDestroyed = false;
182
+ this.originalFetch = null;
183
+ try {
184
+ this.ingestHost = new URL(this.baseUrl).hostname;
185
+ } catch {
186
+ this.ingestHost = "";
187
+ }
131
188
  this.log(`SDK initialized`);
132
189
  this.log(`Environment: ${this.environment}`);
133
190
  this.log(`Flush interval: ${this.flushInterval / 1e3}s`);
@@ -213,6 +270,9 @@ var RelyClient = class {
213
270
  if (this.requestBuffer.totalRequests > 0) {
214
271
  payload.request_telemetry = this.requestBuffer.flush();
215
272
  }
273
+ if (this.vendorCallBuffer.totalCalls > 0) {
274
+ payload.vendor_calls = this.vendorCallBuffer.flush();
275
+ }
216
276
  await this.sendPayload(payload);
217
277
  }
218
278
  // Destroy the client and stop all background activity.
@@ -224,9 +284,19 @@ var RelyClient = class {
224
284
  clearInterval(this.flushTimer);
225
285
  this.flushTimer = null;
226
286
  }
287
+ if (this.originalFetch && typeof globalThis !== "undefined") {
288
+ try {
289
+ globalThis.fetch = this.originalFetch;
290
+ } catch {
291
+ }
292
+ this.originalFetch = null;
293
+ }
227
294
  this.log("SDK destroyed");
228
295
  }
229
296
  initialize() {
297
+ if (this.instrumentFetchEnabled) {
298
+ this.installFetchInstrumentation();
299
+ }
230
300
  if (this.environment === "production" || this.environment === "staging") {
231
301
  this.sendDeploymentMarker();
232
302
  }
@@ -366,6 +436,48 @@ var RelyClient = class {
366
436
  return "unknown";
367
437
  }
368
438
  }
439
+ installFetchInstrumentation() {
440
+ if (typeof globalThis === "undefined" || typeof globalThis.fetch !== "function") {
441
+ this.log("Global fetch not available, skipping instrumentation");
442
+ return;
443
+ }
444
+ const original = globalThis.fetch;
445
+ this.originalFetch = original;
446
+ const self = this;
447
+ const wrapped = async function(input, init) {
448
+ const hostname = self.extractHostname(input);
449
+ if (!hostname || hostname === self.ingestHost) {
450
+ return original(input, init);
451
+ }
452
+ const start = Date.now();
453
+ try {
454
+ const res = await original(input, init);
455
+ self.vendorCallBuffer.record(
456
+ hostname,
457
+ Date.now() - start,
458
+ res.status >= 400
459
+ );
460
+ return res;
461
+ } catch (err) {
462
+ self.vendorCallBuffer.record(hostname, Date.now() - start, true);
463
+ throw err;
464
+ }
465
+ };
466
+ try {
467
+ globalThis.fetch = wrapped;
468
+ this.log("Fetch instrumentation installed");
469
+ } catch {
470
+ }
471
+ }
472
+ extractHostname(input) {
473
+ try {
474
+ if (typeof input === "string") return new URL(input).hostname;
475
+ if (input instanceof URL) return input.hostname;
476
+ return new URL(input.url).hostname;
477
+ } catch {
478
+ return "";
479
+ }
480
+ }
369
481
  log(message) {
370
482
  if (this.debug) {
371
483
  console.log(`[Rely] ${message}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rely-net/sdk",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Official SDK for rely.net — monitor your app from the inside",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",