@owlmetry/node 0.1.1 → 0.1.3

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/src/index.js CHANGED
@@ -199,11 +199,18 @@ export const Owl = {
199
199
  if (!beforeExitRegistered) {
200
200
  beforeExitRegistered = true;
201
201
  process.on("beforeExit", async () => {
202
- if (transport && transport.bufferSize > 0) {
203
- await transport.flush();
202
+ try {
203
+ if (transport && transport.bufferSize > 0) {
204
+ await transport.flush();
205
+ }
206
+ }
207
+ catch {
208
+ // Best-effort flush on exit — never crash the host process
204
209
  }
205
210
  });
206
211
  }
212
+ // Emit session start event
213
+ log("info", "sdk:session_started");
207
214
  },
208
215
  info(message, attrs) {
209
216
  log("info", message, attrs);
@@ -234,7 +241,10 @@ export const Owl = {
234
241
  return experiments[name];
235
242
  }
236
243
  if (options.length === 0) {
237
- throw new Error(`OwlMetry: getVariant("${name}") called with empty options array.`);
244
+ if (config?.debug) {
245
+ console.error(`OwlMetry: getVariant("${name}") called with empty options array`);
246
+ }
247
+ return "";
238
248
  }
239
249
  const variant = options[Math.floor(Math.random() * options.length)];
240
250
  experiments[name] = variant;
@@ -290,6 +300,7 @@ export const Owl = {
290
300
  },
291
301
  async shutdown() {
292
302
  if (transport) {
303
+ log("info", "sdk:session_ended");
293
304
  await transport.shutdown();
294
305
  transport = null;
295
306
  }
@@ -10,5 +10,6 @@ export declare class Transport {
10
10
  flush(): Promise<void>;
11
11
  shutdown(): Promise<void>;
12
12
  get bufferSize(): number;
13
+ private logError;
13
14
  private sendBatch;
14
15
  }
@@ -11,7 +11,7 @@ export class Transport {
11
11
  flushing = false;
12
12
  constructor(config) {
13
13
  this.config = config;
14
- this.timer = setInterval(() => this.flush(), config.flushIntervalMs);
14
+ this.timer = setInterval(() => this.flush().catch((err) => this.logError("flush failed", err)), config.flushIntervalMs);
15
15
  // Prevent timer from keeping the process alive
16
16
  if (this.timer.unref) {
17
17
  this.timer.unref();
@@ -24,7 +24,7 @@ export class Transport {
24
24
  }
25
25
  this.buffer.push(event);
26
26
  if (this.buffer.length >= this.config.flushThreshold) {
27
- this.flush();
27
+ this.flush().catch((err) => this.logError("flush failed", err));
28
28
  }
29
29
  }
30
30
  async flush() {
@@ -51,61 +51,73 @@ export class Transport {
51
51
  get bufferSize() {
52
52
  return this.buffer.length;
53
53
  }
54
- async sendBatch(events) {
55
- const body = { events };
56
- const json = JSON.stringify(body);
57
- let payload;
58
- let contentEncoding;
59
- if (json.length > GZIP_THRESHOLD) {
60
- payload = new Uint8Array(gzipSync(json));
61
- contentEncoding = "gzip";
62
- }
63
- else {
64
- payload = json;
54
+ logError(message, err) {
55
+ if (this.config.debug) {
56
+ console.error(`OwlMetry: ${message}`, err);
65
57
  }
66
- for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
67
- try {
68
- const headers = {
69
- "Content-Type": "application/json",
70
- "Authorization": `Bearer ${this.config.apiKey}`,
71
- };
72
- if (contentEncoding) {
73
- headers["Content-Encoding"] = contentEncoding;
58
+ }
59
+ async sendBatch(events) {
60
+ try {
61
+ const body = { events };
62
+ const json = JSON.stringify(body);
63
+ let payload;
64
+ let contentEncoding;
65
+ if (json.length > GZIP_THRESHOLD) {
66
+ payload = new Uint8Array(gzipSync(json));
67
+ contentEncoding = "gzip";
68
+ }
69
+ else {
70
+ payload = json;
71
+ }
72
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
73
+ try {
74
+ const headers = {
75
+ "Content-Type": "application/json",
76
+ "Authorization": `Bearer ${this.config.apiKey}`,
77
+ };
78
+ if (contentEncoding) {
79
+ headers["Content-Encoding"] = contentEncoding;
80
+ }
81
+ const res = await fetch(`${this.config.endpoint}/v1/ingest`, {
82
+ method: "POST",
83
+ headers,
84
+ body: payload,
85
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
86
+ });
87
+ if (res.ok)
88
+ return;
89
+ // Don't retry client errors (except 429)
90
+ if (res.status >= 400 && res.status < 500 && res.status !== 429) {
91
+ if (this.config.debug) {
92
+ const text = await res.text().catch(() => "");
93
+ console.error(`OwlMetry: ingest failed with ${res.status}: ${text}`);
94
+ }
95
+ return;
96
+ }
97
+ // Server error or 429 — retry with backoff
98
+ if (attempt < MAX_RETRIES) {
99
+ const backoff = Math.min(Math.pow(2, attempt) * 1000, MAX_BACKOFF_MS);
100
+ await new Promise((r) => setTimeout(r, backoff));
101
+ }
74
102
  }
75
- const res = await fetch(`${this.config.endpoint}/v1/ingest`, {
76
- method: "POST",
77
- headers,
78
- body: payload,
79
- signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
80
- });
81
- if (res.ok)
82
- return;
83
- // Don't retry client errors (except 429)
84
- if (res.status >= 400 && res.status < 500 && res.status !== 429) {
103
+ catch (err) {
85
104
  if (this.config.debug) {
86
- const text = await res.text().catch(() => "");
87
- console.error(`OwlMetry: ingest failed with ${res.status}: ${text}`);
105
+ console.error("OwlMetry: network error during ingest", err);
106
+ }
107
+ if (attempt < MAX_RETRIES) {
108
+ const backoff = Math.min(Math.pow(2, attempt) * 1000, MAX_BACKOFF_MS);
109
+ await new Promise((r) => setTimeout(r, backoff));
88
110
  }
89
- return;
90
- }
91
- // Server error or 429 — retry with backoff
92
- if (attempt < MAX_RETRIES) {
93
- const backoff = Math.min(Math.pow(2, attempt) * 1000, MAX_BACKOFF_MS);
94
- await new Promise((r) => setTimeout(r, backoff));
95
111
  }
96
112
  }
97
- catch (err) {
98
- if (this.config.debug) {
99
- console.error("OwlMetry: network error during ingest", err);
100
- }
101
- if (attempt < MAX_RETRIES) {
102
- const backoff = Math.min(Math.pow(2, attempt) * 1000, MAX_BACKOFF_MS);
103
- await new Promise((r) => setTimeout(r, backoff));
104
- }
113
+ if (this.config.debug) {
114
+ console.error(`OwlMetry: failed to send batch after ${MAX_RETRIES + 1} attempts, dropping ${events.length} events`);
105
115
  }
106
116
  }
107
- if (this.config.debug) {
108
- console.error(`OwlMetry: failed to send batch after ${MAX_RETRIES + 1} attempts, dropping ${events.length} events`);
117
+ catch (err) {
118
+ if (this.config.debug) {
119
+ console.error("OwlMetry: failed to prepare batch for sending", err);
120
+ }
109
121
  }
110
122
  }
111
123
  }
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@owlmetry/node",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "OwlMetry Node.js Server SDK",
5
5
  "type": "module",
6
- "main": "dist/index.js",
7
- "types": "dist/index.d.ts",
6
+ "main": "dist/src/index.js",
7
+ "types": "dist/src/index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
- "import": "./dist/index.js",
11
- "types": "./dist/index.d.ts"
10
+ "import": "./dist/src/index.js",
11
+ "types": "./dist/src/index.d.ts"
12
12
  }
13
13
  },
14
14
  "files": [