@hot-updater/aws 1.0.0-rc.32 → 1.0.0-rc.34

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/index.d.cts CHANGED
@@ -86,8 +86,6 @@ type BundleEventFailure = DatabaseJsonObject & {
86
86
  readonly origin_code?: string;
87
87
  readonly error_message?: string;
88
88
  readonly error_stack?: string;
89
- /** Android's `ApplicationExitInfo` reason for the process before this one. */
90
- readonly previous_process_exit?: string;
91
89
  };
92
90
  type DatabaseHttpResponse = DatabaseJsonObject & {
93
91
  readonly resource: "catalog" | "artifact";
@@ -110,8 +108,6 @@ type DatabaseBundleEventMetadata = DatabaseJsonObject & {
110
108
  readonly delivery?: "patch" | "manifest" | "archive" | "unknown";
111
109
  /** `UPDATE_DOWNLOADED`: a patch failed and the full files came instead. */
112
110
  readonly patch_fallback?: boolean;
113
- /** `RECOVERED`: Android's `ApplicationExitInfo` reason for the crashed process. */
114
- readonly previous_process_exit?: string;
115
111
  };
116
112
  type BundleEventRowBase = {
117
113
  readonly id: string;
@@ -236,8 +232,11 @@ type InsightsCoverage = {
236
232
  * which are kept.
237
233
  */
238
234
  interface ReleaseActivityMetrics {
235
+ /** Download reports of the release. */
239
236
  readonly downloads: number;
240
- readonly launches: number;
237
+ /** Apply reports of the release: installations that started running it. */
238
+ readonly applies: number;
239
+ /** Launches that crashed on the release and recovered to the one before. */
241
240
  readonly failedLaunches: number;
242
241
  }
243
242
  interface InsightsGetReleaseActivityInput {
@@ -337,17 +336,6 @@ interface InsightsUpdateFailures {
337
336
  };
338
337
  /** Over a time range: failures by stage and reason, most first. */
339
338
  readonly breakdown?: readonly InsightsFailureBreakdown[];
340
- /**
341
- * Over a time range: failed launches, and the recoveries whose crashed
342
- * process exited for a reason Android 11+ reported, by reason, most first.
343
- */
344
- readonly recoveries?: {
345
- readonly failedLaunches: number;
346
- readonly byExitReason: readonly {
347
- readonly exitReason: string;
348
- readonly events: number;
349
- }[];
350
- };
351
339
  }
352
340
  //#endregion
353
341
  //#region ../api-keys/dist/server/model.d.mts
@@ -515,7 +503,7 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
515
503
  readonly bucket_start_ms: {
516
504
  readonly type: "integer";
517
505
  };
518
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
506
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks", {
519
507
  readonly window: {
520
508
  readonly eq: readonly ["identity"];
521
509
  readonly sort: readonly ["bucket_start_ms"];
@@ -543,7 +531,7 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
543
531
  readonly bucket_start_ms: {
544
532
  readonly type: "integer";
545
533
  };
546
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
534
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks", {
547
535
  readonly window: {
548
536
  readonly eq: readonly ["identity"];
549
537
  readonly sort: readonly ["bucket_start_ms"];
@@ -571,7 +559,7 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
571
559
  readonly bucket_start_ms: {
572
560
  readonly type: "integer";
573
561
  };
574
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
562
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks" | "applies" | "failed_updates", {
575
563
  readonly window: {
576
564
  readonly eq: readonly ["identity"];
577
565
  readonly sort: readonly ["bucket_start_ms"];
package/dist/index.d.mts CHANGED
@@ -86,8 +86,6 @@ type BundleEventFailure = DatabaseJsonObject & {
86
86
  readonly origin_code?: string;
87
87
  readonly error_message?: string;
88
88
  readonly error_stack?: string;
89
- /** Android's `ApplicationExitInfo` reason for the process before this one. */
90
- readonly previous_process_exit?: string;
91
89
  };
92
90
  type DatabaseHttpResponse = DatabaseJsonObject & {
93
91
  readonly resource: "catalog" | "artifact";
@@ -110,8 +108,6 @@ type DatabaseBundleEventMetadata = DatabaseJsonObject & {
110
108
  readonly delivery?: "patch" | "manifest" | "archive" | "unknown";
111
109
  /** `UPDATE_DOWNLOADED`: a patch failed and the full files came instead. */
112
110
  readonly patch_fallback?: boolean;
113
- /** `RECOVERED`: Android's `ApplicationExitInfo` reason for the crashed process. */
114
- readonly previous_process_exit?: string;
115
111
  };
116
112
  type BundleEventRowBase = {
117
113
  readonly id: string;
@@ -236,8 +232,11 @@ type InsightsCoverage = {
236
232
  * which are kept.
237
233
  */
238
234
  interface ReleaseActivityMetrics {
235
+ /** Download reports of the release. */
239
236
  readonly downloads: number;
240
- readonly launches: number;
237
+ /** Apply reports of the release: installations that started running it. */
238
+ readonly applies: number;
239
+ /** Launches that crashed on the release and recovered to the one before. */
241
240
  readonly failedLaunches: number;
242
241
  }
243
242
  interface InsightsGetReleaseActivityInput {
@@ -337,17 +336,6 @@ interface InsightsUpdateFailures {
337
336
  };
338
337
  /** Over a time range: failures by stage and reason, most first. */
339
338
  readonly breakdown?: readonly InsightsFailureBreakdown[];
340
- /**
341
- * Over a time range: failed launches, and the recoveries whose crashed
342
- * process exited for a reason Android 11+ reported, by reason, most first.
343
- */
344
- readonly recoveries?: {
345
- readonly failedLaunches: number;
346
- readonly byExitReason: readonly {
347
- readonly exitReason: string;
348
- readonly events: number;
349
- }[];
350
- };
351
339
  }
352
340
  //#endregion
353
341
  //#region ../api-keys/dist/server/model.d.mts
@@ -515,7 +503,7 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
515
503
  readonly bucket_start_ms: {
516
504
  readonly type: "integer";
517
505
  };
518
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
506
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks", {
519
507
  readonly window: {
520
508
  readonly eq: readonly ["identity"];
521
509
  readonly sort: readonly ["bucket_start_ms"];
@@ -543,7 +531,7 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
543
531
  readonly bucket_start_ms: {
544
532
  readonly type: "integer";
545
533
  };
546
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
534
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks", {
547
535
  readonly window: {
548
536
  readonly eq: readonly ["identity"];
549
537
  readonly sort: readonly ["bucket_start_ms"];
@@ -571,7 +559,7 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
571
559
  readonly bucket_start_ms: {
572
560
  readonly type: "integer";
573
561
  };
574
- }, "downloads" | "launches" | "failed_launches" | "failed_updates" | "patch_downloads" | "patch_fallbacks", {
562
+ }, "downloads" | "failed_launches" | "patch_downloads" | "patch_fallbacks" | "applies" | "failed_updates", {
575
563
  readonly window: {
576
564
  readonly eq: readonly ["identity"];
577
565
  readonly sort: readonly ["bucket_start_ms"];
@@ -4570,7 +4570,7 @@ const createKvAdapter = ({ store, tablePrefix = "" }) => {
4570
4570
  };
4571
4571
  //#endregion
4572
4572
  //#region ../../packages/server/dist/version.mjs
4573
- const HOT_UPDATER_SERVER_VERSION = "1.0.0-rc.32";
4573
+ const HOT_UPDATER_SERVER_VERSION = "1.0.0-rc.34";
4574
4574
  //#endregion
4575
4575
  //#region ../../packages/server/dist/handlerVersionRoutes.mjs
4576
4576
  /**
@@ -9792,8 +9792,8 @@ const isOptional = (value, key, valid) => !Object.hasOwn(value, key) || valid(va
9792
9792
  const isString = (value) => typeof value === "string";
9793
9793
  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);
9794
9794
  /** An `UPDATE_FAILED` report's stored failure: a stage and a reason, and what else the client knew. */
9795
- 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) && isOptional(value, "previous_process_exit", isString);
9796
- 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") && isOptional(value, "previous_process_exit", isString);
9795
+ 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);
9796
+ 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");
9797
9797
  const isRecord$2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
9798
9798
  const isIdentity$1 = (value) => typeof value === "string" && value.length > 0 && value.length <= 255;
9799
9799
  const isTextOrNull = (value) => value === null || typeof value === "string";
@@ -9998,7 +9998,7 @@ const createValidatedInsightsModel = (model) => ({
9998
9998
  async getReleaseActivity(input) {
9999
9999
  if (!isRecord$2(input) || !hasOnlyKeys(input, ["releases"]) || !Array.isArray(input.releases) || input.releases.length < 1 || input.releases.length > 100 || !input.releases.every(isReleaseReference)) invalidQuery();
10000
10000
  const result = await model.getReleaseActivity(input);
10001
- if (!isRecord$2(result) || !isTimestamp(result.measuredAtMs) || !Array.isArray(result.data) || result.data.some((item) => !isRecord$2(item) || !isReleaseReference(item.release) || !isRecord$2(item.metrics) || !isTimestamp(item.metrics.downloads) || !isTimestamp(item.metrics.launches) || !isTimestamp(item.metrics.failedLaunches))) invalidResult();
10001
+ if (!isRecord$2(result) || !isTimestamp(result.measuredAtMs) || !Array.isArray(result.data) || result.data.some((item) => !isRecord$2(item) || !isReleaseReference(item.release) || !isRecord$2(item.metrics) || !isTimestamp(item.metrics.downloads) || !isTimestamp(item.metrics.applies) || !isTimestamp(item.metrics.failedLaunches))) invalidResult();
10002
10002
  return result;
10003
10003
  },
10004
10004
  async getAppUsage(input) {
@@ -10133,7 +10133,7 @@ const DELIVERIES = [
10133
10133
  "manifest",
10134
10134
  "archive"
10135
10135
  ];
10136
- /** A storage error code or an Android exit reason: letters, digits, and `._-`. */
10136
+ /** A storage error code: letters, digits, and `._-`. */
10137
10137
  const CODE = /^[A-Za-z0-9._-]{1,64}$/;
10138
10138
  const readCode = (value) => typeof value === "string" && CODE.test(value) ? value : void 0;
10139
10139
  const readMetadata = (payload) => isRecord$1(payload.metadata) ? payload.metadata : {};
@@ -10149,7 +10149,6 @@ function readFailure(value) {
10149
10149
  const originCode = readCode(failure.originCode);
10150
10150
  const errorMessage = typeof failure.errorMessage === "string" && failure.errorMessage.length <= 2048 ? failure.errorMessage : void 0;
10151
10151
  const errorStack = typeof failure.errorStack === "string" && failure.errorStack.length <= 4096 ? failure.errorStack : void 0;
10152
- const previousProcessExit = readCode(failure.previousProcessExit);
10153
10152
  return {
10154
10153
  stage: oneOf(STAGES, failure.stage),
10155
10154
  reason: oneOf(REASONS, failure.reason),
@@ -10158,8 +10157,7 @@ function readFailure(value) {
10158
10157
  ...failure.transport == null ? {} : { transport: oneOf(TRANSPORTS, failure.transport) },
10159
10158
  ...originCode === void 0 ? {} : { originCode },
10160
10159
  ...errorMessage === void 0 ? {} : { errorMessage },
10161
- ...errorStack === void 0 ? {} : { errorStack },
10162
- ...previousProcessExit === void 0 ? {} : { previousProcessExit }
10160
+ ...errorStack === void 0 ? {} : { errorStack }
10163
10161
  };
10164
10162
  }
10165
10163
  function readHttpResponse(value) {
@@ -10249,21 +10247,11 @@ function requireEvent(payload) {
10249
10247
  ...Object.keys(read).length === 0 ? {} : { metadata: read }
10250
10248
  };
10251
10249
  }
10252
- case "UPDATE_APPLIED": return {
10250
+ case "UPDATE_APPLIED":
10251
+ case "RECOVERED": return {
10253
10252
  ...movement(),
10254
10253
  type
10255
10254
  };
10256
- case "RECOVERED": {
10257
- const previousProcessExit = readCode(metadata.previousProcessExit);
10258
- return {
10259
- ...movement(),
10260
- type,
10261
- ...previousProcessExit === void 0 ? {} : { metadata: {
10262
- ...httpMetadata,
10263
- previousProcessExit
10264
- } }
10265
- };
10266
- }
10267
10255
  case "UPDATE_FAILED": return {
10268
10256
  ...movement(),
10269
10257
  type,
@@ -10329,24 +10317,13 @@ function createBundleEventRow(input) {
10329
10317
  }
10330
10318
  };
10331
10319
  }
10332
- case "UPDATE_APPLIED": return {
10320
+ case "UPDATE_APPLIED":
10321
+ case "RECOVERED": return {
10333
10322
  ...base,
10334
10323
  from_bundle_id: input.fromBundleId,
10335
10324
  type: input.type,
10336
10325
  metadata
10337
10326
  };
10338
- case "RECOVERED": {
10339
- const exit = input.metadata?.previousProcessExit;
10340
- return {
10341
- ...base,
10342
- from_bundle_id: input.fromBundleId,
10343
- type: input.type,
10344
- metadata: {
10345
- ...metadata,
10346
- ...exit === void 0 ? {} : { previous_process_exit: exit }
10347
- }
10348
- };
10349
- }
10350
10327
  case "UPDATE_FAILED": {
10351
10328
  const { failure } = input.metadata;
10352
10329
  return {
@@ -10363,8 +10340,7 @@ function createBundleEventRow(input) {
10363
10340
  ...failure.resource === void 0 ? {} : { resource: failure.resource },
10364
10341
  ...failure.httpStatus === void 0 ? {} : { http_status: failure.httpStatus },
10365
10342
  ...failure.transport === void 0 ? {} : { transport: failure.transport },
10366
- ...failure.originCode === void 0 ? {} : { origin_code: failure.originCode },
10367
- ...failure.previousProcessExit === void 0 ? {} : { previous_process_exit: failure.previousProcessExit }
10343
+ ...failure.originCode === void 0 ? {} : { origin_code: failure.originCode }
10368
10344
  }
10369
10345
  }
10370
10346
  };
@@ -10528,10 +10504,9 @@ const toFailure = (failure) => ({
10528
10504
  ...failure.resource === void 0 ? {} : { resource: failure.resource },
10529
10505
  ...failure.http_status === void 0 ? {} : { httpStatus: failure.http_status },
10530
10506
  ...failure.transport === void 0 ? {} : { transport: failure.transport },
10531
- ...failure.origin_code === void 0 ? {} : { originCode: failure.origin_code },
10532
- ...failure.previous_process_exit === void 0 ? {} : { previousProcessExit: failure.previous_process_exit }
10507
+ ...failure.origin_code === void 0 ? {} : { originCode: failure.origin_code }
10533
10508
  });
10534
- /** What a row's metadata adds for its type: a failure, a delivery, an exit reason. */
10509
+ /** What a row's metadata adds for its type: a failure or a delivery. */
10535
10510
  const eventDetails = ({ type, metadata }) => ({
10536
10511
  ...metadata.http_response !== void 0 ? { httpResponse: {
10537
10512
  resource: metadata.http_response.resource,
@@ -10543,8 +10518,7 @@ const eventDetails = ({ type, metadata }) => ({
10543
10518
  } } : {},
10544
10519
  ...type === "UPDATE_FAILED" && metadata.failure !== void 0 ? { failure: toFailure(metadata.failure) } : {},
10545
10520
  ...type === "UPDATE_DOWNLOADED" && metadata.delivery !== void 0 ? { delivery: metadata.delivery } : {},
10546
- ...type === "UPDATE_DOWNLOADED" && metadata.patch_fallback === true ? { patchFallback: true } : {},
10547
- ...type === "RECOVERED" && metadata.previous_process_exit !== void 0 ? { previousProcessExit: metadata.previous_process_exit } : {}
10521
+ ...type === "UPDATE_DOWNLOADED" && metadata.patch_fallback === true ? { patchFallback: true } : {}
10548
10522
  });
10549
10523
  const toEventHistoryRow = (row) => ({
10550
10524
  ...row.to_release_id == null ? {} : { toReleaseId: row.to_release_id },
@@ -10967,29 +10941,42 @@ const window = {
10967
10941
  eq: ["identity"],
10968
10942
  sort: ["bucket_start_ms"]
10969
10943
  };
10944
+ /** What a release or channel row counts in every period kind. */
10945
+ const WINDOW_COUNTERS = [
10946
+ "downloads",
10947
+ "failed_launches",
10948
+ "patch_downloads",
10949
+ "patch_fallbacks"
10950
+ ];
10970
10951
  /**
10971
- * Counters of one period kind: hourly, daily, or lifetime at bucket 0. A
10972
- * release or channel row counts downloads and failed launches, and downloads
10973
- * a patch delivered and those that fell back from one. Only a release's
10974
- * lifetime row counts launches, which the bundle list reads and no windowed
10975
- * read sums. It also counts the release's failed updates (`failed_updates`),
10976
- * since no breakdown row outlives the raw period; windowed reads sum
10977
- * `insights_failures` instead.
10952
+ * Hourly or daily counters: a release or channel row counts downloads,
10953
+ * launches that crashed and recovered (`failed_launches`), and downloads a
10954
+ * patch delivered and those that fell back from one.
10978
10955
  */
10979
10956
  const counters = (retention) => defineAggregate(identityFields, {
10957
+ key: ["identity", "bucket_start_ms"],
10958
+ counters: WINDOW_COUNTERS,
10959
+ shards: COUNTER_SHARDS,
10960
+ batched: true,
10961
+ indexes: { window },
10962
+ retention
10963
+ });
10964
+ /**
10965
+ * A release's counters since its first report, at bucket 0: the windowed
10966
+ * counters, its applies, which the bundle list reads, and its failed updates
10967
+ * (`failed_updates`), since no breakdown row outlives the raw period; windowed
10968
+ * reads sum `insights_failures` instead.
10969
+ */
10970
+ const lifetimeCounters = () => defineAggregate(identityFields, {
10980
10971
  key: ["identity", "bucket_start_ms"],
10981
10972
  counters: [
10982
- "downloads",
10983
- "launches",
10984
- "failed_launches",
10985
- "failed_updates",
10986
- "patch_downloads",
10987
- "patch_fallbacks"
10973
+ ...WINDOW_COUNTERS,
10974
+ "applies",
10975
+ "failed_updates"
10988
10976
  ],
10989
10977
  shards: COUNTER_SHARDS,
10990
10978
  batched: true,
10991
- indexes: { window },
10992
- ...retention === void 0 ? {} : { retention }
10979
+ indexes: { window }
10993
10980
  });
10994
10981
  /**
10995
10982
  * Unique-installation sketches of one period kind: a usage row's active
@@ -11013,14 +11000,12 @@ const lifetimeSketches = () => defineAggregate(identityFields, {
11013
11000
  indexes: { window }
11014
11001
  });
11015
11002
  /**
11016
- * Update failures by what failed, and recoveries by why the crashed process
11017
- * exited, a row an hour. `release_id` is the failed update's target release,
11018
- * or empty for a failed check or an unknown target, and the recovered
11019
- * release for a recovery. A failure's `stage` and `reason` say where and why
11020
- * it failed, and `detail` what else the client knew, as the JSON array
11021
- * `[resource, httpStatus, originCode, transport]`. A recovery's stage is
11022
- * `launch` and its reason the exit reason, with an empty detail. Every index
11023
- * is in key order, so its counters stay blind increments.
11003
+ * Update failures by what failed, a row an hour. `release_id` is the failed
11004
+ * update's target release, or empty for a failed check or an unknown target.
11005
+ * `stage` and `reason` say where and why it failed, and `detail` what else
11006
+ * the client knew, as the JSON array `[resource, httpStatus, originCode,
11007
+ * transport]`. Every index is in key order, so its counters stay blind
11008
+ * increments.
11024
11009
  */
11025
11010
  const insightsFailures = (retention) => defineAggregate({
11026
11011
  platform: {
@@ -11220,7 +11205,7 @@ const createInsightsSchema = ({ rawDays, dailyDays } = DEFAULT_INSIGHTS_RETENTIO
11220
11205
  insights_overview_daily: counters(daily),
11221
11206
  insights_sketches_daily: sketches(daily),
11222
11207
  /** A release's counters since its first event, and its failed installations: kept. */
11223
- insights_overview_lifetime: counters(),
11208
+ insights_overview_lifetime: lifetimeCounters(),
11224
11209
  insights_sketches_lifetime: lifetimeSketches(),
11225
11210
  insights_distribution: insightsDistribution(daily),
11226
11211
  insights_latest_by_bundle: insightsLatestByBundle(daily),
@@ -11263,7 +11248,7 @@ const insightsOverviewDeltas = (event) => {
11263
11248
  counters.set(key, {
11264
11249
  identity,
11265
11250
  downloads: (previous?.downloads ?? 0) + values.downloads,
11266
- launches: (previous?.launches ?? 0) + values.launches,
11251
+ applies: (previous?.applies ?? 0) + values.applies,
11267
11252
  failedLaunches: (previous?.failedLaunches ?? 0) + values.failedLaunches,
11268
11253
  activityIdentity: previous?.activityIdentity ?? values.activityIdentity
11269
11254
  });
@@ -11271,7 +11256,7 @@ const insightsOverviewDeltas = (event) => {
11271
11256
  const metric = (releaseId, values) => {
11272
11257
  const windowed = {
11273
11258
  ...values,
11274
- launches: 0
11259
+ applies: 0
11275
11260
  };
11276
11261
  if (releaseId !== null) for (const [periodKind, start] of [["lifetime", 0], ["hour", bucketStartMs]]) append({
11277
11262
  scopeKind: "release",
@@ -11298,21 +11283,19 @@ const insightsOverviewDeltas = (event) => {
11298
11283
  };
11299
11284
  if (event.type === "UPDATE_DOWNLOADED") metric(event.to_release_id, {
11300
11285
  downloads: 1,
11301
- launches: 0,
11286
+ applies: 0,
11302
11287
  failedLaunches: 0
11303
11288
  });
11304
- else if (event.type !== "UPDATE_FAILED") {
11305
- metric(event.to_release_id, {
11306
- downloads: 0,
11307
- launches: 1,
11308
- failedLaunches: 0
11309
- });
11310
- if (event.type === "RECOVERED") metric(event.from_release_id, {
11311
- downloads: 0,
11312
- launches: 0,
11313
- failedLaunches: 1
11314
- });
11315
- }
11289
+ else if (event.type === "UPDATE_APPLIED") metric(event.to_release_id, {
11290
+ downloads: 0,
11291
+ applies: 1,
11292
+ failedLaunches: 0
11293
+ });
11294
+ else if (event.type === "RECOVERED") metric(event.from_release_id, {
11295
+ downloads: 0,
11296
+ applies: 0,
11297
+ failedLaunches: 1
11298
+ });
11316
11299
  for (const platform of [event.platform, "all"]) for (const appVersionKind of ["specific", "all"]) append({
11317
11300
  scopeKind: "usage",
11318
11301
  releaseKind: "all",
@@ -11325,7 +11308,7 @@ const insightsOverviewDeltas = (event) => {
11325
11308
  bucketStartMs
11326
11309
  }, {
11327
11310
  downloads: 0,
11328
- launches: 0,
11311
+ applies: 0,
11329
11312
  failedLaunches: 0,
11330
11313
  activityIdentity: event.install_id
11331
11314
  });
@@ -11442,7 +11425,7 @@ const countEvent = (tx, event) => {
11442
11425
  const counters = {
11443
11426
  ...Object.fromEntries([
11444
11427
  ["downloads", delta.downloads],
11445
- ["launches", delta.launches],
11428
+ ["applies", delta.applies],
11446
11429
  ["failed_launches", delta.failedLaunches]
11447
11430
  ].filter(([, value]) => value !== 0)),
11448
11431
  ...delta.downloads === 0 ? {} : patchCounters(event)
@@ -11565,23 +11548,6 @@ const countFailure = (tx, event) => {
11565
11548
  detail: failureDetail(failure)
11566
11549
  }, { events: 1 }, { shardBy });
11567
11550
  };
11568
- /**
11569
- * A recovery's exit reason, which Android 11+ reports, in the breakdown of
11570
- * the release whose launch failed: stage `launch`, the reason as its reason.
11571
- */
11572
- const countExit = (tx, event) => {
11573
- const exit = event.metadata.previous_process_exit;
11574
- if (event.type !== "RECOVERED" || exit === void 0) return;
11575
- tx.aggregate("insights_failures", {
11576
- platform: event.platform,
11577
- channel: event.channel,
11578
- bucket_start_ms: hourOf(event.received_at_ms),
11579
- release_id: event.from_release_id ?? "",
11580
- stage: "launch",
11581
- reason: exit,
11582
- detail: ""
11583
- }, { events: 1 }, { shardBy: event.install_id });
11584
- };
11585
11551
  const isNewer = (event, head) => event.received_at_ms !== head.received_at_ms ? event.received_at_ms > head.received_at_ms : compareUtf8(event.id, head.id) > 0;
11586
11552
  /** What an UNCHANGED report must share with its installation's head to repeat it. */
11587
11553
  const REPEATED_FIELDS = [
@@ -11626,7 +11592,6 @@ const recordEvent = (db, event) => {
11626
11592
  return;
11627
11593
  }
11628
11594
  countEvent(tx, event);
11629
- countExit(tx, event);
11630
11595
  if (previous !== null && !isNewer(event, previous)) return;
11631
11596
  if (previous === null) tx.create("bundle_event_heads", event);
11632
11597
  else {
@@ -12001,7 +11966,7 @@ const lifetimeMetrics = async (db, release) => {
12001
11966
  })).rows;
12002
11967
  return {
12003
11968
  downloads: lifetime?.downloads ?? 0,
12004
- launches: lifetime?.launches ?? 0,
11969
+ applies: lifetime?.applies ?? 0,
12005
11970
  failedLaunches: lifetime?.failed_launches ?? 0
12006
11971
  };
12007
11972
  };
@@ -12110,21 +12075,18 @@ const isText = (value, maxLength) => typeof value === "string" && value.length >
12110
12075
  const failed = (...rows) => countDistinct(mergeDistinct(rows.flat().map((row) => row.failed_users)));
12111
12076
  /** Entries of a count map, most first, then in key order. */
12112
12077
  const mostFirst = (counts) => [...counts].sort(([leftKey, left], [rightKey, right]) => right.events - left.events || compareUtf8(leftKey, rightKey)).map(([, entry]) => entry);
12113
- /** Failure rows as their breakdown, and recovery rows as exit reasons. */
12078
+ /** The stages a failure row records: where an update failed. */
12079
+ const FAILURE_STAGES = /* @__PURE__ */ new Set([
12080
+ "check",
12081
+ "download",
12082
+ "install",
12083
+ "unknown"
12084
+ ]);
12085
+ /** Failure rows as their breakdown by stage, reason, and detail. */
12114
12086
  const breakdownOf = (rows) => {
12115
12087
  const failures = /* @__PURE__ */ new Map();
12116
- const exits = /* @__PURE__ */ new Map();
12117
12088
  for (const { stage, reason, detail, events } of rows) {
12118
12089
  if (events <= 0) continue;
12119
- if (stage === "launch") {
12120
- const exit = exits.get(reason) ?? {
12121
- value: reason,
12122
- events: 0
12123
- };
12124
- exit.events += events;
12125
- exits.set(reason, exit);
12126
- continue;
12127
- }
12128
12090
  const key = JSON.stringify([stage, reason]);
12129
12091
  const entry = failures.get(key) ?? {
12130
12092
  value: {
@@ -12143,37 +12105,30 @@ const breakdownOf = (rows) => {
12143
12105
  entry.details.set(detail, known);
12144
12106
  failures.set(key, entry);
12145
12107
  }
12146
- return {
12147
- breakdown: mostFirst(failures).map(({ value, events }) => {
12148
- const { details } = failures.get(JSON.stringify([value.stage, value.reason]));
12149
- return {
12150
- ...value,
12151
- events,
12152
- details: mostFirst(details).map(({ value: detail, events }) => {
12153
- const [resource, httpStatus, originCode, transport] = JSON.parse(detail);
12154
- return {
12155
- resource,
12156
- httpStatus,
12157
- originCode,
12158
- transport,
12159
- events
12160
- };
12161
- })
12162
- };
12163
- }),
12164
- byExitReason: mostFirst(exits).map(({ value, events }) => ({
12165
- exitReason: value,
12166
- events
12167
- }))
12168
- };
12108
+ return mostFirst(failures).map(({ value, events }) => {
12109
+ const { details } = failures.get(JSON.stringify([value.stage, value.reason]));
12110
+ return {
12111
+ ...value,
12112
+ events,
12113
+ details: mostFirst(details).map(({ value: detail, events }) => {
12114
+ const [resource, httpStatus, originCode, transport] = JSON.parse(detail);
12115
+ return {
12116
+ resource,
12117
+ httpStatus,
12118
+ originCode,
12119
+ transport,
12120
+ events
12121
+ };
12122
+ })
12123
+ };
12124
+ });
12169
12125
  };
12170
12126
  /**
12171
12127
  * A release's or a channel's update failures. Since a release's first
12172
12128
  * report: its lifetime counters and failed installations. Over a time range
12173
- * of at most 30 days: failures, failed checks, the breakdown by stage,
12174
- * reason, and detail, and recoveries by exit reason, all summed from the
12175
- * breakdown's hourly rows; downloads and failed launches from the counters;
12176
- * failed installations from their sketches. A release reads hourly rows, a
12129
+ * of at most 30 days: failures, failed checks, and the breakdown by stage,
12130
+ * reason, and detail, all summed from the breakdown's hourly rows; downloads
12131
+ * from the counters; failed installations from their sketches. A release reads hourly rows, a
12177
12132
  * channel daily rows past 48 hours like its activity; the breakdown's rows
12178
12133
  * are hourly, so its coverage is the raw period's.
12179
12134
  */
@@ -12260,13 +12215,12 @@ const getUpdateFailures = async (db, input, now, retention) => {
12260
12215
  }))
12261
12216
  ]);
12262
12217
  const total = (field) => counters.reduce((sum, row) => sum + row[field], 0);
12263
- const scoped = releaseId === void 0 ? rows : rows.filter((row) => row.release_id === releaseId);
12218
+ const scoped = rows.filter((row) => FAILURE_STAGES.has(row.stage) && (releaseId === void 0 || row.release_id === releaseId));
12264
12219
  const failuresAt = (counted) => scoped.reduce((sum, row) => sum + (counted(row.stage) ? Math.max(0, row.events) : 0), 0);
12265
- const { breakdown, byExitReason } = breakdownOf(scoped);
12266
12220
  return {
12267
12221
  coverage: coverageOf(timeRange.start, kept.hour),
12268
12222
  measuredAtMs: at,
12269
- failedUpdates: failuresAt((stage) => stage !== "check" && stage !== "launch"),
12223
+ failedUpdates: failuresAt((stage) => stage !== "check"),
12270
12224
  failedInstallations: failed(failures),
12271
12225
  downloads: total("downloads"),
12272
12226
  patchDownloads: total("patch_downloads"),
@@ -12276,11 +12230,7 @@ const getUpdateFailures = async (db, input, now, retention) => {
12276
12230
  failedInstallations: failed(checks),
12277
12231
  activeInstallations: countDistinct(mergeDistinct(active.map((row) => row.activity_users)))
12278
12232
  } } : {},
12279
- breakdown,
12280
- recoveries: {
12281
- failedLaunches: total("failed_launches"),
12282
- byExitReason
12283
- }
12233
+ breakdown: breakdownOf(scoped)
12284
12234
  };
12285
12235
  };
12286
12236
  //#endregion
@@ -12543,7 +12493,7 @@ const insights = (options = {}) => {
12543
12493
  const plugin = definePlugin({
12544
12494
  id: "insights",
12545
12495
  namespace: false,
12546
- schemaVersion: "1.2.0",
12496
+ schemaVersion: "1.0.0",
12547
12497
  schema: createInsightsSchema(retention),
12548
12498
  init: ({ db, now }) => {
12549
12499
  const api = createInsightsApi(db, now, retention);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hot-updater/aws",
3
3
  "type": "module",
4
- "version": "1.0.0-rc.32",
4
+ "version": "1.0.0-rc.34",
5
5
  "description": "React Native OTA solution for self-hosted",
6
6
  "main": "dist/index.cjs",
7
7
  "module": "dist/index.mjs",
@@ -45,9 +45,9 @@
45
45
  "es-toolkit": "^1.32.0",
46
46
  "execa": "9.5.2",
47
47
  "mime": "^4.0.4",
48
- "@hot-updater/protocol": "1.0.0-rc.29",
49
- "@hot-updater/mock": "1.0.0-rc.29",
50
- "@hot-updater/test-utils": "1.0.0-rc.30"
48
+ "@hot-updater/protocol": "1.0.0-rc.30",
49
+ "@hot-updater/mock": "1.0.0-rc.30",
50
+ "@hot-updater/test-utils": "1.0.0-rc.32"
51
51
  },
52
52
  "dependencies": {
53
53
  "@aws-sdk/client-cloudfront": "3.1066.0",
@@ -62,9 +62,9 @@
62
62
  "@aws-sdk/lib-storage": "3.1066.0",
63
63
  "@aws-sdk/lib-dynamodb": "3.1066.0",
64
64
  "hono": "4.12.34",
65
- "@hot-updater/cli-tools": "1.0.0-rc.32",
66
- "@hot-updater/plugin-core": "1.0.0-rc.29",
67
- "@hot-updater/server": "1.0.0-rc.32"
65
+ "@hot-updater/cli-tools": "1.0.0-rc.34",
66
+ "@hot-updater/plugin-core": "1.0.0-rc.30",
67
+ "@hot-updater/server": "1.0.0-rc.34"
68
68
  },
69
69
  "scripts": {
70
70
  "build": "tsdown",