@hot-updater/plugin-insights 1.0.0-rc.34 → 1.0.0-rc.36

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.
@@ -60,7 +60,8 @@ const insights = (options = {}) => {
60
60
  appVersion,
61
61
  cohort: context.getCohort(),
62
62
  fingerprintHash: context.getFingerprintHash(),
63
- sdkVersion: context.sdkVersion
63
+ sdkVersion: context.sdkVersion,
64
+ minBundleId: context.minBundleId
64
65
  };
65
66
  const day = require_state.utcDay(now);
66
67
  return {
@@ -59,7 +59,8 @@ const insights = (options = {}) => {
59
59
  appVersion,
60
60
  cohort: context.getCohort(),
61
61
  fingerprintHash: context.getFingerprintHash(),
62
- sdkVersion: context.sdkVersion
62
+ sdkVersion: context.sdkVersion,
63
+ minBundleId: context.minBundleId
63
64
  };
64
65
  const day = utcDay(now);
65
66
  return {
@@ -44,6 +44,12 @@ type CreateBundleEventRequestBase = {
44
44
  readonly cohort: string;
45
45
  readonly fingerprintHash: string | null;
46
46
  readonly sdkVersion?: string | null;
47
+ /**
48
+ * The bundle ID the native build ships as its built-in bundle. A report
49
+ * whose running bundle is this one, with no Release, runs the built-in
50
+ * bundle. Older SDKs leave it out.
51
+ */
52
+ readonly minBundleId?: string | null;
47
53
  readonly fromReleaseId: string | null;
48
54
  readonly toReleaseId: string | null;
49
55
  };
@@ -79,6 +85,8 @@ type ActiveInstallationWindow = "24h" | "7d" | "30d";
79
85
  type EventHistoryRow = {
80
86
  readonly toReleaseId?: string;
81
87
  readonly sdkVersion?: string;
88
+ /** The native build's built-in bundle ID, when the SDK reported it. */
89
+ readonly minBundleId?: string;
82
90
  readonly id: string;
83
91
  readonly installId: string;
84
92
  readonly type: "UPDATE_DOWNLOADED" | "UPDATE_APPLIED" | "RECOVERED" | "UPDATE_FAILED" | "UNCHANGED";
@@ -107,6 +115,8 @@ type InsightsHttpResponse = UpdateHttpResponse & {
107
115
  };
108
116
  type InstallationRow = {
109
117
  readonly httpResponse?: InsightsHttpResponse;
118
+ /** The native build's built-in bundle ID, when the SDK reported it. */
119
+ readonly minBundleId?: string;
110
120
  readonly installId: string;
111
121
  readonly userId: string | null;
112
122
  readonly lastKnownBundleId: string;
@@ -44,6 +44,12 @@ type CreateBundleEventRequestBase = {
44
44
  readonly cohort: string;
45
45
  readonly fingerprintHash: string | null;
46
46
  readonly sdkVersion?: string | null;
47
+ /**
48
+ * The bundle ID the native build ships as its built-in bundle. A report
49
+ * whose running bundle is this one, with no Release, runs the built-in
50
+ * bundle. Older SDKs leave it out.
51
+ */
52
+ readonly minBundleId?: string | null;
47
53
  readonly fromReleaseId: string | null;
48
54
  readonly toReleaseId: string | null;
49
55
  };
@@ -79,6 +85,8 @@ type ActiveInstallationWindow = "24h" | "7d" | "30d";
79
85
  type EventHistoryRow = {
80
86
  readonly toReleaseId?: string;
81
87
  readonly sdkVersion?: string;
88
+ /** The native build's built-in bundle ID, when the SDK reported it. */
89
+ readonly minBundleId?: string;
82
90
  readonly id: string;
83
91
  readonly installId: string;
84
92
  readonly type: "UPDATE_DOWNLOADED" | "UPDATE_APPLIED" | "RECOVERED" | "UPDATE_FAILED" | "UNCHANGED";
@@ -107,6 +115,8 @@ type InsightsHttpResponse = UpdateHttpResponse & {
107
115
  };
108
116
  type InstallationRow = {
109
117
  readonly httpResponse?: InsightsHttpResponse;
118
+ /** The native build's built-in bundle ID, when the SDK reported it. */
119
+ readonly minBundleId?: string;
110
120
  readonly installId: string;
111
121
  readonly userId: string | null;
112
122
  readonly lastKnownBundleId: string;
@@ -31,6 +31,11 @@ function readEventId(payload) {
31
31
  if (!(0, _hot_updater_plugin_core.isUUIDv7)(value)) throw new require_errors.InsightsBadRequestError("Invalid event field: eventId");
32
32
  return value;
33
33
  }
34
+ /**
35
+ * The native build's built-in bundle ID: a bundle ID's length at most, and
36
+ * left out when it is missing or malformed, so the report still records.
37
+ */
38
+ const readMinBundleId = (value) => typeof value === "string" && value.length > 0 && value.length <= 36 ? value : null;
34
39
  /** One value of an open set: any other value reads as `unknown`. */
35
40
  const oneOf = (values, value) => values.includes(value) ? value : "unknown";
36
41
  const STAGES = [
@@ -156,6 +161,7 @@ function requireEvent(payload) {
156
161
  cohort: requireStringField(payload, "cohort"),
157
162
  fingerprintHash: requireNullableStringField(payload, "fingerprintHash"),
158
163
  sdkVersion: payload.sdkVersion === void 0 ? null : requireNullableStringField(payload, "sdkVersion"),
164
+ minBundleId: readMinBundleId(payload.minBundleId),
159
165
  fromReleaseId: requireNullableStringField(payload, "fromReleaseId"),
160
166
  toReleaseId: requireNullableStringField(payload, "toReleaseId")
161
167
  };
@@ -237,6 +243,7 @@ function createBundleEventRow(input) {
237
243
  cohort: input.cohort,
238
244
  fingerprint_hash: input.fingerprintHash,
239
245
  sdk_version: input.sdkVersion ?? null,
246
+ ...input.minBundleId ? { min_bundle_id: input.minBundleId } : {},
240
247
  update_strategy: input.updateStrategy
241
248
  };
242
249
  switch (input.type) {
@@ -31,6 +31,11 @@ function readEventId(payload) {
31
31
  if (!isUUIDv7(value)) throw new InsightsBadRequestError("Invalid event field: eventId");
32
32
  return value;
33
33
  }
34
+ /**
35
+ * The native build's built-in bundle ID: a bundle ID's length at most, and
36
+ * left out when it is missing or malformed, so the report still records.
37
+ */
38
+ const readMinBundleId = (value) => typeof value === "string" && value.length > 0 && value.length <= 36 ? value : null;
34
39
  /** One value of an open set: any other value reads as `unknown`. */
35
40
  const oneOf = (values, value) => values.includes(value) ? value : "unknown";
36
41
  const STAGES = [
@@ -156,6 +161,7 @@ function requireEvent(payload) {
156
161
  cohort: requireStringField(payload, "cohort"),
157
162
  fingerprintHash: requireNullableStringField(payload, "fingerprintHash"),
158
163
  sdkVersion: payload.sdkVersion === void 0 ? null : requireNullableStringField(payload, "sdkVersion"),
164
+ minBundleId: readMinBundleId(payload.minBundleId),
159
165
  fromReleaseId: requireNullableStringField(payload, "fromReleaseId"),
160
166
  toReleaseId: requireNullableStringField(payload, "toReleaseId")
161
167
  };
@@ -237,6 +243,7 @@ function createBundleEventRow(input) {
237
243
  cohort: input.cohort,
238
244
  fingerprint_hash: input.fingerprintHash,
239
245
  sdk_version: input.sdkVersion ?? null,
246
+ ...input.minBundleId ? { min_bundle_id: input.minBundleId } : {},
240
247
  update_strategy: input.updateStrategy
241
248
  };
242
249
  switch (input.type) {
@@ -5,8 +5,19 @@ const isString = (value) => typeof value === "string";
5
5
  const isDatabaseHttpResponse = (value) => (0, _hot_updater_plugin_core.isDatabaseJsonObject)(value) && (value.resource === "catalog" || value.resource === "artifact") && isString(value.path) && Number.isSafeInteger(value.status) && (value.body === null || isString(value.body)) && typeof value.body_truncated === "boolean" && Number.isSafeInteger(value.received_at_ms);
6
6
  /** An `UPDATE_FAILED` report's stored failure: a stage and a reason, and what else the client knew. */
7
7
  const isDatabaseBundleEventFailure = (value) => (0, _hot_updater_plugin_core.isDatabaseJsonObject)(value) && isString(value.stage) && isString(value.reason) && isOptional(value, "resource", isString) && isOptional(value, "http_status", (status) => Number.isSafeInteger(status)) && isOptional(value, "transport", isString) && isOptional(value, "origin_code", isString) && isOptional(value, "error_message", isString) && isOptional(value, "error_stack", isString);
8
- const isDatabaseBundleEventMetadata = (value) => (0, _hot_updater_plugin_core.isDatabaseJsonObject)(value) && typeof value.cohort === "string" && (value.update_strategy === null || value.update_strategy === "fingerprint" || value.update_strategy === "appVersion") && (value.fingerprint_hash === null || typeof value.fingerprint_hash === "string") && (value.sdk_version === null || typeof value.sdk_version === "string") && isOptional(value, "failure", isDatabaseBundleEventFailure) && isOptional(value, "http_response", isDatabaseHttpResponse) && isOptional(value, "delivery", isString) && isOptional(value, "patch_fallback", (flag) => typeof flag === "boolean");
8
+ const isDatabaseBundleEventMetadata = (value) => (0, _hot_updater_plugin_core.isDatabaseJsonObject)(value) && typeof value.cohort === "string" && (value.update_strategy === null || value.update_strategy === "fingerprint" || value.update_strategy === "appVersion") && (value.fingerprint_hash === null || typeof value.fingerprint_hash === "string") && (value.sdk_version === null || typeof value.sdk_version === "string") && isOptional(value, "min_bundle_id", isString) && isOptional(value, "failure", isDatabaseBundleEventFailure) && isOptional(value, "http_response", isDatabaseHttpResponse) && isOptional(value, "delivery", isString) && isOptional(value, "patch_fallback", (flag) => typeof flag === "boolean");
9
9
  const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
10
+ /**
11
+ * The built-in bundle a report says its installation runs: the running
12
+ * bundle, which a download leaves at its source, when it is the native
13
+ * build's built-in bundle, whatever Release selected it. Null for any other
14
+ * bundle, and for a report without the build's built-in bundle ID.
15
+ */
16
+ const runningBuiltinBundleId = (row) => {
17
+ const running = row.type === "UPDATE_DOWNLOADED" ? row.from_bundle_id : row.to_bundle_id;
18
+ const builtin = isRecord(row.metadata) ? row.metadata.min_bundle_id : null;
19
+ return typeof builtin === "string" && running === builtin ? builtin : null;
20
+ };
10
21
  const isIdentity = (value) => typeof value === "string" && value.length > 0 && value.length <= 255;
11
22
  const isTextOrNull = (value) => value === null || typeof value === "string";
12
23
  /** Each bundle event field and what it holds. */
@@ -46,4 +57,5 @@ const validateBundleEventFields = (row) => {
46
57
  //#endregion
47
58
  exports.isDatabaseBundleEventMetadata = isDatabaseBundleEventMetadata;
48
59
  exports.isRecord = isRecord;
60
+ exports.runningBuiltinBundleId = runningBuiltinBundleId;
49
61
  exports.validateBundleEventFields = validateBundleEventFields;
@@ -31,6 +31,8 @@ type DatabaseBundleEventMetadata = DatabaseJsonObject & {
31
31
  readonly update_strategy: "fingerprint" | "appVersion" | null;
32
32
  readonly fingerprint_hash: string | null;
33
33
  readonly sdk_version: string | null;
34
+ /** The native build's built-in bundle ID, when the SDK reports it. */
35
+ readonly min_bundle_id?: string;
34
36
  /** `UPDATE_FAILED`: what failed. */
35
37
  readonly failure?: BundleEventFailure;
36
38
  readonly http_response?: DatabaseHttpResponse;
@@ -31,6 +31,8 @@ type DatabaseBundleEventMetadata = DatabaseJsonObject & {
31
31
  readonly update_strategy: "fingerprint" | "appVersion" | null;
32
32
  readonly fingerprint_hash: string | null;
33
33
  readonly sdk_version: string | null;
34
+ /** The native build's built-in bundle ID, when the SDK reports it. */
35
+ readonly min_bundle_id?: string;
34
36
  /** `UPDATE_FAILED`: what failed. */
35
37
  readonly failure?: BundleEventFailure;
36
38
  readonly http_response?: DatabaseHttpResponse;
@@ -5,8 +5,19 @@ const isString = (value) => typeof value === "string";
5
5
  const isDatabaseHttpResponse = (value) => isDatabaseJsonObject(value) && (value.resource === "catalog" || value.resource === "artifact") && isString(value.path) && Number.isSafeInteger(value.status) && (value.body === null || isString(value.body)) && typeof value.body_truncated === "boolean" && Number.isSafeInteger(value.received_at_ms);
6
6
  /** An `UPDATE_FAILED` report's stored failure: a stage and a reason, and what else the client knew. */
7
7
  const isDatabaseBundleEventFailure = (value) => isDatabaseJsonObject(value) && isString(value.stage) && isString(value.reason) && isOptional(value, "resource", isString) && isOptional(value, "http_status", (status) => Number.isSafeInteger(status)) && isOptional(value, "transport", isString) && isOptional(value, "origin_code", isString) && isOptional(value, "error_message", isString) && isOptional(value, "error_stack", isString);
8
- const isDatabaseBundleEventMetadata = (value) => isDatabaseJsonObject(value) && typeof value.cohort === "string" && (value.update_strategy === null || value.update_strategy === "fingerprint" || value.update_strategy === "appVersion") && (value.fingerprint_hash === null || typeof value.fingerprint_hash === "string") && (value.sdk_version === null || typeof value.sdk_version === "string") && isOptional(value, "failure", isDatabaseBundleEventFailure) && isOptional(value, "http_response", isDatabaseHttpResponse) && isOptional(value, "delivery", isString) && isOptional(value, "patch_fallback", (flag) => typeof flag === "boolean");
8
+ const isDatabaseBundleEventMetadata = (value) => isDatabaseJsonObject(value) && typeof value.cohort === "string" && (value.update_strategy === null || value.update_strategy === "fingerprint" || value.update_strategy === "appVersion") && (value.fingerprint_hash === null || typeof value.fingerprint_hash === "string") && (value.sdk_version === null || typeof value.sdk_version === "string") && isOptional(value, "min_bundle_id", isString) && isOptional(value, "failure", isDatabaseBundleEventFailure) && isOptional(value, "http_response", isDatabaseHttpResponse) && isOptional(value, "delivery", isString) && isOptional(value, "patch_fallback", (flag) => typeof flag === "boolean");
9
9
  const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
10
+ /**
11
+ * The built-in bundle a report says its installation runs: the running
12
+ * bundle, which a download leaves at its source, when it is the native
13
+ * build's built-in bundle, whatever Release selected it. Null for any other
14
+ * bundle, and for a report without the build's built-in bundle ID.
15
+ */
16
+ const runningBuiltinBundleId = (row) => {
17
+ const running = row.type === "UPDATE_DOWNLOADED" ? row.from_bundle_id : row.to_bundle_id;
18
+ const builtin = isRecord(row.metadata) ? row.metadata.min_bundle_id : null;
19
+ return typeof builtin === "string" && running === builtin ? builtin : null;
20
+ };
10
21
  const isIdentity = (value) => typeof value === "string" && value.length > 0 && value.length <= 255;
11
22
  const isTextOrNull = (value) => value === null || typeof value === "string";
12
23
  /** Each bundle event field and what it holds. */
@@ -44,4 +55,4 @@ const validateBundleEventFields = (row) => {
44
55
  if (!hasEventInvariants(row)) throw new DatabaseAdapterInputError("invalid-data");
45
56
  };
46
57
  //#endregion
47
- export { isDatabaseBundleEventMetadata, isRecord, validateBundleEventFields };
58
+ export { isDatabaseBundleEventMetadata, isRecord, runningBuiltinBundleId, validateBundleEventFields };
@@ -284,6 +284,38 @@ declare const insights: (options?: InsightsOptions) => import("@hot-updater/plug
284
284
  readonly sort: readonly ["bucket_start_ms"];
285
285
  };
286
286
  }, never, readonly ["channel", "platform", "app_version", "release_id", "bucket_start_ms"]>;
287
+ readonly insights_builtin_distribution: import("@hot-updater/plugin-core").AggregateDefinition<{
288
+ readonly channel: {
289
+ readonly type: "string";
290
+ };
291
+ readonly platform: {
292
+ readonly type: "string";
293
+ readonly maxLength: 16;
294
+ };
295
+ readonly app_version: {
296
+ readonly type: "string";
297
+ };
298
+ readonly release_id: {
299
+ readonly type: "string";
300
+ readonly maxLength: 36;
301
+ };
302
+ readonly builtin_bundle_id: {
303
+ readonly type: "string";
304
+ readonly maxLength: 36;
305
+ };
306
+ readonly bucket_start_ms: {
307
+ readonly type: "integer";
308
+ };
309
+ }, "latest_installations", {
310
+ readonly byScope: {
311
+ readonly eq: readonly ["channel", "platform"];
312
+ readonly sort: readonly ["bucket_start_ms"];
313
+ };
314
+ readonly byVersion: {
315
+ readonly eq: readonly ["channel", "platform", "app_version"];
316
+ readonly sort: readonly ["bucket_start_ms"];
317
+ };
318
+ }, never, readonly ["channel", "platform", "app_version", "release_id", "builtin_bundle_id", "bucket_start_ms"]>;
287
319
  readonly insights_latest_by_bundle: import("@hot-updater/plugin-core").AggregateDefinition<{
288
320
  readonly platform: {
289
321
  readonly type: "string";
@@ -284,6 +284,38 @@ declare const insights: (options?: InsightsOptions) => import("@hot-updater/plug
284
284
  readonly sort: readonly ["bucket_start_ms"];
285
285
  };
286
286
  }, never, readonly ["channel", "platform", "app_version", "release_id", "bucket_start_ms"]>;
287
+ readonly insights_builtin_distribution: import("@hot-updater/plugin-core").AggregateDefinition<{
288
+ readonly channel: {
289
+ readonly type: "string";
290
+ };
291
+ readonly platform: {
292
+ readonly type: "string";
293
+ readonly maxLength: 16;
294
+ };
295
+ readonly app_version: {
296
+ readonly type: "string";
297
+ };
298
+ readonly release_id: {
299
+ readonly type: "string";
300
+ readonly maxLength: 36;
301
+ };
302
+ readonly builtin_bundle_id: {
303
+ readonly type: "string";
304
+ readonly maxLength: 36;
305
+ };
306
+ readonly bucket_start_ms: {
307
+ readonly type: "integer";
308
+ };
309
+ }, "latest_installations", {
310
+ readonly byScope: {
311
+ readonly eq: readonly ["channel", "platform"];
312
+ readonly sort: readonly ["bucket_start_ms"];
313
+ };
314
+ readonly byVersion: {
315
+ readonly eq: readonly ["channel", "platform", "app_version"];
316
+ readonly sort: readonly ["bucket_start_ms"];
317
+ };
318
+ }, never, readonly ["channel", "platform", "app_version", "release_id", "builtin_bundle_id", "bucket_start_ms"]>;
287
319
  readonly insights_latest_by_bundle: import("@hot-updater/plugin-core").AggregateDefinition<{
288
320
  readonly platform: {
289
321
  readonly type: "string";
@@ -139,7 +139,10 @@ interface InsightsGetAppUsageResult {
139
139
  readonly bundleDistribution: readonly {
140
140
  readonly appVersion: string;
141
141
  readonly platform: "ios" | "android";
142
+ /** The release the installations run; null for none. */
142
143
  readonly releaseId: string | null;
144
+ /** The native build's built-in bundle, when they run it. */
145
+ readonly builtinBundleId: string | null;
143
146
  readonly installations: number;
144
147
  }[];
145
148
  readonly measuredAtMs: number;
@@ -139,7 +139,10 @@ interface InsightsGetAppUsageResult {
139
139
  readonly bundleDistribution: readonly {
140
140
  readonly appVersion: string;
141
141
  readonly platform: "ios" | "android";
142
+ /** The release the installations run; null for none. */
142
143
  readonly releaseId: string | null;
144
+ /** The native build's built-in bundle, when they run it. */
145
+ readonly builtinBundleId: string | null;
143
146
  readonly installations: number;
144
147
  }[];
145
148
  readonly measuredAtMs: number;
@@ -1,14 +1,14 @@
1
1
  import "./eventRow.cjs";
2
2
  //#region src/server/overview.d.ts
3
3
  type InsightsOverviewIdentity = {
4
- readonly scopeKind: "release" | "channel" | "usage" | "distribution";
5
- readonly releaseKind: "all" | "specific" | "embedded";
4
+ readonly scopeKind: "release" | "channel" | "usage";
5
+ readonly releaseKind: "all" | "specific";
6
6
  readonly releaseId: string;
7
7
  readonly channel: string;
8
8
  readonly platform: "all" | "ios" | "android";
9
9
  readonly appVersionKind: "all" | "specific";
10
10
  readonly appVersion: string;
11
- readonly periodKind: "lifetime" | "hour" | "latest";
11
+ readonly periodKind: "lifetime" | "hour";
12
12
  readonly bucketStartMs: number;
13
13
  };
14
14
  //#endregion
@@ -1,14 +1,14 @@
1
1
  import "./eventRow.mjs";
2
2
  //#region src/server/overview.d.ts
3
3
  type InsightsOverviewIdentity = {
4
- readonly scopeKind: "release" | "channel" | "usage" | "distribution";
5
- readonly releaseKind: "all" | "specific" | "embedded";
4
+ readonly scopeKind: "release" | "channel" | "usage";
5
+ readonly releaseKind: "all" | "specific";
6
6
  readonly releaseId: string;
7
7
  readonly channel: string;
8
8
  readonly platform: "all" | "ios" | "android";
9
9
  readonly appVersionKind: "all" | "specific";
10
10
  readonly appVersion: string;
11
- readonly periodKind: "lifetime" | "hour" | "latest";
11
+ readonly periodKind: "lifetime" | "hour";
12
12
  readonly bucketStartMs: number;
13
13
  };
14
14
  //#endregion
@@ -171,6 +171,7 @@ const eventDetails = ({ type, metadata }) => ({
171
171
  const toEventHistoryRow = (row) => ({
172
172
  ...row.to_release_id == null ? {} : { toReleaseId: row.to_release_id },
173
173
  ...row.metadata.sdk_version == null ? {} : { sdkVersion: row.metadata.sdk_version },
174
+ ...row.metadata.min_bundle_id === void 0 ? {} : { minBundleId: row.metadata.min_bundle_id },
174
175
  appVersion: row.app_version,
175
176
  channel: row.channel,
176
177
  cohort: row.metadata.cohort,
@@ -186,6 +187,7 @@ const toEventHistoryRow = (row) => ({
186
187
  });
187
188
  const toInstallationRow = (row) => ({
188
189
  ...eventDetails(row).httpResponse ? { httpResponse: eventDetails(row).httpResponse } : {},
190
+ ...row.metadata.min_bundle_id === void 0 ? {} : { minBundleId: row.metadata.min_bundle_id },
189
191
  appVersion: row.app_version,
190
192
  channel: row.channel,
191
193
  cohort: row.metadata.cohort,
@@ -171,6 +171,7 @@ const eventDetails = ({ type, metadata }) => ({
171
171
  const toEventHistoryRow = (row) => ({
172
172
  ...row.to_release_id == null ? {} : { toReleaseId: row.to_release_id },
173
173
  ...row.metadata.sdk_version == null ? {} : { sdkVersion: row.metadata.sdk_version },
174
+ ...row.metadata.min_bundle_id === void 0 ? {} : { minBundleId: row.metadata.min_bundle_id },
174
175
  appVersion: row.app_version,
175
176
  channel: row.channel,
176
177
  cohort: row.metadata.cohort,
@@ -186,6 +187,7 @@ const toEventHistoryRow = (row) => ({
186
187
  });
187
188
  const toInstallationRow = (row) => ({
188
189
  ...eventDetails(row).httpResponse ? { httpResponse: eventDetails(row).httpResponse } : {},
190
+ ...row.metadata.min_bundle_id === void 0 ? {} : { minBundleId: row.metadata.min_bundle_id },
189
191
  appVersion: row.app_version,
190
192
  channel: row.channel,
191
193
  cohort: row.metadata.cohort,
@@ -410,26 +410,49 @@ const getAppUsage = async (db, input, now, retention) => {
410
410
  lt: timeRange.end
411
411
  };
412
412
  const distribution = [];
413
- for (const platform of reported) distribution.push(...await drain((page) => input.appVersion === void 0 ? db.findAggregates("insights_distribution", {
414
- index: "byScope",
415
- where: {
416
- channel: input.channel,
417
- platform
418
- },
419
- range,
420
- limit: PAGE,
421
- ...page
422
- }) : db.findAggregates("insights_distribution", {
423
- index: "byVersion",
424
- where: {
425
- channel: input.channel,
426
- platform,
427
- app_version: input.appVersion
428
- },
429
- range,
430
- limit: PAGE,
431
- ...page
432
- })));
413
+ const builtinDistribution = [];
414
+ for (const platform of reported) {
415
+ distribution.push(...await drain((page) => input.appVersion === void 0 ? db.findAggregates("insights_distribution", {
416
+ index: "byScope",
417
+ where: {
418
+ channel: input.channel,
419
+ platform
420
+ },
421
+ range,
422
+ limit: PAGE,
423
+ ...page
424
+ }) : db.findAggregates("insights_distribution", {
425
+ index: "byVersion",
426
+ where: {
427
+ channel: input.channel,
428
+ platform,
429
+ app_version: input.appVersion
430
+ },
431
+ range,
432
+ limit: PAGE,
433
+ ...page
434
+ })));
435
+ builtinDistribution.push(...await drain((page) => input.appVersion === void 0 ? db.findAggregates("insights_builtin_distribution", {
436
+ index: "byScope",
437
+ where: {
438
+ channel: input.channel,
439
+ platform
440
+ },
441
+ range,
442
+ limit: PAGE,
443
+ ...page
444
+ }) : db.findAggregates("insights_builtin_distribution", {
445
+ index: "byVersion",
446
+ where: {
447
+ channel: input.channel,
448
+ platform,
449
+ app_version: input.appVersion
450
+ },
451
+ range,
452
+ limit: PAGE,
453
+ ...page
454
+ })));
455
+ }
433
456
  const points = [];
434
457
  for (let start = timeRange.start; start < timeRange.end; start += intervalMs) points.push({
435
458
  startMs: start,
@@ -438,22 +461,31 @@ const getAppUsage = async (db, input, now, retention) => {
438
461
  const versions = /* @__PURE__ */ new Map();
439
462
  const platforms = /* @__PURE__ */ new Map();
440
463
  const bundles = /* @__PURE__ */ new Map();
441
- for (const row of distribution) {
442
- const count = row.latest_installations;
443
- versions.set(row.app_version, (versions.get(row.app_version) ?? 0) + count);
444
- platforms.set(row.platform, (platforms.get(row.platform) ?? 0) + count);
445
- const releaseId = row.release_id === "" ? null : row.release_id;
464
+ const countBundle = (row, releaseId, builtinBundleId, count) => {
446
465
  const key = JSON.stringify([
447
466
  row.app_version,
448
467
  row.platform,
449
- releaseId
468
+ releaseId,
469
+ builtinBundleId
450
470
  ]);
451
471
  bundles.set(key, {
452
472
  appVersion: row.app_version,
453
473
  platform: row.platform,
454
474
  releaseId,
475
+ builtinBundleId,
455
476
  installations: (bundles.get(key)?.installations ?? 0) + count
456
477
  });
478
+ };
479
+ for (const row of distribution) {
480
+ const count = row.latest_installations;
481
+ versions.set(row.app_version, (versions.get(row.app_version) ?? 0) + count);
482
+ platforms.set(row.platform, (platforms.get(row.platform) ?? 0) + count);
483
+ countBundle(row, row.release_id || null, null, count);
484
+ }
485
+ for (const row of builtinDistribution) {
486
+ const releaseId = row.release_id || null;
487
+ countBundle(row, releaseId, null, -row.latest_installations);
488
+ countBundle(row, releaseId, row.builtin_bundle_id, row.latest_installations);
457
489
  }
458
490
  const sortedVersions = byInstallations(versions);
459
491
  return {
@@ -410,26 +410,49 @@ const getAppUsage = async (db, input, now, retention) => {
410
410
  lt: timeRange.end
411
411
  };
412
412
  const distribution = [];
413
- for (const platform of reported) distribution.push(...await drain((page) => input.appVersion === void 0 ? db.findAggregates("insights_distribution", {
414
- index: "byScope",
415
- where: {
416
- channel: input.channel,
417
- platform
418
- },
419
- range,
420
- limit: PAGE,
421
- ...page
422
- }) : db.findAggregates("insights_distribution", {
423
- index: "byVersion",
424
- where: {
425
- channel: input.channel,
426
- platform,
427
- app_version: input.appVersion
428
- },
429
- range,
430
- limit: PAGE,
431
- ...page
432
- })));
413
+ const builtinDistribution = [];
414
+ for (const platform of reported) {
415
+ distribution.push(...await drain((page) => input.appVersion === void 0 ? db.findAggregates("insights_distribution", {
416
+ index: "byScope",
417
+ where: {
418
+ channel: input.channel,
419
+ platform
420
+ },
421
+ range,
422
+ limit: PAGE,
423
+ ...page
424
+ }) : db.findAggregates("insights_distribution", {
425
+ index: "byVersion",
426
+ where: {
427
+ channel: input.channel,
428
+ platform,
429
+ app_version: input.appVersion
430
+ },
431
+ range,
432
+ limit: PAGE,
433
+ ...page
434
+ })));
435
+ builtinDistribution.push(...await drain((page) => input.appVersion === void 0 ? db.findAggregates("insights_builtin_distribution", {
436
+ index: "byScope",
437
+ where: {
438
+ channel: input.channel,
439
+ platform
440
+ },
441
+ range,
442
+ limit: PAGE,
443
+ ...page
444
+ }) : db.findAggregates("insights_builtin_distribution", {
445
+ index: "byVersion",
446
+ where: {
447
+ channel: input.channel,
448
+ platform,
449
+ app_version: input.appVersion
450
+ },
451
+ range,
452
+ limit: PAGE,
453
+ ...page
454
+ })));
455
+ }
433
456
  const points = [];
434
457
  for (let start = timeRange.start; start < timeRange.end; start += intervalMs) points.push({
435
458
  startMs: start,
@@ -438,22 +461,31 @@ const getAppUsage = async (db, input, now, retention) => {
438
461
  const versions = /* @__PURE__ */ new Map();
439
462
  const platforms = /* @__PURE__ */ new Map();
440
463
  const bundles = /* @__PURE__ */ new Map();
441
- for (const row of distribution) {
442
- const count = row.latest_installations;
443
- versions.set(row.app_version, (versions.get(row.app_version) ?? 0) + count);
444
- platforms.set(row.platform, (platforms.get(row.platform) ?? 0) + count);
445
- const releaseId = row.release_id === "" ? null : row.release_id;
464
+ const countBundle = (row, releaseId, builtinBundleId, count) => {
446
465
  const key = JSON.stringify([
447
466
  row.app_version,
448
467
  row.platform,
449
- releaseId
468
+ releaseId,
469
+ builtinBundleId
450
470
  ]);
451
471
  bundles.set(key, {
452
472
  appVersion: row.app_version,
453
473
  platform: row.platform,
454
474
  releaseId,
475
+ builtinBundleId,
455
476
  installations: (bundles.get(key)?.installations ?? 0) + count
456
477
  });
478
+ };
479
+ for (const row of distribution) {
480
+ const count = row.latest_installations;
481
+ versions.set(row.app_version, (versions.get(row.app_version) ?? 0) + count);
482
+ platforms.set(row.platform, (platforms.get(row.platform) ?? 0) + count);
483
+ countBundle(row, row.release_id || null, null, count);
484
+ }
485
+ for (const row of builtinDistribution) {
486
+ const releaseId = row.release_id || null;
487
+ countBundle(row, releaseId, null, -row.latest_installations);
488
+ countBundle(row, releaseId, row.builtin_bundle_id, row.latest_installations);
457
489
  }
458
490
  const sortedVersions = byInstallations(versions);
459
491
  return {
@@ -1,3 +1,4 @@
1
+ const require_eventRow = require("./eventRow.cjs");
1
2
  const require_contract = require("./contract.cjs");
2
3
  const require_overview = require("./overview.cjs");
3
4
  const require_schema = require("./schema.cjs");
@@ -13,13 +14,16 @@ const insightsIdentity = (identity) => require_overview.insightsOverviewId({
13
14
  bucketStartMs: 0
14
15
  });
15
16
  /**
16
- * The bucket a head's gauges count it in, the UTC day of its event, and the
17
- * release its distribution row names: the one it runs, which a download
18
- * leaves at its source.
17
+ * The bucket a head's gauges count it in, the UTC day of its event, and what
18
+ * its distribution row names: the release it runs, which a download leaves
19
+ * at its source, and the built-in bundle, when it runs the native build's.
20
+ * A head stored without the built-in bundle ID counts in no built-in row, so
21
+ * taking it back moves none.
19
22
  */
20
23
  const gaugeSlot = (head) => ({
21
24
  bucket: dayOf(head.received_at_ms),
22
- releaseId: head.type === "UPDATE_DOWNLOADED" ? head.from_release_id : head.to_release_id
25
+ releaseId: head.type === "UPDATE_DOWNLOADED" ? head.from_release_id : head.to_release_id,
26
+ builtinBundleId: require_eventRow.runningBuiltinBundleId(head)
23
27
  });
24
28
  /**
25
29
  * The `insights_latest_by_bundle` field of a head's (from, to) pair. A head
@@ -30,11 +34,12 @@ const PAIR_FIELD = "from_to";
30
34
  /** The pair gauge's `bundle_id`: a hash of the two bundle ids, which fit no single 36-character column. */
31
35
  const bundlePairKey = (from, to) => require_overview.insightsKey(`${from.length}:${from}${to.length}:${to}`);
32
36
  /**
33
- * Moves a head's gauges: the distribution row, one row per bundle it
34
- * references, and its pair; -1 takes back the head an event replaces.
37
+ * Moves a head's gauges: the distribution row, and the built-in one when it
38
+ * runs its build's built-in bundle, one row per bundle it references, and
39
+ * its pair; -1 takes back the head an event replaces.
35
40
  */
36
41
  const countHead = (tx, head, delta) => {
37
- const { bucket, releaseId } = gaugeSlot(head);
42
+ const { bucket, releaseId, builtinBundleId } = gaugeSlot(head);
38
43
  const shardBy = head.install_id;
39
44
  tx.aggregate("insights_distribution", {
40
45
  channel: head.channel,
@@ -43,6 +48,14 @@ const countHead = (tx, head, delta) => {
43
48
  release_id: releaseId ?? "",
44
49
  bucket_start_ms: bucket
45
50
  }, { latest_installations: delta }, { shardBy });
51
+ if (builtinBundleId !== null) tx.aggregate("insights_builtin_distribution", {
52
+ channel: head.channel,
53
+ platform: head.platform,
54
+ app_version: head.app_version,
55
+ release_id: releaseId ?? "",
56
+ builtin_bundle_id: builtinBundleId,
57
+ bucket_start_ms: bucket
58
+ }, { latest_installations: delta }, { shardBy });
46
59
  for (const field of ["from_bundle_id", "to_bundle_id"]) {
47
60
  const bundleId = head[field];
48
61
  if (bundleId === null) continue;
@@ -1,3 +1,4 @@
1
+ import { runningBuiltinBundleId } from "./eventRow.mjs";
1
2
  import { assertBundleEventRow } from "./contract.mjs";
2
3
  import { insightsKey, insightsOverviewDeltas, insightsOverviewId } from "./overview.mjs";
3
4
  import { DAILY_EVENTS, DAY_MS, HOUR_MS, isFailedCheck } from "./schema.mjs";
@@ -13,13 +14,16 @@ const insightsIdentity = (identity) => insightsOverviewId({
13
14
  bucketStartMs: 0
14
15
  });
15
16
  /**
16
- * The bucket a head's gauges count it in, the UTC day of its event, and the
17
- * release its distribution row names: the one it runs, which a download
18
- * leaves at its source.
17
+ * The bucket a head's gauges count it in, the UTC day of its event, and what
18
+ * its distribution row names: the release it runs, which a download leaves
19
+ * at its source, and the built-in bundle, when it runs the native build's.
20
+ * A head stored without the built-in bundle ID counts in no built-in row, so
21
+ * taking it back moves none.
19
22
  */
20
23
  const gaugeSlot = (head) => ({
21
24
  bucket: dayOf(head.received_at_ms),
22
- releaseId: head.type === "UPDATE_DOWNLOADED" ? head.from_release_id : head.to_release_id
25
+ releaseId: head.type === "UPDATE_DOWNLOADED" ? head.from_release_id : head.to_release_id,
26
+ builtinBundleId: runningBuiltinBundleId(head)
23
27
  });
24
28
  /**
25
29
  * The `insights_latest_by_bundle` field of a head's (from, to) pair. A head
@@ -30,11 +34,12 @@ const PAIR_FIELD = "from_to";
30
34
  /** The pair gauge's `bundle_id`: a hash of the two bundle ids, which fit no single 36-character column. */
31
35
  const bundlePairKey = (from, to) => insightsKey(`${from.length}:${from}${to.length}:${to}`);
32
36
  /**
33
- * Moves a head's gauges: the distribution row, one row per bundle it
34
- * references, and its pair; -1 takes back the head an event replaces.
37
+ * Moves a head's gauges: the distribution row, and the built-in one when it
38
+ * runs its build's built-in bundle, one row per bundle it references, and
39
+ * its pair; -1 takes back the head an event replaces.
35
40
  */
36
41
  const countHead = (tx, head, delta) => {
37
- const { bucket, releaseId } = gaugeSlot(head);
42
+ const { bucket, releaseId, builtinBundleId } = gaugeSlot(head);
38
43
  const shardBy = head.install_id;
39
44
  tx.aggregate("insights_distribution", {
40
45
  channel: head.channel,
@@ -43,6 +48,14 @@ const countHead = (tx, head, delta) => {
43
48
  release_id: releaseId ?? "",
44
49
  bucket_start_ms: bucket
45
50
  }, { latest_installations: delta }, { shardBy });
51
+ if (builtinBundleId !== null) tx.aggregate("insights_builtin_distribution", {
52
+ channel: head.channel,
53
+ platform: head.platform,
54
+ app_version: head.app_version,
55
+ release_id: releaseId ?? "",
56
+ builtin_bundle_id: builtinBundleId,
57
+ bucket_start_ms: bucket
58
+ }, { latest_installations: delta }, { shardBy });
46
59
  for (const field of ["from_bundle_id", "to_bundle_id"]) {
47
60
  const bundleId = head[field];
48
61
  if (bundleId === null) continue;
@@ -354,6 +354,55 @@ const insightsDistribution = (retention) => (0, _hot_updater_plugin_core.defineA
354
354
  },
355
355
  retention
356
356
  });
357
+ /**
358
+ * The installations of `insights_distribution` that run their native build's
359
+ * built-in bundle, by its ID (`builtin_bundle_id`), within the same rows:
360
+ * reads move them out of the row of their release, or of no release.
361
+ */
362
+ const insightsBuiltinDistribution = (retention) => (0, _hot_updater_plugin_core.defineAggregate)({
363
+ channel: { type: "string" },
364
+ platform: {
365
+ type: "string",
366
+ maxLength: 16
367
+ },
368
+ app_version: { type: "string" },
369
+ release_id: {
370
+ type: "string",
371
+ maxLength: 36
372
+ },
373
+ builtin_bundle_id: {
374
+ type: "string",
375
+ maxLength: 36
376
+ },
377
+ bucket_start_ms: { type: "integer" }
378
+ }, {
379
+ key: [
380
+ "channel",
381
+ "platform",
382
+ "app_version",
383
+ "release_id",
384
+ "builtin_bundle_id",
385
+ "bucket_start_ms"
386
+ ],
387
+ gauges: ["latest_installations"],
388
+ shards: GAUGE_SHARDS,
389
+ batched: true,
390
+ indexes: {
391
+ byScope: {
392
+ eq: ["channel", "platform"],
393
+ sort: ["bucket_start_ms"]
394
+ },
395
+ byVersion: {
396
+ eq: [
397
+ "channel",
398
+ "platform",
399
+ "app_version"
400
+ ],
401
+ sort: ["bucket_start_ms"]
402
+ }
403
+ },
404
+ retention
405
+ });
357
406
  /** Latest events by the bundle they came from or went to, bucketed like the distribution. */
358
407
  const insightsLatestByBundle = (retention) => (0, _hot_updater_plugin_core.defineAggregate)({
359
408
  platform: {
@@ -473,6 +522,7 @@ const createInsightsSchema = ({ rawDays, dailyDays } = DEFAULT_INSIGHTS_RETENTIO
473
522
  insights_overview_lifetime: lifetimeCounters(),
474
523
  insights_sketches_lifetime: lifetimeSketches(),
475
524
  insights_distribution: insightsDistribution(daily),
525
+ insights_builtin_distribution: insightsBuiltinDistribution(daily),
476
526
  insights_latest_by_bundle: insightsLatestByBundle(daily),
477
527
  insights_outcomes: insightsOutcomes(hourly),
478
528
  insights_failures: insightsFailures(hourly)
@@ -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";
@@ -354,6 +354,55 @@ const insightsDistribution = (retention) => defineAggregate({
354
354
  },
355
355
  retention
356
356
  });
357
+ /**
358
+ * The installations of `insights_distribution` that run their native build's
359
+ * built-in bundle, by its ID (`builtin_bundle_id`), within the same rows:
360
+ * reads move them out of the row of their release, or of no release.
361
+ */
362
+ const insightsBuiltinDistribution = (retention) => defineAggregate({
363
+ channel: { type: "string" },
364
+ platform: {
365
+ type: "string",
366
+ maxLength: 16
367
+ },
368
+ app_version: { type: "string" },
369
+ release_id: {
370
+ type: "string",
371
+ maxLength: 36
372
+ },
373
+ builtin_bundle_id: {
374
+ type: "string",
375
+ maxLength: 36
376
+ },
377
+ bucket_start_ms: { type: "integer" }
378
+ }, {
379
+ key: [
380
+ "channel",
381
+ "platform",
382
+ "app_version",
383
+ "release_id",
384
+ "builtin_bundle_id",
385
+ "bucket_start_ms"
386
+ ],
387
+ gauges: ["latest_installations"],
388
+ shards: GAUGE_SHARDS,
389
+ batched: true,
390
+ indexes: {
391
+ byScope: {
392
+ eq: ["channel", "platform"],
393
+ sort: ["bucket_start_ms"]
394
+ },
395
+ byVersion: {
396
+ eq: [
397
+ "channel",
398
+ "platform",
399
+ "app_version"
400
+ ],
401
+ sort: ["bucket_start_ms"]
402
+ }
403
+ },
404
+ retention
405
+ });
357
406
  /** Latest events by the bundle they came from or went to, bucketed like the distribution. */
358
407
  const insightsLatestByBundle = (retention) => defineAggregate({
359
408
  platform: {
@@ -473,6 +522,7 @@ const createInsightsSchema = ({ rawDays, dailyDays } = DEFAULT_INSIGHTS_RETENTIO
473
522
  insights_overview_lifetime: lifetimeCounters(),
474
523
  insights_sketches_lifetime: lifetimeSketches(),
475
524
  insights_distribution: insightsDistribution(daily),
525
+ insights_builtin_distribution: insightsBuiltinDistribution(daily),
476
526
  insights_latest_by_bundle: insightsLatestByBundle(daily),
477
527
  insights_outcomes: insightsOutcomes(hourly),
478
528
  insights_failures: insightsFailures(hourly)
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.34",
4
+ "version": "1.0.0-rc.36",
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.30"
36
+ "@hot-updater/protocol": "1.0.0-rc.36"
37
37
  },
38
38
  "peerDependencies": {
39
- "@hot-updater/plugin-core": "^1.0.0-rc.30"
39
+ "@hot-updater/plugin-core": "^1.0.0-rc.36"
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.30",
50
- "@hot-updater/test-utils": "1.0.0-rc.32"
49
+ "@hot-updater/plugin-core": "1.0.0-rc.36",
50
+ "@hot-updater/test-utils": "1.0.0-rc.36"
51
51
  },
52
52
  "nx": {
53
53
  "implicitDependencies": [