@hot-updater/plugin-insights 1.0.0-rc.37 → 1.0.0-rc.39
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/server/domain.d.cts +5 -1
- package/dist/server/domain.d.mts +5 -1
- package/dist/server/eventRow.d.cts +4 -3
- package/dist/server/eventRow.d.mts +4 -3
- package/dist/server/reads.cjs +8 -3
- package/dist/server/reads.mjs +8 -3
- package/dist/server/recordEvent.cjs +26 -12
- package/dist/server/recordEvent.mjs +27 -13
- package/package.json +5 -5
package/dist/server/domain.d.cts
CHANGED
|
@@ -123,7 +123,7 @@ type EventHistoryRow = {
|
|
|
123
123
|
readonly patchFallback?: true;
|
|
124
124
|
/** `UNCHANGED`: what the report changed, the reason it was kept. */
|
|
125
125
|
readonly change?: EventHistoryChange;
|
|
126
|
-
/** A download or apply
|
|
126
|
+
/** A download or apply of a target already run or already downloaded: it counted nothing. */
|
|
127
127
|
readonly late?: true;
|
|
128
128
|
/** A launch or crash whose download report never arrived, counted with it. */
|
|
129
129
|
readonly impliedDownload?: true;
|
|
@@ -179,6 +179,10 @@ type ReportingOverview = InsightsScope & {
|
|
|
179
179
|
readonly bundle?: {
|
|
180
180
|
readonly bundleId: string;
|
|
181
181
|
readonly reportingInstallations: InsightsCountMeasurement;
|
|
182
|
+
/**
|
|
183
|
+
* Download reports of the bundle, and in whole hours the downloads a
|
|
184
|
+
* launch or crash implied when its download report never arrived.
|
|
185
|
+
*/
|
|
182
186
|
readonly downloadedReports: InsightsCountMeasurement;
|
|
183
187
|
readonly appliedReports: InsightsCountMeasurement;
|
|
184
188
|
readonly recoveredReports: InsightsCountMeasurement;
|
package/dist/server/domain.d.mts
CHANGED
|
@@ -123,7 +123,7 @@ type EventHistoryRow = {
|
|
|
123
123
|
readonly patchFallback?: true;
|
|
124
124
|
/** `UNCHANGED`: what the report changed, the reason it was kept. */
|
|
125
125
|
readonly change?: EventHistoryChange;
|
|
126
|
-
/** A download or apply
|
|
126
|
+
/** A download or apply of a target already run or already downloaded: it counted nothing. */
|
|
127
127
|
readonly late?: true;
|
|
128
128
|
/** A launch or crash whose download report never arrived, counted with it. */
|
|
129
129
|
readonly impliedDownload?: true;
|
|
@@ -179,6 +179,10 @@ type ReportingOverview = InsightsScope & {
|
|
|
179
179
|
readonly bundle?: {
|
|
180
180
|
readonly bundleId: string;
|
|
181
181
|
readonly reportingInstallations: InsightsCountMeasurement;
|
|
182
|
+
/**
|
|
183
|
+
* Download reports of the bundle, and in whole hours the downloads a
|
|
184
|
+
* launch or crash implied when its download report never arrived.
|
|
185
|
+
*/
|
|
182
186
|
readonly downloadedReports: InsightsCountMeasurement;
|
|
183
187
|
readonly appliedReports: InsightsCountMeasurement;
|
|
184
188
|
readonly recoveredReports: InsightsCountMeasurement;
|
|
@@ -62,9 +62,10 @@ type DatabaseBundleEventMetadata = DatabaseJsonObject & {
|
|
|
62
62
|
/** A kept `UNCHANGED` row: what it changed, which the server sets. */
|
|
63
63
|
readonly change?: BundleEventChange;
|
|
64
64
|
/**
|
|
65
|
-
* A download or apply
|
|
66
|
-
*
|
|
67
|
-
* server keeps it in history, but it moves
|
|
65
|
+
* A download or apply of a target its installation already ran, as when
|
|
66
|
+
* a reload cut the first runtime's report short, or a download that
|
|
67
|
+
* repeats the pending one: the server keeps it in history, but it moves
|
|
68
|
+
* and counts nothing.
|
|
68
69
|
*/
|
|
69
70
|
readonly late?: true;
|
|
70
71
|
/**
|
|
@@ -62,9 +62,10 @@ type DatabaseBundleEventMetadata = DatabaseJsonObject & {
|
|
|
62
62
|
/** A kept `UNCHANGED` row: what it changed, which the server sets. */
|
|
63
63
|
readonly change?: BundleEventChange;
|
|
64
64
|
/**
|
|
65
|
-
* A download or apply
|
|
66
|
-
*
|
|
67
|
-
* server keeps it in history, but it moves
|
|
65
|
+
* A download or apply of a target its installation already ran, as when
|
|
66
|
+
* a reload cut the first runtime's report short, or a download that
|
|
67
|
+
* repeats the pending one: the server keeps it in history, but it moves
|
|
68
|
+
* and counts nothing.
|
|
68
69
|
*/
|
|
69
70
|
readonly late?: true;
|
|
70
71
|
/**
|
package/dist/server/reads.cjs
CHANGED
|
@@ -152,7 +152,11 @@ const countRaw = async (db, filter, from, to) => {
|
|
|
152
152
|
}
|
|
153
153
|
return total;
|
|
154
154
|
};
|
|
155
|
-
/**
|
|
155
|
+
/**
|
|
156
|
+
* Whole hours from outcome counters; the partial hours at either edge from
|
|
157
|
+
* raw events. A download filter's whole hours count the downloads a launch
|
|
158
|
+
* or crash implied too, which its raw events do not hold.
|
|
159
|
+
*/
|
|
156
160
|
const countEvents = async (db, input) => {
|
|
157
161
|
const { filter, sinceMs, beforeReceivedAtMs } = input;
|
|
158
162
|
const start = hourCeil(sinceMs);
|
|
@@ -171,8 +175,9 @@ const countEvents = async (db, input) => {
|
|
|
171
175
|
return await countRaw(db, filter, sinceMs, start) + hours.reduce((sum, row) => sum + row.events, 0) + await countRaw(db, filter, end, beforeReceivedAtMs);
|
|
172
176
|
};
|
|
173
177
|
/**
|
|
174
|
-
* A bundle filter's stored events in each interval of whole hours
|
|
175
|
-
*
|
|
178
|
+
* A bundle filter's stored events in each interval of whole hours, and for a
|
|
179
|
+
* download filter the downloads a launch or crash implied: one read of its
|
|
180
|
+
* hourly counts, every interval present.
|
|
176
181
|
*/
|
|
177
182
|
const countEventSeries = async (db, input) => {
|
|
178
183
|
const { filter, intervalMs } = input;
|
package/dist/server/reads.mjs
CHANGED
|
@@ -152,7 +152,11 @@ const countRaw = async (db, filter, from, to) => {
|
|
|
152
152
|
}
|
|
153
153
|
return total;
|
|
154
154
|
};
|
|
155
|
-
/**
|
|
155
|
+
/**
|
|
156
|
+
* Whole hours from outcome counters; the partial hours at either edge from
|
|
157
|
+
* raw events. A download filter's whole hours count the downloads a launch
|
|
158
|
+
* or crash implied too, which its raw events do not hold.
|
|
159
|
+
*/
|
|
156
160
|
const countEvents = async (db, input) => {
|
|
157
161
|
const { filter, sinceMs, beforeReceivedAtMs } = input;
|
|
158
162
|
const start = hourCeil(sinceMs);
|
|
@@ -171,8 +175,9 @@ const countEvents = async (db, input) => {
|
|
|
171
175
|
return await countRaw(db, filter, sinceMs, start) + hours.reduce((sum, row) => sum + row.events, 0) + await countRaw(db, filter, end, beforeReceivedAtMs);
|
|
172
176
|
};
|
|
173
177
|
/**
|
|
174
|
-
* A bundle filter's stored events in each interval of whole hours
|
|
175
|
-
*
|
|
178
|
+
* A bundle filter's stored events in each interval of whole hours, and for a
|
|
179
|
+
* download filter the downloads a launch or crash implied: one read of its
|
|
180
|
+
* hourly counts, every interval present.
|
|
176
181
|
*/
|
|
177
182
|
const countEventSeries = async (db, input) => {
|
|
178
183
|
const { filter, intervalMs } = input;
|
|
@@ -137,17 +137,25 @@ const countEvent = (tx, event) => {
|
|
|
137
137
|
};
|
|
138
138
|
/**
|
|
139
139
|
* A stored event's outcome rows: its bundle filter's hour, when it has one
|
|
140
|
-
* (`bundleRefsOf`), and every stored event's UTC day.
|
|
140
|
+
* (`bundleRefsOf`), and every stored event's UTC day. A download that a
|
|
141
|
+
* launch or crash implied counts in its bundle's download hour too, in the
|
|
142
|
+
* hour of that launch or crash, so the download series covers what the
|
|
143
|
+
* release's downloads count; the launch or crash is its row.
|
|
141
144
|
*/
|
|
142
145
|
const countOutcome = (tx, event) => {
|
|
143
146
|
const shardBy = event.install_id;
|
|
144
|
-
|
|
147
|
+
const hour = (type, bundleRef) => tx.aggregate("insights_outcomes", {
|
|
145
148
|
platform: event.platform,
|
|
146
149
|
channel: event.channel,
|
|
147
|
-
type
|
|
150
|
+
type,
|
|
148
151
|
bundle_ref: bundleRef,
|
|
149
152
|
bucket_start_ms: hourOf(event.received_at_ms)
|
|
150
153
|
}, { events: 1 }, { shardBy });
|
|
154
|
+
for (const bundleRef of require_schema.bundleRefsOf(event)) hour(event.type, bundleRef);
|
|
155
|
+
if (event.metadata.implied_download === true) hour("UPDATE_DOWNLOADED", require_schema.bundleRefOfFilter({
|
|
156
|
+
type: "UPDATE_DOWNLOADED",
|
|
157
|
+
toBundleId: event.type === "RECOVERED" ? event.from_bundle_id : event.to_bundle_id
|
|
158
|
+
}));
|
|
151
159
|
tx.aggregate("insights_outcomes", {
|
|
152
160
|
...require_schema.DAILY_EVENTS,
|
|
153
161
|
bucket_start_ms: dayOf(event.received_at_ms)
|
|
@@ -262,19 +270,25 @@ const minBundleIdOf = (row) => row.metadata?.min_bundle_id;
|
|
|
262
270
|
/** When the client made a report, from the UUIDv7 it gave it. */
|
|
263
271
|
const madeAt = (id) => (0, _hot_updater_plugin_core.isUUIDv7)(id) ? (0, _hot_updater_plugin_core.extractTimestampFromUUIDv7)(id) : null;
|
|
264
272
|
/**
|
|
265
|
-
* A report
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
273
|
+
* A report its installation already moved past, which stays in history but
|
|
274
|
+
* moves and counts nothing:
|
|
275
|
+
* - a download or an apply of the bundle the head already runs, or a
|
|
276
|
+
* download that repeats the head's pending one, from the same bundle to
|
|
277
|
+
* the same bundle: the installation's download or launch already counted;
|
|
278
|
+
* - an UNCHANGED report made, by its UUIDv7, before the head's report, even
|
|
279
|
+
* after a later report replaced the apply it preceded: a reload can cut
|
|
280
|
+
* one runtime's report short and deliver it after the next runtime's.
|
|
281
|
+
*
|
|
282
|
+
* Report IDs are made on the device, so a device whose clock jumps back has
|
|
283
|
+
* its launch reports judged late until its clock passes its latest report.
|
|
270
284
|
*/
|
|
271
285
|
const isLate = (event, head) => {
|
|
272
286
|
if (head === null) return false;
|
|
273
|
-
if (event.type === "UPDATE_DOWNLOADED" || event.type === "UPDATE_APPLIED") return event.to_bundle_id === runningOf(head).bundle_id;
|
|
274
|
-
if (event.type === "UNCHANGED"
|
|
287
|
+
if (event.type === "UPDATE_DOWNLOADED" || event.type === "UPDATE_APPLIED") return event.to_bundle_id === runningOf(head).bundle_id || event.type === "UPDATE_DOWNLOADED" && head.type === "UPDATE_DOWNLOADED" && event.from_bundle_id === head.from_bundle_id && event.to_bundle_id === head.to_bundle_id;
|
|
288
|
+
if (event.type === "UNCHANGED") {
|
|
275
289
|
const made = madeAt(event.id);
|
|
276
|
-
const
|
|
277
|
-
return made !== null &&
|
|
290
|
+
const latest = madeAt(head.id);
|
|
291
|
+
return made !== null && latest !== null && made < latest;
|
|
278
292
|
}
|
|
279
293
|
return false;
|
|
280
294
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { runningBuiltinBundleId } from "./eventRow.mjs";
|
|
2
|
-
import { DAILY_EVENTS, DAY_MS, HOUR_MS, bundleRefsOf, isLaunch } from "./schema.mjs";
|
|
2
|
+
import { DAILY_EVENTS, DAY_MS, HOUR_MS, bundleRefOfFilter, bundleRefsOf, isLaunch } from "./schema.mjs";
|
|
3
3
|
import { assertBundleEventRow } from "./contract.mjs";
|
|
4
4
|
import { insightsKey, insightsOverviewDeltas, insightsOverviewId } from "./overview.mjs";
|
|
5
5
|
import { addDistinct, compareUtf8, extractTimestampFromUUIDv7, isUUIDv7 } from "@hot-updater/plugin-core";
|
|
@@ -137,17 +137,25 @@ const countEvent = (tx, event) => {
|
|
|
137
137
|
};
|
|
138
138
|
/**
|
|
139
139
|
* A stored event's outcome rows: its bundle filter's hour, when it has one
|
|
140
|
-
* (`bundleRefsOf`), and every stored event's UTC day.
|
|
140
|
+
* (`bundleRefsOf`), and every stored event's UTC day. A download that a
|
|
141
|
+
* launch or crash implied counts in its bundle's download hour too, in the
|
|
142
|
+
* hour of that launch or crash, so the download series covers what the
|
|
143
|
+
* release's downloads count; the launch or crash is its row.
|
|
141
144
|
*/
|
|
142
145
|
const countOutcome = (tx, event) => {
|
|
143
146
|
const shardBy = event.install_id;
|
|
144
|
-
|
|
147
|
+
const hour = (type, bundleRef) => tx.aggregate("insights_outcomes", {
|
|
145
148
|
platform: event.platform,
|
|
146
149
|
channel: event.channel,
|
|
147
|
-
type
|
|
150
|
+
type,
|
|
148
151
|
bundle_ref: bundleRef,
|
|
149
152
|
bucket_start_ms: hourOf(event.received_at_ms)
|
|
150
153
|
}, { events: 1 }, { shardBy });
|
|
154
|
+
for (const bundleRef of bundleRefsOf(event)) hour(event.type, bundleRef);
|
|
155
|
+
if (event.metadata.implied_download === true) hour("UPDATE_DOWNLOADED", bundleRefOfFilter({
|
|
156
|
+
type: "UPDATE_DOWNLOADED",
|
|
157
|
+
toBundleId: event.type === "RECOVERED" ? event.from_bundle_id : event.to_bundle_id
|
|
158
|
+
}));
|
|
151
159
|
tx.aggregate("insights_outcomes", {
|
|
152
160
|
...DAILY_EVENTS,
|
|
153
161
|
bucket_start_ms: dayOf(event.received_at_ms)
|
|
@@ -262,19 +270,25 @@ const minBundleIdOf = (row) => row.metadata?.min_bundle_id;
|
|
|
262
270
|
/** When the client made a report, from the UUIDv7 it gave it. */
|
|
263
271
|
const madeAt = (id) => isUUIDv7(id) ? extractTimestampFromUUIDv7(id) : null;
|
|
264
272
|
/**
|
|
265
|
-
* A report
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
273
|
+
* A report its installation already moved past, which stays in history but
|
|
274
|
+
* moves and counts nothing:
|
|
275
|
+
* - a download or an apply of the bundle the head already runs, or a
|
|
276
|
+
* download that repeats the head's pending one, from the same bundle to
|
|
277
|
+
* the same bundle: the installation's download or launch already counted;
|
|
278
|
+
* - an UNCHANGED report made, by its UUIDv7, before the head's report, even
|
|
279
|
+
* after a later report replaced the apply it preceded: a reload can cut
|
|
280
|
+
* one runtime's report short and deliver it after the next runtime's.
|
|
281
|
+
*
|
|
282
|
+
* Report IDs are made on the device, so a device whose clock jumps back has
|
|
283
|
+
* its launch reports judged late until its clock passes its latest report.
|
|
270
284
|
*/
|
|
271
285
|
const isLate = (event, head) => {
|
|
272
286
|
if (head === null) return false;
|
|
273
|
-
if (event.type === "UPDATE_DOWNLOADED" || event.type === "UPDATE_APPLIED") return event.to_bundle_id === runningOf(head).bundle_id;
|
|
274
|
-
if (event.type === "UNCHANGED"
|
|
287
|
+
if (event.type === "UPDATE_DOWNLOADED" || event.type === "UPDATE_APPLIED") return event.to_bundle_id === runningOf(head).bundle_id || event.type === "UPDATE_DOWNLOADED" && head.type === "UPDATE_DOWNLOADED" && event.from_bundle_id === head.from_bundle_id && event.to_bundle_id === head.to_bundle_id;
|
|
288
|
+
if (event.type === "UNCHANGED") {
|
|
275
289
|
const made = madeAt(event.id);
|
|
276
|
-
const
|
|
277
|
-
return made !== null &&
|
|
290
|
+
const latest = madeAt(head.id);
|
|
291
|
+
return made !== null && latest !== null && made < latest;
|
|
278
292
|
}
|
|
279
293
|
return false;
|
|
280
294
|
};
|
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.0-rc.
|
|
4
|
+
"version": "1.0.0-rc.39",
|
|
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.0-rc.
|
|
36
|
+
"@hot-updater/protocol": "1.0.0-rc.39"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"@hot-updater/plugin-core": "^1.0.0-rc.
|
|
39
|
+
"@hot-updater/plugin-core": "^1.0.0-rc.39"
|
|
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/
|
|
50
|
-
"@hot-updater/
|
|
49
|
+
"@hot-updater/plugin-core": "1.0.0-rc.39",
|
|
50
|
+
"@hot-updater/test-utils": "1.0.0-rc.39"
|
|
51
51
|
},
|
|
52
52
|
"nx": {
|
|
53
53
|
"implicitDependencies": [
|