@lavarage/telemetry 1.2.0 → 1.2.1

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.
Files changed (2) hide show
  1. package/dist/index.js +100 -10
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -124,16 +124,57 @@ class LavarageTelemetry {
124
124
  }
125
125
  shouldCaptureHost(url) {
126
126
  try {
127
- const urlObj = new URL(url);
128
- const hostname = urlObj.hostname;
127
+ // Skip empty or invalid URLs
128
+ if (!url || url.trim() === '') {
129
+ return false;
130
+ }
131
+ // Resolve relative URLs to absolute URLs
132
+ let absoluteUrl;
133
+ try {
134
+ // Try to parse as absolute URL first
135
+ new URL(url);
136
+ absoluteUrl = url;
137
+ }
138
+ catch {
139
+ // If it fails, it's likely a relative URL - resolve it using current origin
140
+ if (typeof window !== 'undefined' && window.location) {
141
+ try {
142
+ absoluteUrl = new URL(url, window.location.origin).href;
143
+ }
144
+ catch {
145
+ // Still can't resolve, don't capture
146
+ return false;
147
+ }
148
+ }
149
+ else {
150
+ // Not in browser environment, can't resolve relative URLs
151
+ return false;
152
+ }
153
+ }
154
+ const urlObj = new URL(absoluteUrl);
155
+ let hostname = urlObj.hostname;
156
+ // Handle special URL types that don't have a hostname (data:, blob:, etc.)
157
+ if (!hostname || hostname === '') {
158
+ // For include mode, don't capture URLs without hostnames
159
+ // For exclude mode, these would be captured (but they're unlikely to be in exclude list)
160
+ return false;
161
+ }
162
+ // Remove port number if present (hostname property should already exclude it, but be defensive)
163
+ // Also normalize: lowercase and trim
164
+ hostname = hostname.split(':')[0].toLowerCase().trim();
129
165
  const { mode, hosts = [], patterns = [] } = this.hostFilter;
130
166
  if (mode === 'all')
131
167
  return true;
132
168
  if (mode === 'none')
133
169
  return false;
134
- // Check host patterns
170
+ // In include mode, if no hosts or patterns are specified, don't capture anything
171
+ if (mode === 'include' && hosts.length === 0 && patterns.length === 0) {
172
+ return false;
173
+ }
174
+ // Check host patterns (normalize them too)
135
175
  for (const host of hosts) {
136
- if (this.matchesHost(hostname, host)) {
176
+ const normalizedHost = host.toLowerCase().trim();
177
+ if (this.matchesHost(hostname, normalizedHost)) {
137
178
  return mode === 'include';
138
179
  }
139
180
  }
@@ -159,16 +200,26 @@ class LavarageTelemetry {
159
200
  }
160
201
  }
161
202
  matchesHost(hostname, pattern) {
203
+ // Normalize inputs (should already be normalized, but be defensive)
204
+ hostname = hostname.toLowerCase().trim();
205
+ pattern = pattern.toLowerCase().trim();
162
206
  // Exact match
163
207
  if (hostname === pattern)
164
208
  return true;
165
209
  // Wildcard subdomain: *.example.com
166
210
  if (pattern.startsWith('*.')) {
167
- const domain = pattern.substring(2);
211
+ const domain = pattern.substring(2).toLowerCase().trim();
212
+ // Match exact domain or any subdomain
168
213
  return hostname === domain || hostname.endsWith('.' + domain);
169
214
  }
170
215
  // Domain match (matches domain and all subdomains)
171
- if (hostname === pattern || hostname.endsWith('.' + pattern)) {
216
+ // e.g., 'lavarave.wtf' matches 'lavarave.wtf' and 'api.lavarave.wtf'
217
+ if (hostname === pattern) {
218
+ return true;
219
+ }
220
+ // Check if hostname is a subdomain of pattern
221
+ // e.g., 'api.lavarave.wtf' ends with '.lavarave.wtf'
222
+ if (hostname.endsWith('.' + pattern)) {
172
223
  return true;
173
224
  }
174
225
  return false;
@@ -502,16 +553,53 @@ class LavarageTelemetry {
502
553
  }
503
554
  // Request interceptor
504
555
  axiosInstance.interceptors.request.use((config) => {
505
- const url = config.url || (config.baseURL ? `${config.baseURL}${config.url || ''}` : '');
556
+ // Construct full URL from axios config
557
+ let url;
558
+ try {
559
+ if (config.url) {
560
+ // If url is absolute (starts with http:// or https://), use it directly
561
+ if (config.url.startsWith('http://') || config.url.startsWith('https://')) {
562
+ url = config.url;
563
+ }
564
+ else if (config.baseURL) {
565
+ // Relative URL with baseURL - use URL constructor to properly combine them
566
+ try {
567
+ url = new URL(config.url, config.baseURL).href;
568
+ }
569
+ catch {
570
+ // Fallback to manual concatenation if URL constructor fails
571
+ const base = config.baseURL.endsWith('/') ? config.baseURL.slice(0, -1) : config.baseURL;
572
+ const path = config.url.startsWith('/') ? config.url : '/' + config.url;
573
+ url = base + path;
574
+ }
575
+ }
576
+ else {
577
+ // Relative URL without baseURL - will be resolved in shouldCaptureHost
578
+ url = config.url;
579
+ }
580
+ }
581
+ else if (config.baseURL) {
582
+ url = config.baseURL;
583
+ }
584
+ else {
585
+ // No URL and no baseURL - skip capture
586
+ return config;
587
+ }
588
+ }
589
+ catch {
590
+ // If URL construction fails, skip capture
591
+ return config;
592
+ }
506
593
  const method = config.method?.toUpperCase() || 'GET';
507
594
  if (!this.shouldCaptureHost(url)) {
508
595
  return config;
509
596
  }
510
597
  const requestId = this.generateRequestId();
511
598
  const startTime = Date.now();
512
- // Store request metadata
599
+ // Store request metadata (including full URL for response interceptor)
513
600
  config._telemetryRequestId = requestId;
514
601
  config._telemetryStartTime = startTime;
602
+ config._telemetryUrl = url; // Store full URL for response interceptor
515
603
  // Track request start
516
604
  this.enqueue({
517
605
  type: 'request',
@@ -535,7 +623,8 @@ class LavarageTelemetry {
535
623
  const startTime = config._telemetryStartTime;
536
624
  if (requestId && startTime) {
537
625
  const duration = Date.now() - startTime;
538
- const url = response.config?.url || response.request?.responseURL || '';
626
+ // Use stored URL from request interceptor, fallback to response URL
627
+ const url = config._telemetryUrl || response.config?.url || response.request?.responseURL || '';
539
628
  this.enqueue({
540
629
  type: 'request',
541
630
  wallet: this.wallet,
@@ -557,7 +646,8 @@ class LavarageTelemetry {
557
646
  const startTime = config._telemetryStartTime;
558
647
  if (requestId && startTime) {
559
648
  const duration = Date.now() - startTime;
560
- const url = config.url || error.request?.responseURL || '';
649
+ // Use stored URL from request interceptor, fallback to error config URL
650
+ const url = config._telemetryUrl || config.url || error.request?.responseURL || '';
561
651
  const errorMessage = error.message || 'Request failed';
562
652
  this.enqueue({
563
653
  type: 'request',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lavarage/telemetry",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Production telemetry SDK for Lavarage and partner applications",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",