@hot-updater/plugin-insights 1.0.0-rc.35 → 1.0.0-rc.37

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.
@@ -259,6 +259,38 @@ declare const createInsightsSchema: ({ rawDays, dailyDays }?: InsightsRetention)
259
259
  readonly sort: readonly ["bucket_start_ms"];
260
260
  };
261
261
  }, never, readonly ["channel", "platform", "app_version", "release_id", "bucket_start_ms"]>;
262
+ readonly insights_builtin_distribution: import("@hot-updater/plugin-core").AggregateDefinition<{
263
+ readonly channel: {
264
+ readonly type: "string";
265
+ };
266
+ readonly platform: {
267
+ readonly type: "string";
268
+ readonly maxLength: 16;
269
+ };
270
+ readonly app_version: {
271
+ readonly type: "string";
272
+ };
273
+ readonly release_id: {
274
+ readonly type: "string";
275
+ readonly maxLength: 36;
276
+ };
277
+ readonly builtin_bundle_id: {
278
+ readonly type: "string";
279
+ readonly maxLength: 36;
280
+ };
281
+ readonly bucket_start_ms: {
282
+ readonly type: "integer";
283
+ };
284
+ }, "latest_installations", {
285
+ readonly byScope: {
286
+ readonly eq: readonly ["channel", "platform"];
287
+ readonly sort: readonly ["bucket_start_ms"];
288
+ };
289
+ readonly byVersion: {
290
+ readonly eq: readonly ["channel", "platform", "app_version"];
291
+ readonly sort: readonly ["bucket_start_ms"];
292
+ };
293
+ }, never, readonly ["channel", "platform", "app_version", "release_id", "builtin_bundle_id", "bucket_start_ms"]>;
262
294
  readonly insights_latest_by_bundle: import("@hot-updater/plugin-core").AggregateDefinition<{
263
295
  readonly platform: {
264
296
  readonly type: "string";
@@ -594,6 +626,38 @@ declare const insightsSchema: {
594
626
  readonly sort: readonly ["bucket_start_ms"];
595
627
  };
596
628
  }, never, readonly ["channel", "platform", "app_version", "release_id", "bucket_start_ms"]>;
629
+ readonly insights_builtin_distribution: import("@hot-updater/plugin-core").AggregateDefinition<{
630
+ readonly channel: {
631
+ readonly type: "string";
632
+ };
633
+ readonly platform: {
634
+ readonly type: "string";
635
+ readonly maxLength: 16;
636
+ };
637
+ readonly app_version: {
638
+ readonly type: "string";
639
+ };
640
+ readonly release_id: {
641
+ readonly type: "string";
642
+ readonly maxLength: 36;
643
+ };
644
+ readonly builtin_bundle_id: {
645
+ readonly type: "string";
646
+ readonly maxLength: 36;
647
+ };
648
+ readonly bucket_start_ms: {
649
+ readonly type: "integer";
650
+ };
651
+ }, "latest_installations", {
652
+ readonly byScope: {
653
+ readonly eq: readonly ["channel", "platform"];
654
+ readonly sort: readonly ["bucket_start_ms"];
655
+ };
656
+ readonly byVersion: {
657
+ readonly eq: readonly ["channel", "platform", "app_version"];
658
+ readonly sort: readonly ["bucket_start_ms"];
659
+ };
660
+ }, never, readonly ["channel", "platform", "app_version", "release_id", "builtin_bundle_id", "bucket_start_ms"]>;
597
661
  readonly insights_latest_by_bundle: import("@hot-updater/plugin-core").AggregateDefinition<{
598
662
  readonly platform: {
599
663
  readonly type: "string";
@@ -259,6 +259,38 @@ declare const createInsightsSchema: ({ rawDays, dailyDays }?: InsightsRetention)
259
259
  readonly sort: readonly ["bucket_start_ms"];
260
260
  };
261
261
  }, never, readonly ["channel", "platform", "app_version", "release_id", "bucket_start_ms"]>;
262
+ readonly insights_builtin_distribution: import("@hot-updater/plugin-core").AggregateDefinition<{
263
+ readonly channel: {
264
+ readonly type: "string";
265
+ };
266
+ readonly platform: {
267
+ readonly type: "string";
268
+ readonly maxLength: 16;
269
+ };
270
+ readonly app_version: {
271
+ readonly type: "string";
272
+ };
273
+ readonly release_id: {
274
+ readonly type: "string";
275
+ readonly maxLength: 36;
276
+ };
277
+ readonly builtin_bundle_id: {
278
+ readonly type: "string";
279
+ readonly maxLength: 36;
280
+ };
281
+ readonly bucket_start_ms: {
282
+ readonly type: "integer";
283
+ };
284
+ }, "latest_installations", {
285
+ readonly byScope: {
286
+ readonly eq: readonly ["channel", "platform"];
287
+ readonly sort: readonly ["bucket_start_ms"];
288
+ };
289
+ readonly byVersion: {
290
+ readonly eq: readonly ["channel", "platform", "app_version"];
291
+ readonly sort: readonly ["bucket_start_ms"];
292
+ };
293
+ }, never, readonly ["channel", "platform", "app_version", "release_id", "builtin_bundle_id", "bucket_start_ms"]>;
262
294
  readonly insights_latest_by_bundle: import("@hot-updater/plugin-core").AggregateDefinition<{
263
295
  readonly platform: {
264
296
  readonly type: "string";
@@ -594,6 +626,38 @@ declare const insightsSchema: {
594
626
  readonly sort: readonly ["bucket_start_ms"];
595
627
  };
596
628
  }, never, readonly ["channel", "platform", "app_version", "release_id", "bucket_start_ms"]>;
629
+ readonly insights_builtin_distribution: import("@hot-updater/plugin-core").AggregateDefinition<{
630
+ readonly channel: {
631
+ readonly type: "string";
632
+ };
633
+ readonly platform: {
634
+ readonly type: "string";
635
+ readonly maxLength: 16;
636
+ };
637
+ readonly app_version: {
638
+ readonly type: "string";
639
+ };
640
+ readonly release_id: {
641
+ readonly type: "string";
642
+ readonly maxLength: 36;
643
+ };
644
+ readonly builtin_bundle_id: {
645
+ readonly type: "string";
646
+ readonly maxLength: 36;
647
+ };
648
+ readonly bucket_start_ms: {
649
+ readonly type: "integer";
650
+ };
651
+ }, "latest_installations", {
652
+ readonly byScope: {
653
+ readonly eq: readonly ["channel", "platform"];
654
+ readonly sort: readonly ["bucket_start_ms"];
655
+ };
656
+ readonly byVersion: {
657
+ readonly eq: readonly ["channel", "platform", "app_version"];
658
+ readonly sort: readonly ["bucket_start_ms"];
659
+ };
660
+ }, never, readonly ["channel", "platform", "app_version", "release_id", "builtin_bundle_id", "bucket_start_ms"]>;
597
661
  readonly insights_latest_by_bundle: import("@hot-updater/plugin-core").AggregateDefinition<{
598
662
  readonly platform: {
599
663
  readonly type: "string";
@@ -10,18 +10,53 @@ const bucketRetention = (days) => ({
10
10
  field: "bucket_start_ms",
11
11
  days
12
12
  });
13
+ /** Every kind of row the table keeps belongs in installation history. */
13
14
  const MOVEMENTS = /* @__PURE__ */ new Set([
14
15
  "UPDATE_DOWNLOADED",
15
16
  "UPDATE_APPLIED",
16
17
  "RECOVERED",
17
- "UPDATE_FAILED"
18
+ "UPDATE_FAILED",
19
+ "UNCHANGED"
18
20
  ]);
19
21
  /** A failed update check: it names no target bundle, so no bundle list shows it. */
20
22
  const isFailedCheck = (row) => row.type === "UPDATE_FAILED" && row.metadata?.failure?.stage === "check";
23
+ const metadataOf = (row) => row.metadata ?? {};
24
+ /** A download or apply that arrived after its target ran: it counts nothing. */
25
+ const isLateEvent = (row) => metadataOf(row).late === true;
21
26
  /**
22
- * Downloads, applies, recoveries, and update failures, whole. An UNCHANGED
23
- * report counts as a launch and moves its installation's head, but no list
24
- * shows it, so no row keeps it.
27
+ * Whether a stored row launched its installation into a bundle release: an
28
+ * apply, or a kept UNCHANGED row that moved the installation to another
29
+ * bundle of a release, as when the apply report never arrived or a rollback
30
+ * to the built-in bundle took it there. A first report, a release change of
31
+ * the bundle already running, and a late row launch nothing.
32
+ */
33
+ const isLaunch = (row) => {
34
+ if (isLateEvent(row)) return false;
35
+ if (row.type === "UPDATE_APPLIED") return true;
36
+ if (row.type !== "UNCHANGED") return false;
37
+ const kinds = metadataOf(row).change?.kinds ?? [];
38
+ return kinds.includes("bundle") && !kinds.includes("first_seen") && row.to_release_id !== null;
39
+ };
40
+ /**
41
+ * The bundle a stored row's bundle filter matches: `from:<bundle>` for a
42
+ * recovery; `to:<bundle>` for a download, an apply, and an update failure;
43
+ * `on:<bundle>` for an UNCHANGED row that launched a bundle (`isLaunch`), a
44
+ * key no other row uses. None for a failed check, which targets no bundle, a
45
+ * late row, or any other UNCHANGED row.
46
+ */
47
+ const bundleRefsOf = (row) => {
48
+ if (isFailedCheck(row) || isLateEvent(row)) return [];
49
+ if (row.type === "RECOVERED") return [`from:${row.from_bundle_id}`];
50
+ if (row.type === "UNCHANGED") return isLaunch(row) ? [`on:${row.to_bundle_id}`] : [];
51
+ return [`to:${row.to_bundle_id}`];
52
+ };
53
+ /** The key a bundle filter reads, as `bundleRefsOf` writes it. */
54
+ const bundleRefOfFilter = (filter) => filter.type === "RECOVERED" ? `from:${filter.fromBundleId}` : filter.type === "UNCHANGED" ? `on:${filter.toBundleId}` : `to:${filter.toBundleId}`;
55
+ /**
56
+ * Downloads, applies, recoveries, update failures, and the UNCHANGED
57
+ * reports that changed what their installation runs, whole. An UNCHANGED
58
+ * report that changes nothing counts as a launch and moves its
59
+ * installation's head, but no row keeps it.
25
60
  */
26
61
  const bundleEvents = (days) => defineTable({
27
62
  id: {
@@ -76,19 +111,16 @@ const bundleEvents = (days) => defineTable({
76
111
  type: "integer",
77
112
  compute: (row) => row.received_at_ms - row.received_at_ms % DAY_MS
78
113
  },
79
- /** Set only for events that belong in installation history. */
114
+ /** Every stored event belongs in installation history. */
80
115
  movement_install_id: {
81
116
  type: "string",
82
117
  compute: (row) => MOVEMENTS.has(row.type) ? row.install_id : null
83
118
  },
84
- /**
85
- * The bundle a bundle filter matches: `from:<bundle>` for RECOVERED,
86
- * none for a failed check, else `to:<bundle>`.
87
- */
119
+ /** The bundle a bundle filter matches (`bundleRefsOf`). */
88
120
  bundle_ref: {
89
121
  type: "string",
90
122
  multi: true,
91
- compute: (row) => isFailedCheck(row) ? [] : [row.type === "RECOVERED" && row.from_bundle_id !== null ? `from:${row.from_bundle_id}` : `to:${row.to_bundle_id}`]
123
+ compute: (row) => bundleRefsOf(row)
92
124
  }
93
125
  },
94
126
  indexes: {
@@ -354,6 +386,55 @@ const insightsDistribution = (retention) => defineAggregate({
354
386
  },
355
387
  retention
356
388
  });
389
+ /**
390
+ * The installations of `insights_distribution` that run their native build's
391
+ * built-in bundle, by its ID (`builtin_bundle_id`), within the same rows:
392
+ * reads move them out of the row of their release, or of no release.
393
+ */
394
+ const insightsBuiltinDistribution = (retention) => defineAggregate({
395
+ channel: { type: "string" },
396
+ platform: {
397
+ type: "string",
398
+ maxLength: 16
399
+ },
400
+ app_version: { type: "string" },
401
+ release_id: {
402
+ type: "string",
403
+ maxLength: 36
404
+ },
405
+ builtin_bundle_id: {
406
+ type: "string",
407
+ maxLength: 36
408
+ },
409
+ bucket_start_ms: { type: "integer" }
410
+ }, {
411
+ key: [
412
+ "channel",
413
+ "platform",
414
+ "app_version",
415
+ "release_id",
416
+ "builtin_bundle_id",
417
+ "bucket_start_ms"
418
+ ],
419
+ gauges: ["latest_installations"],
420
+ shards: GAUGE_SHARDS,
421
+ batched: true,
422
+ indexes: {
423
+ byScope: {
424
+ eq: ["channel", "platform"],
425
+ sort: ["bucket_start_ms"]
426
+ },
427
+ byVersion: {
428
+ eq: [
429
+ "channel",
430
+ "platform",
431
+ "app_version"
432
+ ],
433
+ sort: ["bucket_start_ms"]
434
+ }
435
+ },
436
+ retention
437
+ });
357
438
  /** Latest events by the bundle they came from or went to, bucketed like the distribution. */
358
439
  const insightsLatestByBundle = (retention) => defineAggregate({
359
440
  platform: {
@@ -473,6 +554,7 @@ const createInsightsSchema = ({ rawDays, dailyDays } = DEFAULT_INSIGHTS_RETENTIO
473
554
  insights_overview_lifetime: lifetimeCounters(),
474
555
  insights_sketches_lifetime: lifetimeSketches(),
475
556
  insights_distribution: insightsDistribution(daily),
557
+ insights_builtin_distribution: insightsBuiltinDistribution(daily),
476
558
  insights_latest_by_bundle: insightsLatestByBundle(daily),
477
559
  insights_outcomes: insightsOutcomes(hourly),
478
560
  insights_failures: insightsFailures(hourly)
@@ -480,4 +562,4 @@ const createInsightsSchema = ({ rawDays, dailyDays } = DEFAULT_INSIGHTS_RETENTIO
480
562
  };
481
563
  const insightsSchema = createInsightsSchema();
482
564
  //#endregion
483
- export { DAILY_EVENTS, DAY_MS, DEFAULT_INSIGHTS_RETENTION, HOUR_MS, createInsightsSchema, insightsSchema, isFailedCheck };
565
+ export { DAILY_EVENTS, DAY_MS, DEFAULT_INSIGHTS_RETENTION, HOUR_MS, bundleRefOfFilter, bundleRefsOf, createInsightsSchema, insightsSchema, isFailedCheck, isLateEvent, isLaunch };
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.35",
4
+ "version": "1.0.0-rc.37",
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.35"
36
+ "@hot-updater/protocol": "1.0.0-rc.37"
37
37
  },
38
38
  "peerDependencies": {
39
- "@hot-updater/plugin-core": "^1.0.0-rc.35"
39
+ "@hot-updater/plugin-core": "^1.0.0-rc.37"
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/test-utils": "1.0.0-rc.35",
50
- "@hot-updater/plugin-core": "1.0.0-rc.35"
49
+ "@hot-updater/test-utils": "1.0.0-rc.37",
50
+ "@hot-updater/plugin-core": "1.0.0-rc.37"
51
51
  },
52
52
  "nx": {
53
53
  "implicitDependencies": [