@hot-updater/plugin-insights 1.0.0-rc.31 → 1.0.0-rc.33

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.
@@ -166,7 +166,7 @@ const createValidatedInsightsModel = (model) => ({
166
166
  async getReleaseActivity(input) {
167
167
  if (!require_eventRow.isRecord(input) || !hasOnlyKeys(input, ["releases"]) || !Array.isArray(input.releases) || input.releases.length < 1 || input.releases.length > 100 || !input.releases.every(isReleaseReference)) invalidQuery();
168
168
  const result = await model.getReleaseActivity(input);
169
- if (!require_eventRow.isRecord(result) || !isTimestamp(result.measuredAtMs) || !Array.isArray(result.data) || result.data.some((item) => !require_eventRow.isRecord(item) || !isReleaseReference(item.release) || !require_eventRow.isRecord(item.metrics) || !isTimestamp(item.metrics.downloads) || !isTimestamp(item.metrics.launches) || !isTimestamp(item.metrics.failedLaunches))) invalidResult();
169
+ if (!require_eventRow.isRecord(result) || !isTimestamp(result.measuredAtMs) || !Array.isArray(result.data) || result.data.some((item) => !require_eventRow.isRecord(item) || !isReleaseReference(item.release) || !require_eventRow.isRecord(item.metrics) || !isTimestamp(item.metrics.downloads) || !isTimestamp(item.metrics.applies) || !isTimestamp(item.metrics.failedLaunches))) invalidResult();
170
170
  return result;
171
171
  },
172
172
  async getAppUsage(input) {
@@ -166,7 +166,7 @@ const createValidatedInsightsModel = (model) => ({
166
166
  async getReleaseActivity(input) {
167
167
  if (!isRecord(input) || !hasOnlyKeys(input, ["releases"]) || !Array.isArray(input.releases) || input.releases.length < 1 || input.releases.length > 100 || !input.releases.every(isReleaseReference)) invalidQuery();
168
168
  const result = await model.getReleaseActivity(input);
169
- if (!isRecord(result) || !isTimestamp(result.measuredAtMs) || !Array.isArray(result.data) || result.data.some((item) => !isRecord(item) || !isReleaseReference(item.release) || !isRecord(item.metrics) || !isTimestamp(item.metrics.downloads) || !isTimestamp(item.metrics.launches) || !isTimestamp(item.metrics.failedLaunches))) invalidResult();
169
+ if (!isRecord(result) || !isTimestamp(result.measuredAtMs) || !Array.isArray(result.data) || result.data.some((item) => !isRecord(item) || !isReleaseReference(item.release) || !isRecord(item.metrics) || !isTimestamp(item.metrics.downloads) || !isTimestamp(item.metrics.applies) || !isTimestamp(item.metrics.failedLaunches))) invalidResult();
170
170
  return result;
171
171
  },
172
172
  async getAppUsage(input) {
@@ -88,7 +88,7 @@ const insights = (options = {}) => {
88
88
  const plugin = (0, _hot_updater_plugin_core.definePlugin)({
89
89
  id: "insights",
90
90
  namespace: false,
91
- schemaVersion: "1.2.0",
91
+ schemaVersion: "1.4.0",
92
92
  schema: require_schema.createInsightsSchema(retention),
93
93
  init: ({ db, now }) => {
94
94
  const api = createInsightsApi(db, now, retention);
@@ -180,7 +180,7 @@ declare const insights: (options?: InsightsOptions) => import("@hot-updater/plug
180
180
  readonly bucket_start_ms: {
181
181
  readonly type: "integer";
182
182
  };
183
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
183
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks", {
184
184
  readonly window: {
185
185
  readonly eq: readonly ["identity"];
186
186
  readonly sort: readonly ["bucket_start_ms"];
@@ -208,7 +208,7 @@ declare const insights: (options?: InsightsOptions) => import("@hot-updater/plug
208
208
  readonly bucket_start_ms: {
209
209
  readonly type: "integer";
210
210
  };
211
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
211
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks", {
212
212
  readonly window: {
213
213
  readonly eq: readonly ["identity"];
214
214
  readonly sort: readonly ["bucket_start_ms"];
@@ -236,7 +236,7 @@ declare const insights: (options?: InsightsOptions) => import("@hot-updater/plug
236
236
  readonly bucket_start_ms: {
237
237
  readonly type: "integer";
238
238
  };
239
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
239
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks" | "applies" | "failed_updates", {
240
240
  readonly window: {
241
241
  readonly eq: readonly ["identity"];
242
242
  readonly sort: readonly ["bucket_start_ms"];
@@ -180,7 +180,7 @@ declare const insights: (options?: InsightsOptions) => import("@hot-updater/plug
180
180
  readonly bucket_start_ms: {
181
181
  readonly type: "integer";
182
182
  };
183
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
183
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks", {
184
184
  readonly window: {
185
185
  readonly eq: readonly ["identity"];
186
186
  readonly sort: readonly ["bucket_start_ms"];
@@ -208,7 +208,7 @@ declare const insights: (options?: InsightsOptions) => import("@hot-updater/plug
208
208
  readonly bucket_start_ms: {
209
209
  readonly type: "integer";
210
210
  };
211
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
211
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks", {
212
212
  readonly window: {
213
213
  readonly eq: readonly ["identity"];
214
214
  readonly sort: readonly ["bucket_start_ms"];
@@ -236,7 +236,7 @@ declare const insights: (options?: InsightsOptions) => import("@hot-updater/plug
236
236
  readonly bucket_start_ms: {
237
237
  readonly type: "integer";
238
238
  };
239
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
239
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks" | "applies" | "failed_updates", {
240
240
  readonly window: {
241
241
  readonly eq: readonly ["identity"];
242
242
  readonly sort: readonly ["bucket_start_ms"];
@@ -87,7 +87,7 @@ const insights = (options = {}) => {
87
87
  const plugin = definePlugin({
88
88
  id: "insights",
89
89
  namespace: false,
90
- schemaVersion: "1.2.0",
90
+ schemaVersion: "1.4.0",
91
91
  schema: createInsightsSchema(retention),
92
92
  init: ({ db, now }) => {
93
93
  const api = createInsightsApi(db, now, retention);
@@ -95,8 +95,11 @@ type InsightsCoverage = {
95
95
  * which are kept.
96
96
  */
97
97
  interface ReleaseActivityMetrics {
98
+ /** Download reports of the release. */
98
99
  readonly downloads: number;
99
- readonly launches: number;
100
+ /** Apply reports of the release: installations that started running it. */
101
+ readonly applies: number;
102
+ /** Launches that crashed on the release and recovered to the one before. */
100
103
  readonly failedLaunches: number;
101
104
  }
102
105
  interface InsightsGetReleaseActivityInput {
@@ -95,8 +95,11 @@ type InsightsCoverage = {
95
95
  * which are kept.
96
96
  */
97
97
  interface ReleaseActivityMetrics {
98
+ /** Download reports of the release. */
98
99
  readonly downloads: number;
99
- readonly launches: number;
100
+ /** Apply reports of the release: installations that started running it. */
101
+ readonly applies: number;
102
+ /** Launches that crashed on the release and recovered to the one before. */
100
103
  readonly failedLaunches: number;
101
104
  }
102
105
  interface InsightsGetReleaseActivityInput {
@@ -31,7 +31,7 @@ const insightsOverviewDeltas = (event) => {
31
31
  counters.set(key, {
32
32
  identity,
33
33
  downloads: (previous?.downloads ?? 0) + values.downloads,
34
- launches: (previous?.launches ?? 0) + values.launches,
34
+ applies: (previous?.applies ?? 0) + values.applies,
35
35
  failedLaunches: (previous?.failedLaunches ?? 0) + values.failedLaunches,
36
36
  activityIdentity: previous?.activityIdentity ?? values.activityIdentity
37
37
  });
@@ -39,7 +39,7 @@ const insightsOverviewDeltas = (event) => {
39
39
  const metric = (releaseId, values) => {
40
40
  const windowed = {
41
41
  ...values,
42
- launches: 0
42
+ applies: 0
43
43
  };
44
44
  if (releaseId !== null) for (const [periodKind, start] of [["lifetime", 0], ["hour", bucketStartMs]]) append({
45
45
  scopeKind: "release",
@@ -66,21 +66,19 @@ const insightsOverviewDeltas = (event) => {
66
66
  };
67
67
  if (event.type === "UPDATE_DOWNLOADED") metric(event.to_release_id, {
68
68
  downloads: 1,
69
- launches: 0,
69
+ applies: 0,
70
70
  failedLaunches: 0
71
71
  });
72
- else if (event.type !== "UPDATE_FAILED") {
73
- metric(event.to_release_id, {
74
- downloads: 0,
75
- launches: 1,
76
- failedLaunches: 0
77
- });
78
- if (event.type === "RECOVERED") metric(event.from_release_id, {
79
- downloads: 0,
80
- launches: 0,
81
- failedLaunches: 1
82
- });
83
- }
72
+ else if (event.type === "UPDATE_APPLIED") metric(event.to_release_id, {
73
+ downloads: 0,
74
+ applies: 1,
75
+ failedLaunches: 0
76
+ });
77
+ else if (event.type === "RECOVERED") metric(event.from_release_id, {
78
+ downloads: 0,
79
+ applies: 0,
80
+ failedLaunches: 1
81
+ });
84
82
  for (const platform of [event.platform, "all"]) for (const appVersionKind of ["specific", "all"]) append({
85
83
  scopeKind: "usage",
86
84
  releaseKind: "all",
@@ -93,7 +91,7 @@ const insightsOverviewDeltas = (event) => {
93
91
  bucketStartMs
94
92
  }, {
95
93
  downloads: 0,
96
- launches: 0,
94
+ applies: 0,
97
95
  failedLaunches: 0,
98
96
  activityIdentity: event.install_id
99
97
  });
@@ -31,7 +31,7 @@ const insightsOverviewDeltas = (event) => {
31
31
  counters.set(key, {
32
32
  identity,
33
33
  downloads: (previous?.downloads ?? 0) + values.downloads,
34
- launches: (previous?.launches ?? 0) + values.launches,
34
+ applies: (previous?.applies ?? 0) + values.applies,
35
35
  failedLaunches: (previous?.failedLaunches ?? 0) + values.failedLaunches,
36
36
  activityIdentity: previous?.activityIdentity ?? values.activityIdentity
37
37
  });
@@ -39,7 +39,7 @@ const insightsOverviewDeltas = (event) => {
39
39
  const metric = (releaseId, values) => {
40
40
  const windowed = {
41
41
  ...values,
42
- launches: 0
42
+ applies: 0
43
43
  };
44
44
  if (releaseId !== null) for (const [periodKind, start] of [["lifetime", 0], ["hour", bucketStartMs]]) append({
45
45
  scopeKind: "release",
@@ -66,21 +66,19 @@ const insightsOverviewDeltas = (event) => {
66
66
  };
67
67
  if (event.type === "UPDATE_DOWNLOADED") metric(event.to_release_id, {
68
68
  downloads: 1,
69
- launches: 0,
69
+ applies: 0,
70
70
  failedLaunches: 0
71
71
  });
72
- else if (event.type !== "UPDATE_FAILED") {
73
- metric(event.to_release_id, {
74
- downloads: 0,
75
- launches: 1,
76
- failedLaunches: 0
77
- });
78
- if (event.type === "RECOVERED") metric(event.from_release_id, {
79
- downloads: 0,
80
- launches: 0,
81
- failedLaunches: 1
82
- });
83
- }
72
+ else if (event.type === "UPDATE_APPLIED") metric(event.to_release_id, {
73
+ downloads: 0,
74
+ applies: 1,
75
+ failedLaunches: 0
76
+ });
77
+ else if (event.type === "RECOVERED") metric(event.from_release_id, {
78
+ downloads: 0,
79
+ applies: 0,
80
+ failedLaunches: 1
81
+ });
84
82
  for (const platform of [event.platform, "all"]) for (const appVersionKind of ["specific", "all"]) append({
85
83
  scopeKind: "usage",
86
84
  releaseKind: "all",
@@ -93,7 +91,7 @@ const insightsOverviewDeltas = (event) => {
93
91
  bucketStartMs
94
92
  }, {
95
93
  downloads: 0,
96
- launches: 0,
94
+ applies: 0,
97
95
  failedLaunches: 0,
98
96
  activityIdentity: event.install_id
99
97
  });
@@ -365,7 +365,7 @@ const lifetimeMetrics = async (db, release) => {
365
365
  })).rows;
366
366
  return {
367
367
  downloads: lifetime?.downloads ?? 0,
368
- launches: lifetime?.launches ?? 0,
368
+ applies: lifetime?.applies ?? 0,
369
369
  failedLaunches: lifetime?.failed_launches ?? 0
370
370
  };
371
371
  };
@@ -365,7 +365,7 @@ const lifetimeMetrics = async (db, release) => {
365
365
  })).rows;
366
366
  return {
367
367
  downloads: lifetime?.downloads ?? 0,
368
- launches: lifetime?.launches ?? 0,
368
+ applies: lifetime?.applies ?? 0,
369
369
  failedLaunches: lifetime?.failed_launches ?? 0
370
370
  };
371
371
  };
@@ -112,7 +112,7 @@ const countEvent = (tx, event) => {
112
112
  const counters = {
113
113
  ...Object.fromEntries([
114
114
  ["downloads", delta.downloads],
115
- ["launches", delta.launches],
115
+ ["applies", delta.applies],
116
116
  ["failed_launches", delta.failedLaunches]
117
117
  ].filter(([, value]) => value !== 0)),
118
118
  ...delta.downloads === 0 ? {} : patchCounters(event)
@@ -112,7 +112,7 @@ const countEvent = (tx, event) => {
112
112
  const counters = {
113
113
  ...Object.fromEntries([
114
114
  ["downloads", delta.downloads],
115
- ["launches", delta.launches],
115
+ ["applies", delta.applies],
116
116
  ["failed_launches", delta.failedLaunches]
117
117
  ].filter(([, value]) => value !== 0)),
118
118
  ...delta.downloads === 0 ? {} : patchCounters(event)
@@ -206,29 +206,42 @@ const window = {
206
206
  eq: ["identity"],
207
207
  sort: ["bucket_start_ms"]
208
208
  };
209
+ /** What a release or channel row counts in every period kind. */
210
+ const WINDOW_COUNTERS = [
211
+ "downloads",
212
+ "failed_launches",
213
+ "patch_downloads",
214
+ "patch_fallbacks"
215
+ ];
209
216
  /**
210
- * Counters of one period kind: hourly, daily, or lifetime at bucket 0. A
211
- * release or channel row counts downloads and failed launches, and downloads
212
- * a patch delivered and those that fell back from one. Only a release's
213
- * lifetime row counts launches, which the bundle list reads and no windowed
214
- * read sums. It also counts the release's failed updates (`failed_updates`),
215
- * since no breakdown row outlives the raw period; windowed reads sum
216
- * `insights_failures` instead.
217
+ * Hourly or daily counters: a release or channel row counts downloads,
218
+ * launches that crashed and recovered (`failed_launches`), and downloads a
219
+ * patch delivered and those that fell back from one.
217
220
  */
218
221
  const counters = (retention) => (0, _hot_updater_plugin_core.defineAggregate)(identityFields, {
222
+ key: ["identity", "bucket_start_ms"],
223
+ counters: WINDOW_COUNTERS,
224
+ shards: COUNTER_SHARDS,
225
+ batched: true,
226
+ indexes: { window },
227
+ retention
228
+ });
229
+ /**
230
+ * A release's counters since its first report, at bucket 0: the windowed
231
+ * counters, its applies, which the bundle list reads, and its failed updates
232
+ * (`failed_updates`), since no breakdown row outlives the raw period; windowed
233
+ * reads sum `insights_failures` instead.
234
+ */
235
+ const lifetimeCounters = () => (0, _hot_updater_plugin_core.defineAggregate)(identityFields, {
219
236
  key: ["identity", "bucket_start_ms"],
220
237
  counters: [
221
- "downloads",
222
- "launches",
223
- "failed_launches",
224
- "failed_updates",
225
- "patch_downloads",
226
- "patch_fallbacks"
238
+ ...WINDOW_COUNTERS,
239
+ "applies",
240
+ "failed_updates"
227
241
  ],
228
242
  shards: COUNTER_SHARDS,
229
243
  batched: true,
230
- indexes: { window },
231
- ...retention === void 0 ? {} : { retention }
244
+ indexes: { window }
232
245
  });
233
246
  /**
234
247
  * Unique-installation sketches of one period kind: a usage row's active
@@ -459,7 +472,7 @@ const createInsightsSchema = ({ rawDays, dailyDays } = DEFAULT_INSIGHTS_RETENTIO
459
472
  insights_overview_daily: counters(daily),
460
473
  insights_sketches_daily: sketches(daily),
461
474
  /** A release's counters since its first event, and its failed installations: kept. */
462
- insights_overview_lifetime: counters(),
475
+ insights_overview_lifetime: lifetimeCounters(),
463
476
  insights_sketches_lifetime: lifetimeSketches(),
464
477
  insights_distribution: insightsDistribution(daily),
465
478
  insights_latest_by_bundle: insightsLatestByBundle(daily),
@@ -153,7 +153,7 @@ declare const createInsightsSchema: ({ rawDays, dailyDays }?: InsightsRetention)
153
153
  readonly bucket_start_ms: {
154
154
  readonly type: "integer";
155
155
  };
156
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
156
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks", {
157
157
  readonly window: {
158
158
  readonly eq: readonly ["identity"];
159
159
  readonly sort: readonly ["bucket_start_ms"];
@@ -182,7 +182,7 @@ declare const createInsightsSchema: ({ rawDays, dailyDays }?: InsightsRetention)
182
182
  readonly bucket_start_ms: {
183
183
  readonly type: "integer";
184
184
  };
185
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
185
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks", {
186
186
  readonly window: {
187
187
  readonly eq: readonly ["identity"];
188
188
  readonly sort: readonly ["bucket_start_ms"];
@@ -211,7 +211,7 @@ declare const createInsightsSchema: ({ rawDays, dailyDays }?: InsightsRetention)
211
211
  readonly bucket_start_ms: {
212
212
  readonly type: "integer";
213
213
  };
214
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
214
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks" | "applies" | "failed_updates", {
215
215
  readonly window: {
216
216
  readonly eq: readonly ["identity"];
217
217
  readonly sort: readonly ["bucket_start_ms"];
@@ -488,7 +488,7 @@ declare const insightsSchema: {
488
488
  readonly bucket_start_ms: {
489
489
  readonly type: "integer";
490
490
  };
491
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
491
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks", {
492
492
  readonly window: {
493
493
  readonly eq: readonly ["identity"];
494
494
  readonly sort: readonly ["bucket_start_ms"];
@@ -517,7 +517,7 @@ declare const insightsSchema: {
517
517
  readonly bucket_start_ms: {
518
518
  readonly type: "integer";
519
519
  };
520
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
520
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks", {
521
521
  readonly window: {
522
522
  readonly eq: readonly ["identity"];
523
523
  readonly sort: readonly ["bucket_start_ms"];
@@ -546,7 +546,7 @@ declare const insightsSchema: {
546
546
  readonly bucket_start_ms: {
547
547
  readonly type: "integer";
548
548
  };
549
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
549
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks" | "applies" | "failed_updates", {
550
550
  readonly window: {
551
551
  readonly eq: readonly ["identity"];
552
552
  readonly sort: readonly ["bucket_start_ms"];
@@ -153,7 +153,7 @@ declare const createInsightsSchema: ({ rawDays, dailyDays }?: InsightsRetention)
153
153
  readonly bucket_start_ms: {
154
154
  readonly type: "integer";
155
155
  };
156
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
156
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks", {
157
157
  readonly window: {
158
158
  readonly eq: readonly ["identity"];
159
159
  readonly sort: readonly ["bucket_start_ms"];
@@ -182,7 +182,7 @@ declare const createInsightsSchema: ({ rawDays, dailyDays }?: InsightsRetention)
182
182
  readonly bucket_start_ms: {
183
183
  readonly type: "integer";
184
184
  };
185
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
185
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks", {
186
186
  readonly window: {
187
187
  readonly eq: readonly ["identity"];
188
188
  readonly sort: readonly ["bucket_start_ms"];
@@ -211,7 +211,7 @@ declare const createInsightsSchema: ({ rawDays, dailyDays }?: InsightsRetention)
211
211
  readonly bucket_start_ms: {
212
212
  readonly type: "integer";
213
213
  };
214
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
214
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks" | "applies" | "failed_updates", {
215
215
  readonly window: {
216
216
  readonly eq: readonly ["identity"];
217
217
  readonly sort: readonly ["bucket_start_ms"];
@@ -488,7 +488,7 @@ declare const insightsSchema: {
488
488
  readonly bucket_start_ms: {
489
489
  readonly type: "integer";
490
490
  };
491
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
491
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks", {
492
492
  readonly window: {
493
493
  readonly eq: readonly ["identity"];
494
494
  readonly sort: readonly ["bucket_start_ms"];
@@ -517,7 +517,7 @@ declare const insightsSchema: {
517
517
  readonly bucket_start_ms: {
518
518
  readonly type: "integer";
519
519
  };
520
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
520
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks", {
521
521
  readonly window: {
522
522
  readonly eq: readonly ["identity"];
523
523
  readonly sort: readonly ["bucket_start_ms"];
@@ -546,7 +546,7 @@ declare const insightsSchema: {
546
546
  readonly bucket_start_ms: {
547
547
  readonly type: "integer";
548
548
  };
549
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
549
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks" | "applies" | "failed_updates", {
550
550
  readonly window: {
551
551
  readonly eq: readonly ["identity"];
552
552
  readonly sort: readonly ["bucket_start_ms"];
@@ -206,29 +206,42 @@ const window = {
206
206
  eq: ["identity"],
207
207
  sort: ["bucket_start_ms"]
208
208
  };
209
+ /** What a release or channel row counts in every period kind. */
210
+ const WINDOW_COUNTERS = [
211
+ "downloads",
212
+ "failed_launches",
213
+ "patch_downloads",
214
+ "patch_fallbacks"
215
+ ];
209
216
  /**
210
- * Counters of one period kind: hourly, daily, or lifetime at bucket 0. A
211
- * release or channel row counts downloads and failed launches, and downloads
212
- * a patch delivered and those that fell back from one. Only a release's
213
- * lifetime row counts launches, which the bundle list reads and no windowed
214
- * read sums. It also counts the release's failed updates (`failed_updates`),
215
- * since no breakdown row outlives the raw period; windowed reads sum
216
- * `insights_failures` instead.
217
+ * Hourly or daily counters: a release or channel row counts downloads,
218
+ * launches that crashed and recovered (`failed_launches`), and downloads a
219
+ * patch delivered and those that fell back from one.
217
220
  */
218
221
  const counters = (retention) => defineAggregate(identityFields, {
222
+ key: ["identity", "bucket_start_ms"],
223
+ counters: WINDOW_COUNTERS,
224
+ shards: COUNTER_SHARDS,
225
+ batched: true,
226
+ indexes: { window },
227
+ retention
228
+ });
229
+ /**
230
+ * A release's counters since its first report, at bucket 0: the windowed
231
+ * counters, its applies, which the bundle list reads, and its failed updates
232
+ * (`failed_updates`), since no breakdown row outlives the raw period; windowed
233
+ * reads sum `insights_failures` instead.
234
+ */
235
+ const lifetimeCounters = () => defineAggregate(identityFields, {
219
236
  key: ["identity", "bucket_start_ms"],
220
237
  counters: [
221
- "downloads",
222
- "launches",
223
- "failed_launches",
224
- "failed_updates",
225
- "patch_downloads",
226
- "patch_fallbacks"
238
+ ...WINDOW_COUNTERS,
239
+ "applies",
240
+ "failed_updates"
227
241
  ],
228
242
  shards: COUNTER_SHARDS,
229
243
  batched: true,
230
- indexes: { window },
231
- ...retention === void 0 ? {} : { retention }
244
+ indexes: { window }
232
245
  });
233
246
  /**
234
247
  * Unique-installation sketches of one period kind: a usage row's active
@@ -459,7 +472,7 @@ const createInsightsSchema = ({ rawDays, dailyDays } = DEFAULT_INSIGHTS_RETENTIO
459
472
  insights_overview_daily: counters(daily),
460
473
  insights_sketches_daily: sketches(daily),
461
474
  /** A release's counters since its first event, and its failed installations: kept. */
462
- insights_overview_lifetime: counters(),
475
+ insights_overview_lifetime: lifetimeCounters(),
463
476
  insights_sketches_lifetime: lifetimeSketches(),
464
477
  insights_distribution: insightsDistribution(daily),
465
478
  insights_latest_by_bundle: insightsLatestByBundle(daily),
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.31",
4
+ "version": "1.0.0-rc.33",
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.28"
36
+ "@hot-updater/protocol": "1.0.0-rc.29"
37
37
  },
38
38
  "peerDependencies": {
39
- "@hot-updater/plugin-core": "^1.0.0-rc.28"
39
+ "@hot-updater/plugin-core": "^1.0.0-rc.29"
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.0-rc.28",
50
- "@hot-updater/test-utils": "1.0.0-rc.30"
49
+ "@hot-updater/plugin-core": "1.0.0-rc.29",
50
+ "@hot-updater/test-utils": "1.0.0-rc.31"
51
51
  },
52
52
  "nx": {
53
53
  "implicitDependencies": [