@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 +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +112 -0
- package/dist/index.mjs +112 -0
- package/package.json +1 -1
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}`);
|