@abbacchio/browser-transport 0.1.4 → 0.2.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/auto.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Auto-initialize console interception
2
+ * Auto-initialize console interception (OTLP native)
3
3
  *
4
4
  * Simply import this module to start capturing console output:
5
5
  *
@@ -7,7 +7,7 @@
7
7
  * ```typescript
8
8
  * import '@abbacchio/browser-transport/auto'
9
9
  *
10
- * // All console.log calls now go to Abbacchio
10
+ * // All console.log calls now go to Abbacchio via OTLP
11
11
  * console.log('This is captured!')
12
12
  * ```
13
13
  *
@@ -17,10 +17,9 @@
17
17
  * ```html
18
18
  * <script>
19
19
  * window.__ABBACCHIO_CONFIG__ = {
20
- * url: 'http://localhost:4000/api/logs',
21
- * channel: 'my-app',
20
+ * endpoint: 'http://localhost:4002',
21
+ * serviceName: 'my-app',
22
22
  * appName: 'my-web-app',
23
- * secretKey: 'optional-encryption-key',
24
23
  * }
25
24
  * </script>
26
25
  * <script type="module">
@@ -1 +1 @@
1
- {"version":3,"file":"auto.d.ts","sourceRoot":"","sources":["../src/auto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAoB,KAAK,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAGhF,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,oBAAoB,CAAC,EAAE,yBAAyB,CAAC;KAClD;CACF"}
1
+ {"version":3,"file":"auto.d.ts","sourceRoot":"","sources":["../src/auto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAoB,KAAK,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAEhF,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,oBAAoB,CAAC,EAAE,yBAAyB,CAAC;KAClD;CACF"}
package/dist/auto.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Auto-initialize console interception
2
+ * Auto-initialize console interception (OTLP native)
3
3
  *
4
4
  * Simply import this module to start capturing console output:
5
5
  *
@@ -7,7 +7,7 @@
7
7
  * ```typescript
8
8
  * import '@abbacchio/browser-transport/auto'
9
9
  *
10
- * // All console.log calls now go to Abbacchio
10
+ * // All console.log calls now go to Abbacchio via OTLP
11
11
  * console.log('This is captured!')
12
12
  * ```
13
13
  *
@@ -17,10 +17,9 @@
17
17
  * ```html
18
18
  * <script>
19
19
  * window.__ABBACCHIO_CONFIG__ = {
20
- * url: 'http://localhost:4000/api/logs',
21
- * channel: 'my-app',
20
+ * endpoint: 'http://localhost:4002',
21
+ * serviceName: 'my-app',
22
22
  * appName: 'my-web-app',
23
- * secretKey: 'optional-encryption-key',
24
23
  * }
25
24
  * </script>
26
25
  * <script type="module">
