@hot-updater/plugin-insights 1.0.7 → 1.1.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.
@@ -12,18 +12,20 @@ const formatUUIDv7 = (bytes, timestamp) => {
12
12
  };
13
13
  /**
14
14
  * Creates an RFC 9562 UUIDv7: a 48-bit millisecond timestamp, then random bits.
15
+ * `timestamp` is when the event happened, now unless native code recorded it
16
+ * earlier.
15
17
  *
16
18
  * `createUUIDv7` from `@hot-updater/plugin-core` calls `crypto.getRandomValues`
17
19
  * unconditionally, but React Native has no Web Crypto unless the app installs a
18
20
  * polyfill. An Insights event ID only has to tell one event from another so a
19
21
  * retried POST is counted once, so `Math.random` is enough without one.
20
22
  */
21
- const createUUIDv7 = () => {
23
+ const createUUIDv7 = (timestamp = Date.now()) => {
22
24
  const bytes = /* @__PURE__ */ new Uint8Array(16);
23
25
  const { crypto } = globalThis;
24
26
  if (typeof crypto?.getRandomValues === "function") crypto.getRandomValues(bytes);
25
27
  else for (let index = 0; index < bytes.length; index += 1) bytes[index] = Math.floor(Math.random() * 256);
26
- return formatUUIDv7(bytes, Date.now());
28
+ return formatUUIDv7(bytes, timestamp);
27
29
  };
28
30
  const SHA256_K = [
29
31
  1116352408,
@@ -12,18 +12,20 @@ const formatUUIDv7 = (bytes, timestamp) => {
12
12
  };
13
13
  /**
14
14
  * Creates an RFC 9562 UUIDv7: a 48-bit millisecond timestamp, then random bits.
15
+ * `timestamp` is when the event happened, now unless native code recorded it
16
+ * earlier.
15
17
  *
16
18
  * `createUUIDv7` from `@hot-updater/plugin-core` calls `crypto.getRandomValues`
17
19
  * unconditionally, but React Native has no Web Crypto unless the app installs a
18
20
  * polyfill. An Insights event ID only has to tell one event from another so a
19
21
  * retried POST is counted once, so `Math.random` is enough without one.
20
22
  */
21
- const createUUIDv7 = () => {
23
+ const createUUIDv7 = (timestamp = Date.now()) => {
22
24
  const bytes = /* @__PURE__ */ new Uint8Array(16);
23
25
  const { crypto } = globalThis;
24
26
  if (typeof crypto?.getRandomValues === "function") crypto.getRandomValues(bytes);
25
27
  else for (let index = 0; index < bytes.length; index += 1) bytes[index] = Math.floor(Math.random() * 256);
26
- return formatUUIDv7(bytes, Date.now());
28
+ return formatUUIDv7(bytes, timestamp);
27
29
  };
28
30
  const SHA256_K = [
29
31
  1116352408,
@@ -42,14 +42,18 @@ const setUpInsights = (options, pluginContext) => {
42
42
  let lastDownloadedSelection = null;
43
43
  const pending = /* @__PURE__ */ new Map();
44
44
  let send = null;
45
- const createEvent = (movement, eventId) => {
45
+ /**
46
+ * `occurredAt` dates an event native code recorded before this runtime
47
+ * started, such as a download a native `HotUpdaterInstance` finished.
48
+ */
49
+ const createEvent = (movement, eventId, occurredAt) => {
46
50
  if (context === null || state === null) return null;
47
51
  const appVersion = context.appVersion;
48
52
  if (appVersion === null) {
49
53
  console.warn(`[HotUpdater] Insights needs the native app version; the ${movement.type} event was not sent.`);
50
54
  return null;
51
55
  }
52
- const now = context.now();
56
+ const now = occurredAt ?? context.now();
53
57
  const installId = context.installId;
54
58
  const userId = state.readUserId();
55
59
  const response = state.readHttpResponse(movement.channel, appVersion);
@@ -59,7 +63,7 @@ const setUpInsights = (options, pluginContext) => {
59
63
  ...movement.metadata,
60
64
  httpResponse: response
61
65
  } } : {},
62
- eventId: eventId?.(now, installId) ?? require_eventId.createUUIDv7(),
66
+ eventId: eventId?.(now, installId) ?? require_eventId.createUUIDv7(now),
63
67
  installId,
64
68
  ...userId === null ? {} : { userId },
65
69
  platform: context.platform,
@@ -95,7 +99,7 @@ const setUpInsights = (options, pluginContext) => {
95
99
  console.warn(`[HotUpdater] Insights ${event.body.type} event was not delivered:`, error);
96
100
  });
97
101
  };
98
- const reportLaunch = (channel, bundleId, releaseId, fromReleaseId) => {
102
+ const reportLaunch = (channel, bundleId, releaseId, fromReleaseId, occurredAt) => {
99
103
  if (didDownload) return;
100
104
  enqueue(createEvent({
101
105
  type: "UNCHANGED",
@@ -105,7 +109,7 @@ const setUpInsights = (options, pluginContext) => {
105
109
  toBundleId: bundleId,
106
110
  toReleaseId: releaseId,
107
111
  updateStrategy: null
108
- }));
112
+ }, void 0, occurredAt));
109
113
  };
110
114
  const onAppReady = (result) => {
111
115
  if (result.status === "UNCHANGED") {
@@ -124,7 +128,7 @@ const setUpInsights = (options, pluginContext) => {
124
128
  };
125
129
  const onUpdateCheck = (result) => {
126
130
  if (result.status !== "UNCHANGED") return;
127
- reportLaunch(result.channel, result.bundleId, result.releaseId, result.previousReleaseId === result.releaseId ? null : result.previousReleaseId);
131
+ reportLaunch(result.channel, result.bundleId, result.releaseId, result.previousReleaseId === result.releaseId ? null : result.previousReleaseId, result.occurredAt);
128
132
  };
129
133
  const onBundleDownloaded = (info) => {
130
134
  const selection = JSON.stringify([
@@ -147,7 +151,7 @@ const setUpInsights = (options, pluginContext) => {
147
151
  delivery: info.delivery,
148
152
  ...info.patchFallback ? { patchFallback: true } : {}
149
153
  }
150
- }));
154
+ }, void 0, info.occurredAt));
151
155
  };
152
156
  const onUpdateError = (error) => {
153
157
  if (error.stage === "check" && error.reason === "network") return;
@@ -180,7 +184,7 @@ const setUpInsights = (options, pluginContext) => {
180
184
  installId,
181
185
  require_state.utcDay(now),
182
186
  failureKey
183
- ].join("\n"), require_state.utcDayStart(now)));
187
+ ].join("\n"), require_state.utcDayStart(now)), error.occurredAt);
184
188
  enqueue(event === null ? null : {
185
189
  ...event,
186
190
  failureKey
@@ -189,12 +193,14 @@ const setUpInsights = (options, pluginContext) => {
189
193
  const onHttpResponse = (response) => {
190
194
  if (context === null || state === null || context.appVersion === null) return;
191
195
  const body = response.body === null ? null : require_errorDetails.boundedText(response.body, 4096);
192
- state.recordHttpResponse(context.getChannel(), context.appVersion, {
193
- ...response,
196
+ const channel = response.channel ?? context.getChannel();
197
+ state.recordHttpResponse(channel, context.appVersion, {
198
+ resource: response.resource,
199
+ status: response.status,
194
200
  path: require_errorDetails.boundedText(response.path, 1024),
195
201
  body,
196
202
  bodyTruncated: response.bodyTruncated || body !== response.body,
197
- receivedAtMs: context.now()
203
+ receivedAtMs: response.occurredAt ?? context.now()
198
204
  });
199
205
  };
200
206
  const admit = (body) => {
@@ -41,14 +41,18 @@ const setUpInsights = (options, pluginContext) => {
41
41
  let lastDownloadedSelection = null;
42
42
  const pending = /* @__PURE__ */ new Map();
43
43
  let send = null;
44
- const createEvent = (movement, eventId) => {
44
+ /**
45
+ * `occurredAt` dates an event native code recorded before this runtime
46
+ * started, such as a download a native `HotUpdaterInstance` finished.
47
+ */
48
+ const createEvent = (movement, eventId, occurredAt) => {
45
49
  if (context === null || state === null) return null;
46
50
  const appVersion = context.appVersion;
47
51
  if (appVersion === null) {
48
52
  console.warn(`[HotUpdater] Insights needs the native app version; the ${movement.type} event was not sent.`);
49
53
  return null;
50
54
  }
51
- const now = context.now();
55
+ const now = occurredAt ?? context.now();
52
56
  const installId = context.installId;
53
57
  const userId = state.readUserId();
54
58
  const response = state.readHttpResponse(movement.channel, appVersion);
@@ -58,7 +62,7 @@ const setUpInsights = (options, pluginContext) => {
58
62
  ...movement.metadata,
59
63
  httpResponse: response
60
64
  } } : {},
61
- eventId: eventId?.(now, installId) ?? createUUIDv7(),
65
+ eventId: eventId?.(now, installId) ?? createUUIDv7(now),
62
66
  installId,
63
67
  ...userId === null ? {} : { userId },
64
68
  platform: context.platform,
@@ -94,7 +98,7 @@ const setUpInsights = (options, pluginContext) => {
94
98
  console.warn(`[HotUpdater] Insights ${event.body.type} event was not delivered:`, error);
95
99
  });
96
100
  };
97
- const reportLaunch = (channel, bundleId, releaseId, fromReleaseId) => {
101
+ const reportLaunch = (channel, bundleId, releaseId, fromReleaseId, occurredAt) => {
98
102
  if (didDownload) return;
99
103
  enqueue(createEvent({
100
104
  type: "UNCHANGED",
@@ -104,7 +108,7 @@ const setUpInsights = (options, pluginContext) => {
104
108
  toBundleId: bundleId,
105
109
  toReleaseId: releaseId,
106
110
  updateStrategy: null
107
- }));
111
+ }, void 0, occurredAt));
108
112
  };
109
113
  const onAppReady = (result) => {
110
114
  if (result.status === "UNCHANGED") {
@@ -123,7 +127,7 @@ const setUpInsights = (options, pluginContext) => {
123
127
  };
124
128
  const onUpdateCheck = (result) => {
125
129
  if (result.status !== "UNCHANGED") return;
126
- reportLaunch(result.channel, result.bundleId, result.releaseId, result.previousReleaseId === result.releaseId ? null : result.previousReleaseId);
130
+ reportLaunch(result.channel, result.bundleId, result.releaseId, result.previousReleaseId === result.releaseId ? null : result.previousReleaseId, result.occurredAt);
127
131
  };
128
132
  const onBundleDownloaded = (info) => {
129
133
  const selection = JSON.stringify([
@@ -146,7 +150,7 @@ const setUpInsights = (options, pluginContext) => {
146
150
  delivery: info.delivery,
147
151
  ...info.patchFallback ? { patchFallback: true } : {}
148
152
  }
149
- }));
153
+ }, void 0, info.occurredAt));
150
154
  };
151
155
  const onUpdateError = (error) => {
152
156
  if (error.stage === "check" && error.reason === "network") return;
@@ -179,7 +183,7 @@ const setUpInsights = (options, pluginContext) => {
179
183
  installId,
180
184
  utcDay(now),
181
185
  failureKey
182
- ].join("\n"), utcDayStart(now)));
186
+ ].join("\n"), utcDayStart(now)), error.occurredAt);
183
187
  enqueue(event === null ? null : {
184
188
  ...event,
185
189
  failureKey
@@ -188,12 +192,14 @@ const setUpInsights = (options, pluginContext) => {
188
192
  const onHttpResponse = (response) => {
189
193
  if (context === null || state === null || context.appVersion === null) return;
190
194
  const body = response.body === null ? null : boundedText(response.body, 4096);
191
- state.recordHttpResponse(context.getChannel(), context.appVersion, {
192
- ...response,
195
+ const channel = response.channel ?? context.getChannel();
196
+ state.recordHttpResponse(channel, context.appVersion, {
197
+ resource: response.resource,
198
+ status: response.status,
193
199
  path: boundedText(response.path, 1024),
194
200
  body,
195
201
  bodyTruncated: response.bodyTruncated || body !== response.body,
196
- receivedAtMs: context.now()
202
+ receivedAtMs: response.occurredAt ?? context.now()
197
203
  });
198
204
  };
199
205
  const admit = (body) => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hot-updater/plugin-insights",
3
3
  "type": "module",
4
- "version": "1.0.7",
4
+ "version": "1.1.0",
5
5
  "description": "Hot Updater's Insights plugin: launch, download, and update failure events, for the server and the React Native app",
6
6
  "exports": {
7
7
  "./client": {
@@ -33,10 +33,10 @@
33
33
  "package.json"
34
34
  ],
35
35
  "dependencies": {
36
- "@hot-updater/protocol": "1.0.7"
36
+ "@hot-updater/protocol": "1.1.0"
37
37
  },
38
38
  "peerDependencies": {
39
- "@hot-updater/plugin-core": "^1.0.7"
39
+ "@hot-updater/plugin-core": "^1.1.0"
40
40
  },
41
41
  "peerDependenciesMeta": {
42
42
  "@hot-updater/plugin-core": {
@@ -46,8 +46,8 @@
46
46
  "devDependencies": {
47
47
  "@types/node": "^20",
48
48
  "vitest": "4.1.4",
49
- "@hot-updater/plugin-core": "1.0.7",
50
- "@hot-updater/test-utils": "1.0.7"
49
+ "@hot-updater/plugin-core": "1.1.0",
50
+ "@hot-updater/test-utils": "1.1.0"
51
51
  },
52
52
  "nx": {
53
53
  "implicitDependencies": [