@hot-updater/plugin-insights 1.0.8 → 1.1.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.
- package/dist/client/eventId.cjs +4 -2
- package/dist/client/eventId.mjs +4 -2
- package/dist/client/index.cjs +17 -11
- package/dist/client/index.mjs +17 -11
- package/package.json +5 -5
package/dist/client/eventId.cjs
CHANGED
|
@@ -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,
|
|
28
|
+
return formatUUIDv7(bytes, timestamp);
|
|
27
29
|
};
|
|
28
30
|
const SHA256_K = [
|
|
29
31
|
1116352408,
|
package/dist/client/eventId.mjs
CHANGED
|
@@ -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,
|
|
28
|
+
return formatUUIDv7(bytes, timestamp);
|
|
27
29
|
};
|
|
28
30
|
const SHA256_K = [
|
|
29
31
|
1116352408,
|
package/dist/client/index.cjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
193
|
-
|
|
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) => {
|
package/dist/client/index.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
192
|
-
|
|
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.
|
|
4
|
+
"version": "1.1.1",
|
|
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.
|
|
36
|
+
"@hot-updater/protocol": "1.1.1"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"@hot-updater/plugin-core": "^1.
|
|
39
|
+
"@hot-updater/plugin-core": "^1.1.1"
|
|
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.
|
|
50
|
-
"@hot-updater/test-utils": "1.
|
|
49
|
+
"@hot-updater/plugin-core": "1.1.1",
|
|
50
|
+
"@hot-updater/test-utils": "1.1.1"
|
|
51
51
|
},
|
|
52
52
|
"nx": {
|
|
53
53
|
"implicitDependencies": [
|