@hot-updater/firebase 1.0.0-rc.33 → 1.0.0-rc.35

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.
@@ -4524,7 +4524,7 @@ const createKvAdapter = ({ store, tablePrefix = "" }) => {
4524
4524
  };
4525
4525
  //#endregion
4526
4526
  //#region ../../packages/server/dist/version.mjs
4527
- const HOT_UPDATER_SERVER_VERSION = "1.0.0-rc.33";
4527
+ const HOT_UPDATER_SERVER_VERSION = "1.0.0-rc.35";
4528
4528
  //#endregion
4529
4529
  //#region ../../packages/server/dist/handlerVersionRoutes.mjs
4530
4530
  /**
@@ -7686,8 +7686,8 @@ const isOptional = (value, key, valid) => !Object.hasOwn(value, key) || valid(va
7686
7686
  const isString = (value) => typeof value === "string";
7687
7687
  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);
7688
7688
  /** An `UPDATE_FAILED` report's stored failure: a stage and a reason, and what else the client knew. */
7689
- 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);
7690
- 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);
7689
+ 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);
7690
+ 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");
7691
7691
  const isRecord$2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
7692
7692
  const isIdentity$1 = (value) => typeof value === "string" && value.length > 0 && value.length <= 255;
7693
7693
  const isTextOrNull = (value) => value === null || typeof value === "string";
@@ -8027,7 +8027,7 @@ const DELIVERIES = [
8027
8027
  "manifest",
8028
8028
  "archive"
8029
8029
  ];
8030
- /** A storage error code or an Android exit reason: letters, digits, and `._-`. */
8030
+ /** A storage error code: letters, digits, and `._-`. */
8031
8031
  const CODE = /^[A-Za-z0-9._-]{1,64}$/;
8032
8032
  const readCode = (value) => typeof value === "string" && CODE.test(value) ? value : void 0;
8033
8033
  const readMetadata = (payload) => isRecord$1(payload.metadata) ? payload.metadata : {};