@@ -29,12 +28,10 @@
29
28
  * ```
30
29
  */
31
30
  import { interceptConsole } from './console.js';
32
- // Get configuration from global variable or use defaults
33
31
  const config = (typeof window !== 'undefined' && window.__ABBACCHIO_CONFIG__) || {};
34
- // Start intercepting with merged config
35
32
  interceptConsole({
36
- url: 'http://localhost:4000/api/logs',
37
- channel: 'browser',
33
+ endpoint: 'http://localhost:4002',
34
+ serviceName: 'browser',
38
35
  appName: 'auto',
39
36
  ...config,
40
37
  });
package/dist/auto.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"auto.js","sourceRoot":"","sources":["../src/auto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,gBAAgB,EAAkC,MAAM,cAAc,CAAC;AAShF,yDAAyD;AACzD,MAAM,MAAM,GACV,CAAC,OAAO,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAC;AAEvE,wCAAwC;AACxC,gBAAgB,CAAC;IACf,GAAG,EAAE,gCAAgC;IACrC,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,MAAM;IACf,GAAG,MAAM;CACV,CAAC,CAAC"}
1
+ {"version":3,"file":"auto.js","sourceRoot":"","sources":["../src/auto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,gBAAgB,EAAkC,MAAM,cAAc,CAAC;AAQhF,MAAM,MAAM,GACV,CAAC,OAAO,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAC;AAEvE,gBAAgB,CAAC;IACf,QAAQ,EAAE,uBAAuB;IACjC,WAAW,EAAE,SAAS;IACtB,OAAO,EAAE,MAAM;IACf,GAAG,MAAM;CACV,CAAC,CAAC"}
package/dist/client.d.ts CHANGED
@@ -1,17 +1,19 @@
1
1
  /**
2
- * Browser HTTP client for Abbacchio
3
- * Handles batching, encryption, and sending logs to the server
2
+ * Browser HTTP client for Abbacchio (OTLP native)
3
+ * Sends logs to POST /v1/logs, metrics to POST /v1/metrics, traces to POST /v1/traces
4
4
  */
5
5
  export interface AbbacchioClientOptions {
6
- /** Server URL endpoint */
6
+ /** Base URL of the OTLP server (e.g. "http://localhost:4002"). Logs go to {endpoint}/v1/logs */
7
+ endpoint?: string;
8
+ /** @deprecated Use `endpoint` instead */
7
9
  url?: string;
8
- /** Secret key for encryption. If provided, logs will be encrypted before sending */
10
+ /** Secret key for encryption. If provided, payloads will be encrypted before sending */
9
11
  secretKey?: string;
10
- /** Channel/app name for multi-app support. Defaults to 'default' */
11
- channel?: string;
12
- /** Default namespace for all logs. Per-log namespace/name fields take precedence */
13
- namespace?: string;
14
- /** Number of logs to batch before sending. Defaults to 10 */
12
+ /** Service name maps to OTLP resource attribute `service.name`. Defaults to 'default' */
13
+ serviceName?: string;
14
+ /** Additional OTLP resource attributes */
15
+ resourceAttributes?: Record<string, string>;
16
+ /** Number of log records to batch before sending. Defaults to 10 */
15
17
  batchSize?: number;
16
18
  /** Interval in ms between flushes. Defaults to 1000 */
17
19
  flushInterval?: number;
@@ -28,65 +30,83 @@ export interface LogEntry {
28
30
  [key: string]: unknown;
29
31
  }
30
32
  /**
31
- * Browser HTTP client for Abbacchio.
32
- * Handles batching, encryption, and HTTP communication.
33
+ * A metric data point to send via OTLP.
34
+ */
35
+ export interface MetricRecord {
36
+ name: string;
37
+ description?: string;
38
+ unit?: string;
39
+ type: 'sum' | 'gauge';
40
+ value: number;
41
+ attributes?: Record<string, unknown>;
42
+ isMonotonic?: boolean;
43
+ }
44
+ /**
45
+ * A trace span to send via OTLP.
46
+ */
47
+ export interface SpanRecord {
48
+ traceId: string;
49
+ spanId: string;
50
+ parentSpanId?: string;
51
+ name: string;
52
+ kind?: number;
53
+ startTimeUnixNano: string;
54
+ endTimeUnixNano: string;
55
+ attributes?: Record<string, unknown>;
56
+ status?: {
57
+ code: number;
58
+ message?: string;
59
+ };
60
+ }
61
+ /**
62
+ * Browser HTTP client for Abbacchio (OTLP native).
63
+ * Sends logs via POST /v1/logs in ExportLogsServiceRequest format.
33
64
  */
34
65
  export declare class AbbacchioClient {
35
- private url;
66
+ private endpoint;
36
67
  private secretKey?;
37
- private channel;
38
- private namespace?;
68
+ private serviceName;
69
+ private resourceAttributes;
39
70
  private batchSize;
40
71
  private flushInterval;
41
72
  private headers;
42
73
  private enabled;
43
74
  private buffer;
75
+ private metricBuffer;
76
+ private spanBuffer;
44
77
  private timer;
78
+ private metricTimer;
79
+ private spanTimer;
45
80
  private isClosed;
46
81
  constructor(options?: AbbacchioClientOptions);
47
- /**
48
- * Process a log entry: inject default namespace, then encrypt if needed
49
- */
50
- private processLog;
51
- /**
52
- * Enable sending logs to the server
53
- */
54
82
  enable(): void;
55
- /**
56
- * Disable sending logs to the server. Logs will be silently dropped.
57
- */
58
83
  disable(): void;
59
- /**
60
- * Check if the client is currently enabled
61
- */
62
84
  isEnabled(): boolean;
63
- /**
64
- * Add a log to the buffer and trigger send if needed
65
- */
85
+ /** Add a log to the buffer and trigger send if needed */
66
86
  add(log: LogEntry): void;
67
- /**
68
- * Schedule a send after the interval
69
- */
70
87
  private scheduleSend;
71
- /**
72
- * Flush the buffer and send to server
73
- */
88
+ /** Flush all buffers (logs, metrics, traces) */
74
89
  flush(): Promise<void>;
75
- /**
76
- * Send logs to the Abbacchio server
77
- */
78
- private sendToServer;
79
- /**
80
- * Close the client and flush any remaining logs
81
- */
90
+ flushLogs(): Promise<void>;
91
+ private sendLogs;
92
+ /** Add a metric data point to the buffer */
93
+ addMetric(metric: MetricRecord): void;
94
+ private scheduleMetricFlush;
95
+ flushMetrics(): Promise<void>;
96
+ private sendMetrics;
97
+ /** Add a trace span to the buffer */
98
+ addSpan(span: SpanRecord): void;
99
+ private scheduleSpanFlush;
100
+ flushSpans(): Promise<void>;
101
+ private sendSpans;
82
102
  close(): Promise<void>;
83
- /**
84
- * Update client configuration
85
- */
103
+ private buildResourceAttributes;
104
+ private post;
105
+ /** Update client configuration */
86
106
  configure(options: Partial<AbbacchioClientOptions>): void;
87
107
  }
88
108
  /**
89
- * Create a new Abbacchio client instance
109
+ * Create a new Abbacchio OTLP client instance
90
110
  */
91
111
  export declare function createClient(options?: AbbacchioClientOptions): AbbacchioClient;
92
112
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,sBAAsB;IACrC,0BAA0B;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oFAAoF;IACpF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oFAAoF;IACpF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,2DAA2D;IAC3D,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,OAAO,CAAU;IAEzB,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,QAAQ,CAAS;gBAEb,OAAO,GAAE,sBAA2B;IAiBhD;;OAEG;YACW,UAAU;IAaxB;;OAEG;IACH,MAAM,IAAI,IAAI;IAId;;OAEG;IACH,OAAO,IAAI,IAAI;IAIf;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI;IAYxB;;OAEG;IACH,OAAO,CAAC,YAAY;IAQpB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAiB5B;;OAEG;YACW,YAAY;IAiB1B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAS5B;;OAEG;IACH,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,sBAAsB,CAAC,GAAG,IAAI;CAU1D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,eAAe,CAE9E"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js CHANGED
@@ -1,21 +1,74 @@
1
1
  /**
2
- * Browser HTTP client for Abbacchio
3
- * Handles batching, encryption, and sending logs to the server
2
+ * Browser HTTP client for Abbacchio (OTLP native)
3
+ * Sends logs to POST /v1/logs, metrics to POST /v1/metrics, traces to POST /v1/traces
4
4
  */
5
5
  import { encrypt } from './crypto.js';
6
6
  /**
7
- * Browser HTTP client for Abbacchio.
8
- * Handles batching, encryption, and HTTP communication.
7
+ * OTLP severity numbers per OpenTelemetry spec.
8
+ */
9
+ const PINO_TO_OTLP_SEVERITY = {
10
+ 10: { number: 1, text: 'TRACE' },
11
+ 20: { number: 5, text: 'DEBUG' },
12
+ 30: { number: 9, text: 'INFO' },
13
+ 40: { number: 13, text: 'WARN' },
14
+ 50: { number: 17, text: 'ERROR' },
15
+ 60: { number: 21, text: 'FATAL' },
16
+ };
17
+ function toOtlpAnyValue(val) {
18
+ if (typeof val === 'string')
19
+ return { stringValue: val };
20
+ if (typeof val === 'number') {
21
+ return Number.isInteger(val) ? { intValue: val } : { doubleValue: val };
22
+ }
23
+ if (typeof val === 'boolean')
24
+ return { boolValue: val };
25
+ if (val === null || val === undefined)
26
+ return { stringValue: '' };
27
+ // For objects/arrays, JSON.stringify
28
+ try {
29
+ return { stringValue: JSON.stringify(val) };
30
+ }
31
+ catch {
32
+ return { stringValue: String(val) };
33
+ }
34
+ }
35
+ function toOtlpAttributes(obj) {
36
+ const attrs = [];
37
+ for (const [key, val] of Object.entries(obj)) {
38
+ if (val === undefined)
39
+ continue;
40
+ attrs.push({ key, value: toOtlpAnyValue(val) });
41
+ }
42
+ return attrs;
43
+ }
44
+ /**
45
+ * Browser HTTP client for Abbacchio (OTLP native).
46
+ * Sends logs via POST /v1/logs in ExportLogsServiceRequest format.
9
47
  */
10
48
  export class AbbacchioClient {
11
49
  constructor(options = {}) {
12
50
  this.buffer = [];
51
+ this.metricBuffer = [];
52
+ this.spanBuffer = [];
13
53
  this.timer = null;
54
+ this.metricTimer = null;
55
+ this.spanTimer = null;
14
56
  this.isClosed = false;
15
- this.url = options.url || 'http://localhost:4000/api/logs';
57
+ if (options.url && !options.endpoint) {
58
+ try {
59
+ const u = new URL(options.url);
60
+ this.endpoint = `${u.protocol}//${u.host}`;
61
+ }
62
+ catch {
63
+ this.endpoint = options.url;
64
+ }
65
+ }
66
+ else {
67
+ this.endpoint = options.endpoint || 'http://localhost:4002';
68
+ }
16
69
  this.secretKey = options.secretKey;
