git-watchtower 1.9.5 → 1.9.6

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.
@@ -2839,13 +2839,14 @@ async function shutdown() {
2839
2839
 
2840
2840
  process.on('SIGINT', shutdown);
2841
2841
  process.on('SIGTERM', shutdown);
2842
- process.on('uncaughtException', (err) => {
2842
+ process.on('uncaughtException', async (err) => {
2843
2843
  telemetry.captureError(err);
2844
2844
  write(ansi.showCursor);
2845
2845
  write(ansi.restoreScreen);
2846
2846
  restoreTerminalTitle();
2847
2847
  if (process.stdin.isTTY) process.stdin.setRawMode(false);
2848
2848
  console.error('Uncaught exception:', err);
2849
+ await telemetry.shutdown();
2849
2850
  process.exit(1);
2850
2851
  });
2851
2852
 
@@ -2867,7 +2868,8 @@ async function start() {
2867
2868
  const config = await ensureConfig(cliArgs);
2868
2869
  applyConfig(config);
2869
2870
 
2870
- // Telemetry: opt-in prompt (first run only) and initialization
2871
+ // Telemetry: set version early so consent events include $lib_version
2872
+ telemetry.setVersion(PACKAGE_VERSION);
2871
2873
  await telemetry.promptIfNeeded(promptYesNo);
2872
2874
  telemetry.init({ version: PACKAGE_VERSION });
2873
2875
  sessionStartTime = Date.now();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-watchtower",
3
- "version": "1.9.5",
3
+ "version": "1.9.6",
4
4
  "description": "Terminal-based Git branch monitor with activity sparklines and optional dev server with live reload",
5
5
  "main": "bin/git-watchtower.js",
6
6
  "bin": {
@@ -8,13 +8,13 @@
8
8
  },
9
9
  "scripts": {
10
10
  "start": "node bin/git-watchtower.js",
11
- "test": "node --test tests/unit/**/*.test.js tests/integration/**/*.test.js",
12
- "test:unit": "node --test tests/unit/**/*.test.js",
13
- "test:integration": "node --test tests/integration/**/*.test.js",
14
- "test:watch": "node --test --watch tests/unit/**/*.test.js",
15
- "test:coverage": "c8 --reporter=text --reporter=html --reporter=lcov node --test tests/unit/**/*.test.js tests/integration/**/*.test.js",
16
- "test:coverage:text": "c8 --reporter=text node --test tests/unit/**/*.test.js tests/integration/**/*.test.js",
17
- "test:coverage:html": "c8 --reporter=html node --test tests/unit/**/*.test.js tests/integration/**/*.test.js && echo 'Coverage report: coverage/index.html'",
11
+ "test": "node --require ./tests/setup.js --test tests/unit/**/*.test.js tests/integration/**/*.test.js",
12
+ "test:unit": "node --require ./tests/setup.js --test tests/unit/**/*.test.js",
13
+ "test:integration": "node --require ./tests/setup.js --test tests/integration/**/*.test.js",
14
+ "test:watch": "node --require ./tests/setup.js --test --watch tests/unit/**/*.test.js",
15
+ "test:coverage": "c8 --reporter=text --reporter=html --reporter=lcov node --require ./tests/setup.js --test tests/unit/**/*.test.js tests/integration/**/*.test.js",
16
+ "test:coverage:text": "c8 --reporter=text node --require ./tests/setup.js --test tests/unit/**/*.test.js tests/integration/**/*.test.js",
17
+ "test:coverage:html": "c8 --reporter=html node --require ./tests/setup.js --test tests/unit/**/*.test.js tests/integration/**/*.test.js && echo 'Coverage report: coverage/index.html'",
18
18
  "typecheck": "tsc --noEmit"
19
19
  },
20
20
  "devDependencies": {
@@ -22,40 +22,46 @@ const FLUSH_INTERVAL = 30000; // 30 seconds
22
22
  const FLUSH_AT = 10; // flush when 10 events accumulated
23
23
 
24
24
  /**
25
- * Send a batch of events to PostHog via HTTPS POST (fire-and-forget)
25
+ * Send a batch of events to PostHog via HTTPS POST.
26
+ * Returns a promise that resolves when the request completes (or fails).
27
+ * Callers that don't need to wait can ignore the return value.
26
28
  * @param {Array<Record<string, any>>} events
29
+ * @returns {Promise<void>}
27
30
  */
28
31
  function sendBatch(events) {
29
- if (events.length === 0) return;
30
-
31
- const payload = JSON.stringify({ api_key: POSTHOG_API_KEY, batch: events });
32
-
33
- const req = https.request({
34
- hostname: POSTHOG_HOST,
35
- port: 443,
36
- path: '/batch',
37
- method: 'POST',
38
- headers: {
39
- 'Content-Type': 'application/json',
40
- 'Content-Length': Buffer.byteLength(payload),
41
- },
42
- timeout: 5000,
43
- });
32
+ if (events.length === 0) return Promise.resolve();
33
+
34
+ return new Promise((resolve) => {
35
+ const payload = JSON.stringify({ api_key: POSTHOG_API_KEY, batch: events });
36
+
37
+ const req = https.request({
38
+ hostname: POSTHOG_HOST,
39
+ port: 443,
40
+ path: '/batch',
41
+ method: 'POST',
42
+ headers: {
43
+ 'Content-Type': 'application/json',
44
+ 'Content-Length': Buffer.byteLength(payload),
45
+ },
46
+ timeout: 5000,
47
+ });
44
48
 
45
- // Fire-and-forget: ignore all errors and responses
46
- req.on('error', () => {});
47
- req.on('timeout', () => req.destroy());
48
- req.end(payload);
49
+ req.on('error', () => resolve());
50
+ req.on('timeout', () => { req.destroy(); resolve(); });
51
+ req.on('response', () => resolve());
52
+ req.end(payload);
53
+ });
49
54
  }
50
55
 
51
56
  /**
52
- * Flush pending events
57
+ * Flush pending events.
58
+ * @returns {Promise<void>} Resolves when the batch has been sent (or fails).
53
59
  */
54
60
  function flush() {
55
- if (eventQueue.length === 0) return;
61
+ if (eventQueue.length === 0) return Promise.resolve();
56
62
  const batch = eventQueue;
57
63
  eventQueue = [];
58
- sendBatch(batch);
64
+ return sendBatch(batch);
59
65
  }
60
66
 
61
67
  /**
@@ -81,6 +87,15 @@ function queueEvent(event, properties, overrideDistinctId) {
81
87
  }
82
88
  }
83
89
 
90
+ /**
91
+ * Set the app version so that even pre-init events include $lib_version.
92
+ * Call this before promptIfNeeded() so consent events carry the version.
93
+ * @param {string} version
94
+ */
95
+ function setVersion(version) {
96
+ appVersion = version;
97
+ }
98
+
84
99
  /**
85
100
  * Initialize the analytics client if telemetry is enabled
86
101
  * @param {{ version: string }} options
@@ -190,7 +205,7 @@ function captureAlways(event, userDistinctId, properties = {}) {
190
205
  }
191
206
 
192
207
  /**
193
- * Flush pending events and shutdown
208
+ * Flush pending events and shutdown.
194
209
  * Call this before process exit to ensure events are sent.
195
210
  * @returns {Promise<void>}
196
211
  */
@@ -203,7 +218,7 @@ async function shutdown() {
203
218
  if (!enabled) return;
204
219
 
205
220
  try {
206
- flush();
221
+ await flush();
207
222
  } catch {
208
223
  // Best-effort flush
209
224
  } finally {
@@ -220,6 +235,7 @@ function isEnabled() {
220
235
  }
221
236
 
222
237
  module.exports = {
238
+ setVersion,
223
239
  init,
224
240
  capture,
225
241
  captureError,
@@ -83,6 +83,7 @@ async function promptIfNeeded(promptYesNo) {
83
83
 
84
84
  module.exports = {
85
85
  // Analytics
86
+ setVersion: analytics.setVersion,
86
87
  init: analytics.init,
87
88
  capture: analytics.capture,
88
89
  captureError: analytics.captureError,