@@ -8043,7 +8043,6 @@ function readFailure(value) {
8043
8043
  const originCode = readCode(failure.originCode);
8044
8044
  const errorMessage = typeof failure.errorMessage === "string" && failure.errorMessage.length <= 2048 ? failure.errorMessage : void 0;
8045
8045
  const errorStack = typeof failure.errorStack === "string" && failure.errorStack.length <= 4096 ? failure.errorStack : void 0;
8046
- const previousProcessExit = readCode(failure.previousProcessExit);
8047
8046
  return {
8048
8047
  stage: oneOf(STAGES, failure.stage),
8049
8048
  reason: oneOf(REASONS, failure.reason),
@@ -8052,8 +8051,7 @@ function readFailure(value) {
8052
8051
  ...failure.transport == null ? {} : { transport: oneOf(TRANSPORTS, failure.transport) },
8053
8052
  ...originCode === void 0 ? {} : { originCode },
8054
8053
  ...errorMessage === void 0 ? {} : { errorMessage },
8055
- ...errorStack === void 0 ? {} : { errorStack },
8056
- ...previousProcessExit === void 0 ? {} : { previousProcessExit }
8054
+ ...errorStack === void 0 ? {} : { errorStack }
8057
8055
  };
8058
8056
  }
8059
8057
  function readHttpResponse(value) {
@@ -8143,21 +8141,11 @@ function requireEvent(payload) {
8143
8141
  ...Object.keys(read).length === 0 ? {} : { metadata: read }
8144
8142
  };
8145
8143
  }
8146
- case "UPDATE_APPLIED": return {
8144
+ case "UPDATE_APPLIED":
8145
+ case "RECOVERED": return {
8147
8146
  ...movement(),
8148
8147
  type
8149
8148
  };
8150
- case "RECOVERED": {
8151
- const previousProcessExit = readCode(metadata.previousProcessExit);
8152
- return {
8153
- ...movement(),
8154
- type,
8155
- ...previousProcessExit === void 0 ? {} : { metadata: {
8156
- ...httpMetadata,
8157
- previousProcessExit
8158
- } }
8159
- };
8160
- }
8161
8149
  case "UPDATE_FAILED": return {
8162
8150
  ...movement(),
8163
8151
  type,
@@ -8223,24 +8211,13 @@ function createBundleEventRow(input) {
8223
8211
  }
8224
8212
  };
8225
8213
  }
8226
- case "UPDATE_APPLIED": return {
8214
+ case "UPDATE_APPLIED":
8215
+ case "RECOVERED": return {
8227
8216
  ...base,
8228
8217
  from_bundle_id: input.fromBundleId,
8229
8218
  type: input.type,
8230
8219
  metadata
8231
8220
  };
8232
- case "RECOVERED": {
8233
- const exit = input.metadata?.previousProcessExit;
8234
- return {
8235
- ...base,
8236
- from_bundle_id: input.fromBundleId,
8237
- type: input.type,
8238
- metadata: {
8239
- ...metadata,
8240
- ...exit === void 0 ? {} : { previous_process_exit: exit }
8241
- }
8242
- };
8243
- }
8244
8221
  case "UPDATE_FAILED": {
8245
8222
  const { failure } = input.metadata;
8246
8223
  return {
@@ -8257,8 +8234,7 @@ function createBundleEventRow(input) {
8257
8234
  ...failure.resource === void 0 ? {} : { resource: failure.resource },
8258
8235
  ...failure.httpStatus === void 0 ? {} : { http_status: failure.httpStatus },
8259
8236
  ...failure.transport === void 0 ? {} : { transport: failure.transport },
8260
- ...failure.originCode === void 0 ? {} : { origin_code: failure.originCode },
8261
- ...failure.previousProcessExit === void 0 ? {} : { previous_process_exit: failure.previousProcessExit }
8237
+ ...failure.originCode === void 0 ? {} : { origin_code: failure.originCode }
8262
8238
  }
8263
8239
  }
8264
8240
  };
@@ -8422,10 +8398,9 @@ const toFailure = (failure) => ({
8422
8398
  ...failure.resource === void 0 ? {} : { resource: failure.resource },
8423
8399
  ...failure.http_status === void 0 ? {} : { httpStatus: failure.http_status },
8424
8400
  ...failure.transport === void 0 ? {} : { transport: failure.transport },
8425
- ...failure.origin_code === void 0 ? {} : { originCode: failure.origin_code },
8426
- ...failure.previous_process_exit === void 0 ? {} : { previousProcessExit: failure.previous_process_exit }
8401
+ ...failure.origin_code === void 0 ? {} : { originCode: failure.origin_code }
8427
8402
  });
8428
- /** What a row's metadata adds for its type: a failure, a delivery, an exit reason. */
8403
+ /** What a row's metadata adds for its type: a failure or a delivery. */
8429
8404
  const eventDetails = ({ type, metadata }) => ({
8430
8405
  ...metadata.http_response !== void 0 ? { httpResponse: {
8431
8406
  resource: metadata.http_response.resource,
@@ -8437,8 +8412,7 @@ const eventDetails = ({ type, metadata }) => ({
8437
8412
  } } : {},
8438
8413
  ...type === "UPDATE_FAILED" && metadata.failure !== void 0 ? { failure: toFailure(metadata.failure) } : {},
8439
8414
  ...type === "UPDATE_DOWNLOADED" && metadata.delivery !== void 0 ? { delivery: metadata.delivery } : {},
8440
- ...type === "UPDATE_DOWNLOADED" && metadata.patch_fallback === true ? { patchFallback: true } : {},
8441
- ...type === "RECOVERED" && metadata.previous_process_exit !== void 0 ? { previousProcessExit: metadata.previous_process_exit } : {}
8415
+ ...type === "UPDATE_DOWNLOADED" && metadata.patch_fallback === true ? { patchFallback: true } : {}
8442
8416
  });
8443
8417
  const toEventHistoryRow = (row) => ({
8444
8418
  ...row.to_release_id == null ? {} : { toReleaseId: row.to_release_id },
@@ -8920,14 +8894,12 @@ const lifetimeSketches = () => defineAggregate(identityFields, {
8920
8894
  indexes: { window }
8921
8895
  });
8922
8896
  /**
8923
- * Update failures by what failed, and recoveries by why the crashed process
8924
- * exited, a row an hour. `release_id` is the failed update's target release,
8925
- * or empty for a failed check or an unknown target, and the recovered
8926
- * release for a recovery. A failure's `stage` and `reason` say where and why
8927
- * it failed, and `detail` what else the client knew, as the JSON array
8928
- * `[resource, httpStatus, originCode, transport]`. A recovery's stage is
8929
- * `launch` and its reason the exit reason, with an empty detail. Every index
8930
- * is in key order, so its counters stay blind increments.
8897
+ * Update failures by what failed, a row an hour. `release_id` is the failed
8898
+ * update's target release, or empty for a failed check or an unknown target.
8899
+ * `stage` and `reason` say where and why it failed, and `detail` what else
8900
+ * the client knew, as the JSON array `[resource, httpStatus, originCode,
8901
+ * transport]`. Every index is in key order, so its counters stay blind
8902
+ * increments.
8931
8903
  */
8932
8904
  const insightsFailures = (retention) => defineAggregate({
8933
8905
  platform: {
@@ -9470,23 +9442,6 @@ const countFailure = (tx, event) => {
9470
9442
  detail: failureDetail(failure)
9471
9443
  }, { events: 1 }, { shardBy });
9472
9444
  };
9473
- /**
9474
- * A recovery's exit reason, which Android 11+ reports, in the breakdown of
9475
- * the release whose launch failed: stage `launch`, the reason as its reason.
9476
- */
9477
- const countExit = (tx, event) => {
9478
- const exit = event.metadata.previous_process_exit;
9479
- if (event.type !== "RECOVERED" || exit === void 0) return;
9480
- tx.aggregate("insights_failures", {
9481
- platform: event.platform,
9482
- channel: event.channel,
9483
- bucket_start_ms: hourOf(event.received_at_ms),
9484
- release_id: event.from_release_id ?? "",
9485
- stage: "launch",
9486
- reason: exit,
9487
- detail: ""
9488
- }, { events: 1 }, { shardBy: event.install_id });
9489
- };
9490
9445
  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;
9491
9446
  /** What an UNCHANGED report must share with its installation's head to repeat it. */
9492
9447
  const REPEATED_FIELDS = [
@@ -9531,7 +9486,6 @@ const recordEvent = (db, event) => {
9531
9486
  return;
9532
9487
  }
9533
9488
  countEvent(tx, event);
9534
- countExit(tx, event);
9535
9489
  if (previous !== null && !isNewer(event, previous)) return;
9536
9490
  if (previous === null) tx.create("bundle_event_heads", event);
9537
9491
  else {
@@ -10015,21 +9969,18 @@ const isText = (value, maxLength) => typeof value === "string" && value.length >
10015
9969
  const failed = (...rows) => countDistinct(mergeDistinct(rows.flat().map((row) => row.failed_users)));
10016
9970
  /** Entries of a count map, most first, then in key order. */
10017
9971
  const mostFirst = (counts) => [...counts].sort(([leftKey, left], [rightKey, right]) => right.events - left.events || compareUtf8(leftKey, rightKey)).map(([, entry]) => entry);
10018
- /** Failure rows as their breakdown, and recovery rows as exit reasons. */
9972
+ /** The stages a failure row records: where an update failed. */
9973
+ const FAILURE_STAGES = /* @__PURE__ */ new Set([
9974
+ "check",
9975
+ "download",
9976
+ "install",
9977
+ "unknown"
9978
+ ]);
9979
+ /** Failure rows as their breakdown by stage, reason, and detail. */
10019
9980
  const breakdownOf = (rows) => {
10020
9981
  const failures = /* @__PURE__ */ new Map();
10021
- const exits = /* @__PURE__ */ new Map();
10022
9982
  for (const { stage, reason, detail, events } of rows) {
10023
9983
  if (events <= 0) continue;
10024
- if (stage === "launch") {
10025
- const exit = exits.get(reason) ?? {
10026
- value: reason,
10027
- events: 0
10028
- };
10029
- exit.events += events;
10030
- exits.set(reason, exit);
10031
- continue;
10032
- }
10033
9984
  const key = JSON.stringify([stage, reason]);
10034
9985
  const entry = failures.get(key) ?? {
10035
9986
  value: {
@@ -10048,37 +9999,30 @@ const breakdownOf = (rows) => {
10048
9999
  entry.details.set(detail, known);
10049
10000
  failures.set(key, entry);
10050
10001
  }
10051
- return {
10052
- breakdown: mostFirst(failures).map(({ value, events }) => {
10053
- const { details } = failures.get(JSON.stringify([value.stage, value.reason]));
10054
- return {
10055
- ...value,
10056
- events,
10057
- details: mostFirst(details).map(({ value: detail, events }) => {
10058
- const [resource, httpStatus, originCode, transport] = JSON.parse(detail);
10059
- return {
10060
- resource,
10061
- httpStatus,
10062
- originCode,
10063
- transport,
10064
- events
10065
- };
10066
- })
10067
- };
10068
- }),
10069
- byExitReason: mostFirst(exits).map(({ value, events }) => ({
10070
- exitReason: value,
10071
- events
10072
- }))
10073
- };
10002
+ return mostFirst(failures).map(({ value, events }) => {
10003
+ const { details } = failures.get(JSON.stringify([value.stage, value.reason]));
10004
+ return {
10005
+ ...value,
10006
+ events,
10007
+ details: mostFirst(details).map(({ value: detail, events }) => {
10008
+ const [resource, httpStatus, originCode, transport] = JSON.parse(detail);
10009
+ return {
10010
+ resource,
10011
+ httpStatus,
10012
+ originCode,
10013
+ transport,
10014
+ events
10015
+ };
10016
+ })
10017
+ };
10018
+ });
10074
10019
  };
10075
10020
  /**
10076
10021
  * A release's or a channel's update failures. Since a release's first
10077
10022
  * report: its lifetime counters and failed installations. Over a time range
10078
- * of at most 30 days: failures, failed checks, the breakdown by stage,
10079
- * reason, and detail, and recoveries by exit reason, all summed from the
10080
- * breakdown's hourly rows; downloads and failed launches from the counters;
10081
- * failed installations from their sketches. A release reads hourly rows, a
10023
+ * of at most 30 days: failures, failed checks, and the breakdown by stage,
10024
+ * reason, and detail, all summed from the breakdown's hourly rows; downloads
10025
+ * from the counters; failed installations from their sketches. A release reads hourly rows, a
10082
10026
  * channel daily rows past 48 hours like its activity; the breakdown's rows
10083
10027
  * are hourly, so its coverage is the raw period's.
10084
10028
  */
@@ -10165,13 +10109,12 @@ const getUpdateFailures = async (db, input, now, retention) => {
10165
10109
  }))
10166
10110
  ]);
10167
10111
  const total = (field) => counters.reduce((sum, row) => sum + row[field], 0);
10168
- const scoped = releaseId === void 0 ? rows : rows.filter((row) => row.release_id === releaseId);
10112
+ const scoped = rows.filter((row) => FAILURE_STAGES.has(row.stage) && (releaseId === void 0 || row.release_id === releaseId));
10169
10113
  const failuresAt = (counted) => scoped.reduce((sum, row) => sum + (counted(row.stage) ? Math.max(0, row.events) : 0), 0);
10170
- const { breakdown, byExitReason } = breakdownOf(scoped);
10171
10114
  return {
10172
10115
  coverage: coverageOf(timeRange.start, kept.hour),
10173
10116
  measuredAtMs: at,
10174
- failedUpdates: failuresAt((stage) => stage !== "check" && stage !== "launch"),
10117
+ failedUpdates: failuresAt((stage) => stage !== "check"),
10175
10118
  failedInstallations: failed(failures),
10176
10119
  downloads: total("downloads"),
10177
10120
  patchDownloads: total("patch_downloads"),
@@ -10181,11 +10124,7 @@ const getUpdateFailures = async (db, input, now, retention) => {
10181
10124
  failedInstallations: failed(checks),
10182
10125
  activeInstallations: countDistinct(mergeDistinct(active.map((row) => row.activity_users)))
10183
10126
  } } : {},
10184
- breakdown,
10185
- recoveries: {
10186
- failedLaunches: total("failed_launches"),
10187
- byExitReason
10188
- }
10127
+ breakdown: breakdownOf(scoped)
10189
10128
  };
10190
10129
  };
10191
10130
  //#endregion
@@ -10448,7 +10387,7 @@ const insights = (options = {}) => {
10448
10387
  const plugin = definePlugin({
10449
10388
  id: "insights",
10450
10389
  namespace: false,
10451
- schemaVersion: "1.4.0",
10390
+ schemaVersion: "1.0.0",
10452
10391
  schema: createInsightsSchema(retention),
10453
10392
  init: ({ db, now }) => {
10454
10393
  const api = createInsightsApi(db, now, retention);
package/dist/index.d.cts CHANGED
@@ -53,8 +53,6 @@ type BundleEventFailure = DatabaseJsonObject & {
53
53
  readonly origin_code?: string;
54
54
  readonly error_message?: string;
55
55
  readonly error_stack?: string;
56
- /** Android's `ApplicationExitInfo` reason for the process before this one. */
57
- readonly previous_process_exit?: string;
58
56
  };
59
57
  type DatabaseHttpResponse = DatabaseJsonObject & {
60
58
  readonly resource: "catalog" | "artifact";
@@ -77,8 +75,6 @@ type DatabaseBundleEventMetadata = DatabaseJsonObject & {
77
75
  readonly delivery?: "patch" | "manifest" | "archive" | "unknown";
78
76
  /** `UPDATE_DOWNLOADED`: a patch failed and the full files came instead. */
79
77
  readonly patch_fallback?: boolean;
80
- /** `RECOVERED`: Android's `ApplicationExitInfo` reason for the crashed process. */
81
- readonly previous_process_exit?: string;
82
78
  };
83
79
  type BundleEventRowBase = {
84
80
  readonly id: string;
@@ -307,17 +303,6 @@ interface InsightsUpdateFailures {
307
303
  };
308
304
  /** Over a time range: failures by stage and reason, most first. */
309
305
  readonly breakdown?: readonly InsightsFailureBreakdown[];
310
- /**
311
- * Over a time range: failed launches, and the recoveries whose crashed
312
- * process exited for a reason Android 11+ reported, by reason, most first.
313
- */
314
- readonly recoveries?: {
315
- readonly failedLaunches: number;
316
- readonly byExitReason: readonly {
317
- readonly exitReason: string;
318
- readonly events: number;
319
- }[];
320
- };
321
306
  }
322
307
  //#endregion
323
308
  //#region ../api-keys/dist/server/model.d.mts
package/dist/index.d.mts CHANGED
@@ -53,8 +53,6 @@ type BundleEventFailure = DatabaseJsonObject & {
53
53
  readonly origin_code?: string;
54
54
  readonly error_message?: string;
55
55
  readonly error_stack?: string;
56
- /** Android's `ApplicationExitInfo` reason for the process before this one. */
57
- readonly previous_process_exit?: string;
58
56
  };
59
57
  type DatabaseHttpResponse = DatabaseJsonObject & {
60
58
  readonly resource: "catalog" | "artifact";
@@ -77,8 +75,6 @@ type DatabaseBundleEventMetadata = DatabaseJsonObject & {
77
75
  readonly delivery?: "patch" | "manifest" | "archive" | "unknown";
78
76
  /** `UPDATE_DOWNLOADED`: a patch failed and the full files came instead. */
79
77
  readonly patch_fallback?: boolean;
80
- /** `RECOVERED`: Android's `ApplicationExitInfo` reason for the crashed process. */
81
- readonly previous_process_exit?: string;
82
78
  };
83
79
  type BundleEventRowBase = {
84
80
  readonly id: string;
@@ -307,17 +303,6 @@ interface InsightsUpdateFailures {
307
303
  };
308
304
  /** Over a time range: failures by stage and reason, most first. */
309
305
  readonly breakdown?: readonly InsightsFailureBreakdown[];
310
- /**
311
- * Over a time range: failed launches, and the recoveries whose crashed
312
- * process exited for a reason Android 11+ reported, by reason, most first.
313
- */
314
- readonly recoveries?: {
315
- readonly failedLaunches: number;
316
- readonly byExitReason: readonly {
317
- readonly exitReason: string;
318
- readonly events: number;
319
- }[];
320
- };
321
306
  }
322
307
  //#endregion
323
308
  //#region ../api-keys/dist/server/model.d.mts
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hot-updater/firebase",
3
3
  "type": "module",
4
- "version": "1.0.0-rc.33",
4
+ "version": "1.0.0-rc.35",
5
5
  "description": "React Native OTA solution for self-hosted",
6
6
  "main": "dist/index.cjs",
7
7
  "types": "dist/index.d.cts",
@@ -37,10 +37,10 @@
37
37
  ],
38
38
  "dependencies": {
39
39
  "hono": "4.12.34",
40
- "@hot-updater/cli-tools": "1.0.0-rc.33",
41
- "@hot-updater/protocol": "1.0.0-rc.29",
42
- "@hot-updater/plugin-core": "1.0.0-rc.29",
43
- "@hot-updater/server": "1.0.0-rc.33"
40
+ "@hot-updater/cli-tools": "1.0.0-rc.35",
41
+ "@hot-updater/plugin-core": "1.0.0-rc.35",
42
+ "@hot-updater/protocol": "1.0.0-rc.35",
43
+ "@hot-updater/server": "1.0.0-rc.35"
44
44
  },
45
45
  "publishConfig": {
46
46
  "access": "public"
@@ -55,8 +55,8 @@
55
55
  "firebase-tools": "15.29.0",
56
56
  "fkill": "^9.0.0",
57
57
  "mime": "^4.0.4",
58
- "@hot-updater/mock": "1.0.0-rc.29",
59
- "@hot-updater/test-utils": "1.0.0-rc.31"
58
+ "@hot-updater/mock": "1.0.0-rc.35",
59
+ "@hot-updater/test-utils": "1.0.0-rc.35"
60
60
  },
61
61
  "peerDependencies": {
62
62
  "firebase-admin": "*",