17
- this.channel = options.channel || 'default';
18
- this.namespace = options.namespace;
70
+ this.serviceName = options.serviceName || 'default';
71
+ this.resourceAttributes = options.resourceAttributes || {};
19
72
  this.batchSize = options.batchSize || 10;
20
73
  this.flushInterval = options.flushInterval || 1000;
21
74
  this.headers = options.headers || {};
@@ -26,40 +79,10 @@ export class AbbacchioClient {
26
79
  window.addEventListener('pagehide', () => this.flush());
27
80
  }
28
81
  }
29
- /**
30
- * Process a log entry: inject default namespace, then encrypt if needed
31
- */
32
- async processLog(log) {
33
- let processed = log;
34
- if (this.namespace && !processed.namespace && !processed.name) {
35
- processed = { ...processed, namespace: this.namespace };
36
- }
37
- if (this.secretKey) {
38
- return { encrypted: await encrypt(JSON.stringify(processed), this.secretKey) };
39
- }
40
- return processed;
41
- }
42
- /**
43
- * Enable sending logs to the server
44
- */
45
- enable() {
46
- this.enabled = true;
47
- }
48
- /**
49
- * Disable sending logs to the server. Logs will be silently dropped.
50
- */
51
- disable() {
52
- this.enabled = false;
53
- }
54
- /**
55
- * Check if the client is currently enabled
56
- */
57
- isEnabled() {
58
- return this.enabled;
59
- }
60
- /**
61
- * Add a log to the buffer and trigger send if needed
62
- */
82
+ enable() { this.enabled = true; }
83
+ disable() { this.enabled = false; }
84
+ isEnabled() { return this.enabled; }
85
+ /** Add a log to the buffer and trigger send if needed */
63
86
  add(log) {
64
87
  if (!this.enabled || this.isClosed)
65
88
  return;
@@ -71,9 +94,6 @@ export class AbbacchioClient {
71
94
  this.scheduleSend();
72
95
  }
73
96
  }
74
- /**
75
- * Schedule a send after the interval
76
- */
77
97
  scheduleSend() {
78
98
  if (this.timer)
79
99
  return;
@@ -82,10 +102,15 @@ export class AbbacchioClient {
82
102
  this.flush();
83
103
  }, this.flushInterval);
84
104
  }
85
- /**
86
- * Flush the buffer and send to server
87
- */
105
+ /** Flush all buffers (logs, metrics, traces) */
88
106
  async flush() {
107
+ await Promise.all([
108
+ this.flushLogs(),
109
+ this.flushMetrics(),
110
+ this.flushSpans(),
111
+ ]);
112
+ }
113
+ async flushLogs() {
89
114
  if (this.buffer.length === 0)
90
115
  return;
91
116
  const toSend = this.buffer;
@@ -94,53 +119,241 @@ export class AbbacchioClient {
94
119
  clearTimeout(this.timer);
95
120
  this.timer = null;
96
121
  }
97
- // Process logs (encrypt if needed)
98
- const processedLogs = await Promise.all(toSend.map(log => this.processLog(log)));
99
- await this.sendToServer(processedLogs);
122
+ await this.sendLogs(toSend);
100
123
  }
101
- /**
102
- * Send logs to the Abbacchio server
103
- */
104
- async sendToServer(logs) {
124
+ async sendLogs(logs) {
105
125
  try {
106
- await fetch(this.url, {
107
- method: 'POST',
108
- headers: {
109
- 'Content-Type': 'application/json',
110
- 'X-Encrypted': this.secretKey ? 'true' : 'false',
111
- 'X-Channel': this.channel,
112
- ...this.headers,
113
- },
114
- body: JSON.stringify({ logs }),
126
+ const logRecords = logs.map((log) => {
127
+ const { level, msg, message, time, name, ...extra } = log;
128
+ const pinoLevel = typeof level === 'number' ? level : 30;
129
+ const severity = PINO_TO_OTLP_SEVERITY[pinoLevel] || PINO_TO_OTLP_SEVERITY[30];
130
+ const body = msg || message || '';
131
+ const timeNano = String((time || Date.now()) * 1000000);
132
+ return {
133
+ timeUnixNano: timeNano,
134
+ observedTimeUnixNano: timeNano,
135
+ severityNumber: severity.number,
136
+ severityText: severity.text,
137
+ body: { stringValue: String(body) },
138
+ attributes: toOtlpAttributes(extra),
139
+ };
115
140
  });
141
+ const payload = {
142
+ resourceLogs: [{
143
+ resource: { attributes: this.buildResourceAttributes() },
144
+ scopeLogs: [{
145
+ scope: { name: '@abbacchio/browser-transport' },
146
+ logRecords,
147
+ }],
148
+ }],
149
+ };
150
+ await this.post('/v1/logs', payload);
151
+ }
152
+ catch {
153
+ // Silently fail
154
+ }
155
+ }
156
+ // ─── Metrics ───────────────────────────────────────
157
+ /** Add a metric data point to the buffer */
158
+ addMetric(metric) {
159
+ if (!this.enabled || this.isClosed)
160
+ return;
161
+ this.metricBuffer.push(metric);
162
+ if (this.metricBuffer.length >= this.batchSize) {
163
+ this.flushMetrics();
164
+ }
165
+ else {
166
+ this.scheduleMetricFlush();
167
+ }
168
+ }
169
+ scheduleMetricFlush() {
170
+ if (this.metricTimer)
171
+ return;
172
+ this.metricTimer = setTimeout(() => {
173
+ this.metricTimer = null;
174
+ this.flushMetrics();
175
+ }, this.flushInterval);
176
+ }
177
+ async flushMetrics() {
178
+ if (this.metricBuffer.length === 0)
179
+ return;
180
+ const batch = this.metricBuffer;
181
+ this.metricBuffer = [];
182
+ if (this.metricTimer) {
183
+ clearTimeout(this.metricTimer);
184
+ this.metricTimer = null;
185
+ }
186
+ await this.sendMetrics(batch);
187
+ }
188
+ async sendMetrics(metrics) {
189
+ try {
190
+ const byName = new Map();
191
+ for (const m of metrics) {
192
+ const arr = byName.get(m.name) || [];
193
+ arr.push(m);
194
+ byName.set(m.name, arr);
195
+ }
196
+ const otlpMetrics = [];
197
+ for (const [name, records] of byName) {
198
+ const first = records[0];
199
+ const dataPoints = records.map((r) => ({
200
+ timeUnixNano: String(Date.now() * 1000000),
201
+ asDouble: r.value,
202
+ attributes: r.attributes ? toOtlpAttributes(r.attributes) : [],
203
+ }));
204
+ const metric = {
205
+ name,
206
+ description: first.description || '',
207
+ unit: first.unit || '',
208
+ };
209
+ if (first.type === 'sum') {
210
+ metric.sum = {
211
+ dataPoints,
212
+ aggregationTemporality: 2,
213
+ isMonotonic: first.isMonotonic ?? true,
214
+ };
215
+ }
216
+ else {
217
+ metric.gauge = { dataPoints };
218
+ }
219
+ otlpMetrics.push(metric);
220
+ }
221
+ const payload = {
222
+ resourceMetrics: [{
223
+ resource: { attributes: this.buildResourceAttributes() },
224
+ scopeMetrics: [{
225
+ scope: { name: '@abbacchio/browser-transport' },
226
+ metrics: otlpMetrics,
227
+ }],
228
+ }],
229
+ };
230
+ await this.post('/v1/metrics', payload);
231
+ }
232
+ catch {
233
+ // Silently fail
234
+ }
235
+ }
236
+ // ─── Traces ────────────────────────────────────────
237
+ /** Add a trace span to the buffer */
238
+ addSpan(span) {
239
+ if (!this.enabled || this.isClosed)
240
+ return;
241
+ this.spanBuffer.push(span);
242
+ if (this.spanBuffer.length >= this.batchSize) {
243
+ this.flushSpans();
244
+ }
245
+ else {
246
+ this.scheduleSpanFlush();
247
+ }
248
+ }
249
+ scheduleSpanFlush() {
250
+ if (this.spanTimer)
251
+ return;
252
+ this.spanTimer = setTimeout(() => {
253
+ this.spanTimer = null;
254
+ this.flushSpans();
255
+ }, this.flushInterval);
256
+ }
257
+ async flushSpans() {
258
+ if (this.spanBuffer.length === 0)
259
+ return;
260
+ const batch = this.spanBuffer;
261
+ this.spanBuffer = [];
262
+ if (this.spanTimer) {
263
+ clearTimeout(this.spanTimer);
264
+ this.spanTimer = null;
265
+ }
266
+ await this.sendSpans(batch);
267
+ }
268
+ async sendSpans(spans) {
269
+ try {
270
+ const otlpSpans = spans.map((s) => ({
271
+ traceId: s.traceId,
272
+ spanId: s.spanId,
273
+ parentSpanId: s.parentSpanId,
274
+ name: s.name,
275
+ kind: s.kind ?? 1,
276
+ startTimeUnixNano: s.startTimeUnixNano,
277
+ endTimeUnixNano: s.endTimeUnixNano,
278
+ attributes: s.attributes ? toOtlpAttributes(s.attributes) : [],
279
+ status: s.status,
280
+ }));
281
+ const payload = {
282
+ resourceSpans: [{
283
+ resource: { attributes: this.buildResourceAttributes() },
284
+ scopeSpans: [{
285
+ scope: { name: '@abbacchio/browser-transport' },
286
+ spans: otlpSpans,
287
+ }],
288
+ }],
289
+ };
290
+ await this.post('/v1/traces', payload);
116
291
  }
117
292
  catch {
118
- // Silently fail - don't break the app if Abbacchio server is down
293
+ // Silently fail
119
294
  }
120
295
  }
121
- /**
122
- * Close the client and flush any remaining logs
123
- */
296
+ // ─── Shared ────────────────────────────────────────
124
297
  async close() {
125
298
  this.isClosed = true;
126
299
  if (this.timer) {
127
300
  clearTimeout(this.timer);
128
301
  this.timer = null;
129
302
  }
303
+ if (this.metricTimer) {
304
+ clearTimeout(this.metricTimer);
305
+ this.metricTimer = null;
306
+ }
307
+ if (this.spanTimer) {
308
+ clearTimeout(this.spanTimer);
309
+ this.spanTimer = null;
310
+ }
130
311
  await this.flush();
131
312
  }
132
- /**
133
- * Update client configuration
134
- */
313
+ buildResourceAttributes() {
314
+ const attrs = [
315
+ { key: 'service.name', value: { stringValue: this.serviceName } },
316
+ ];
317
+ for (const [key, val] of Object.entries(this.resourceAttributes)) {
318
+ attrs.push({ key, value: { stringValue: val } });
319
+ }
320
+ return attrs;
321
+ }
322
+ async post(path, payload) {
323
+ let body = JSON.stringify(payload);
324
+ const headers = {
325
+ 'Content-Type': 'application/json',
326
+ ...this.headers,
327
+ };
328
+ if (this.secretKey) {
329
+ body = await encrypt(JSON.stringify(payload), this.secretKey);
330
+ headers['X-Encrypted'] = 'true';
331
+ }
332
+ await fetch(`${this.endpoint}${path}`, {
333
+ method: 'POST',
334
+ headers,
335
+ body,
336
+ });
337
+ }
338
+ /** Update client configuration */
135
339
  configure(options) {
136
- if (options.url !== undefined)
137
- this.url = options.url;
340
+ if (options.endpoint !== undefined)
341
+ this.endpoint = options.endpoint;
342
+ if (options.url !== undefined && !options.endpoint) {
343
+ try {
344
+ const u = new URL(options.url);
345
+ this.endpoint = `${u.protocol}//${u.host}`;
346
+ }
347
+ catch {
348
+ this.endpoint = options.url;
349
+ }
350
+ }
138
351
  if (options.secretKey !== undefined)
139
352
  this.secretKey = options.secretKey;
140
- if (options.channel !== undefined)
141
- this.channel = options.channel;
142
- if (options.namespace !== undefined)
143
- this.namespace = options.namespace;
353
+ if (options.serviceName !== undefined)
354
+ this.serviceName = options.serviceName;
355
+ if (options.resourceAttributes !== undefined)
356
+ this.resourceAttributes = options.resourceAttributes;
144
357
  if (options.enabled !== undefined)
145
358
  this.enabled = options.enabled;
146
359
  if (options.batchSize !== undefined)
@@ -152,7 +365,7 @@ export class AbbacchioClient {
152
365
  }
153
366
  }
154
367
  /**
155
- * Create a new Abbacchio client instance
368
+ * Create a new Abbacchio OTLP client instance
156
369
  */
157
370
  export function createClient(options) {
158
371
  return new AbbacchioClient(options);