@hot-updater/firebase 1.0.0-rc.30 → 1.0.0-rc.31

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.30";
4527
+ const HOT_UPDATER_SERVER_VERSION = "1.0.0-rc.31";
4528
4528
  //#endregion
4529
4529
  //#region ../../packages/server/dist/handlerVersionRoutes.mjs
4530
4530
  /**
@@ -7876,42 +7876,23 @@ const createValidatedInsightsModel = (model) => ({
7876
7876
  ]) || !isBundleFilter(input.filter) || !isTimestamp(input.sinceMs) || !isTimestamp(input.beforeReceivedAtMs) || input.sinceMs > input.beforeReceivedAtMs) invalidQuery();
7877
7877
  return validateCount(await model.countEvents(input));
7878
7878
  },
7879
- async getReleaseActivity(input) {
7879
+ async countEventSeries(input) {
7880
7880
  if (!isRecord$2(input) || !hasOnlyKeys(input, [
7881
- "releases",
7882
- "scope",
7881
+ "filter",
7883
7882
  "timeRange",
7884
7883
  "intervalMs"
7885
- ])) invalidQuery();
7886
- const releases = input.releases;
7887
- const scope = input.scope;
7888
- const timeRange = input.timeRange;
7889
- const intervalMs = input.intervalMs;
7890
- const releaseQuery = Array.isArray(releases) && releases.length > 0 && releases.length <= 100 && releases.every(isReleaseReference) && scope === void 0 && (timeRange === void 0 ? intervalMs === void 0 : isTimeRange(timeRange) && (intervalMs === void 0 || isTimestamp(intervalMs) && intervalMs > 0 && intervalMs % 36e5 === 0 && (timeRange.end - timeRange.start) / intervalMs <= 8784));
7891
- const scopeQuery = releases === void 0 && intervalMs === void 0 && isRecord$2(scope) && hasOnlyKeys(scope, ["platform", "channel"]) && hasScope(scope) && isTimeRange(timeRange);
7892
- if (!releaseQuery && !scopeQuery) invalidQuery();
7893
- const result = await model.getReleaseActivity(input);
7894
- validateAggregateResult(result);
7895
- if (!Array.isArray(result.data)) invalidResult();
7896
- return result;
7884
+ ]) || !isBundleFilter(input.filter) || !isTimeRange(input.timeRange) || !isTimestamp(input.intervalMs)) invalidQuery();
7885
+ const { start, end } = input.timeRange;
7886
+ const { intervalMs } = input;
7887
+ if (intervalMs === 0 || intervalMs % 36e5 !== 0 || start % 36e5 !== 0 || (end - start) % intervalMs !== 0 || end - start > 7776e6) invalidQuery();
7888
+ const points = await model.countEventSeries(input);
7889
+ if (!Array.isArray(points) || points.length !== (end - start) / intervalMs || points.some((point, index) => !isRecord$2(point) || !hasOnlyKeys(point, ["startMs", "events"]) || point.startMs !== start + index * intervalMs || !isTimestamp(point.events))) invalidResult();
7890
+ return points;
7897
7891
  },
7898
- async getDistributionHistory(input) {
7899
- if (!isRecord$2(input) || !hasOnlyKeys(input, [
7900
- "channel",
7901
- "platform",
7902
- "timeRange"
7903
- ]) || !hasScope(input) || !isTimeRange(input.timeRange) || input.timeRange.start % 864e5 !== 0 || input.timeRange.end % 864e5 !== 0 || input.timeRange.end - input.timeRange.start > 26784e5) invalidQuery();
7904
- const result = await model.getDistributionHistory(input);
7905
- validateAggregateResult(result);
7906
- if (!Array.isArray(result.points)) invalidResult();
7907
- for (const point of result.points) {
7908
- if (!isTimestamp(point.startMs) || !Array.isArray(point.bundles)) invalidResult();
7909
- for (const bundle of point.bundles) if (!isText$1(bundle.appVersion) || bundle.releaseId !== null && !isText$1(bundle.releaseId) || ![
7910
- "release",
7911
- "builtin",
7912
- "unknown"
7913
- ].includes(bundle.bundleKind) || !isTimestamp(bundle.installations)) invalidResult();
7914
- }
7892
+ async getReleaseActivity(input) {
7893
+ if (!isRecord$2(input) || !hasOnlyKeys(input, ["releases"]) || !Array.isArray(input.releases) || input.releases.length < 1 || input.releases.length > 100 || !input.releases.every(isReleaseReference)) invalidQuery();
7894
+ const result = await model.getReleaseActivity(input);
7895
+ 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();
7915
7896
  return result;
7916
7897
  },
7917
7898
  async getAppUsage(input) {
@@ -7944,9 +7925,9 @@ const createInsightsModel = (api) => createValidatedInsightsModel({
7944
7925
  findLatestEvents: api.findLatestEvents,
7945
7926
  countLatestEvents: api.countLatestEvents,
7946
7927
  countEvents: api.countEvents,
7928
+ countEventSeries: api.countEventSeries,
7947
7929
  getReleaseActivity: api.getReleaseActivity,
7948
- getAppUsage: api.getAppUsage,
7949
- getDistributionHistory: api.getDistributionHistory
7930
+ getAppUsage: api.getAppUsage
7950
7931
  });
7951
7932
  //#endregion
7952
7933
  //#region ../insights/dist/server/official.mjs
@@ -8766,14 +8747,6 @@ const bundleEvents = (days) => defineTable({
8766
8747
  }
8767
8748
  },
8768
8749
  indexes: {
8769
- recent: {
8770
- eq: [
8771
- "channel",
8772
- "platform",
8773
- "day"
8774
- ],
8775
- sort: ["received_at_ms"]
8776
- },
8777
8750
  movementsByInstall: {
8778
8751
  eq: ["movement_install_id"],
8779
8752
  sort: ["received_at_ms"]
@@ -8890,10 +8863,11 @@ const window = {
8890
8863
  };
8891
8864
  /**
8892
8865
  * Counters of one period kind: hourly, daily, or lifetime at bucket 0. A
8893
- * release or channel row counts downloads, launches, and failed launches,
8894
- * and downloads a patch delivered and those that fell back from one. A
8895
- * release's lifetime row counts its failed updates (`failed_updates`), since
8896
- * no breakdown row outlives the raw period; windowed reads sum
8866
+ * release or channel row counts downloads and failed launches, and downloads
8867
+ * a patch delivered and those that fell back from one. Only a release's
8868
+ * lifetime row counts launches, which the bundle list reads and no windowed
8869
+ * read sums. It also counts the release's failed updates (`failed_updates`),
8870
+ * since no breakdown row outlives the raw period; windowed reads sum
8897
8871
  * `insights_failures` instead.
8898
8872
  */
8899
8873
  const counters = (retention) => defineAggregate(identityFields, {
@@ -8912,17 +8886,13 @@ const counters = (retention) => defineAggregate(identityFields, {
8912
8886
  ...retention === void 0 ? {} : { retention }
8913
8887
  });
8914
8888
  /**
8915
- * Unique-installation sketches of one period kind. Rows of a failure
8916
- * identity (`failure` and `check` scopes) keep only `failed_users`, so a
8917
- * release's launch rows never grow by a failure's registers.
8889
+ * Unique-installation sketches of one period kind: a usage row's active
8890
+ * installations (`activity_users`), and a `failure` or `check` row's
8891
+ * installations whose update or update check failed (`failed_users`).
8918
8892
  */
8919
8893
  const sketches = (retention) => defineAggregate(identityFields, {
8920
8894
  key: ["identity", "bucket_start_ms"],
8921
- distinct: [
8922
- "launch_users",
8923
- "activity_users",
8924
- "failed_users"
8925
- ],
8895
+ distinct: ["activity_users", "failed_users"],
8926
8896
  shards: SKETCH_SHARDS,
8927
8897
  batched: true,
8928
8898
  indexes: { window },
@@ -9028,71 +8998,6 @@ const insightsDistribution = (retention) => defineAggregate({
9028
8998
  },
9029
8999
  retention
9030
9000
  });
9031
- /** The last running bundle observed per installation and UTC day. */
9032
- const bundleDailyHeads = (retention) => defineTable({
9033
- install_id: {
9034
- type: "string",
9035
- maxLength: 255
9036
- },
9037
- bucket_start_ms: { type: "integer" },
9038
- id: {
9039
- type: "string",
9040
- maxLength: 36
9041
- },
9042
- received_at_ms: { type: "integer" },
9043
- channel: { type: "string" },
9044
- platform: {
9045
- type: "string",
9046
- maxLength: 16
9047
- },
9048
- app_version: { type: "string" },
9049
- release_id: {
9050
- type: "string",
9051
- maxLength: 36
9052
- },
9053
- bundle_kind: {
9054
- type: "string",
9055
- maxLength: 16
9056
- }
9057
- }, {
9058
- key: ["install_id", "bucket_start_ms"],
9059
- retention
9060
- });
9061
- /** Daily observations stay in their day when an installation reports again. */
9062
- const insightsDistributionHistory = (retention) => defineAggregate({
9063
- channel: { type: "string" },
9064
- platform: {
9065
- type: "string",
9066
- maxLength: 16
9067
- },
9068
- app_version: { type: "string" },
9069
- release_id: {
9070
- type: "string",
9071
- maxLength: 36
9072
- },
9073
- bundle_kind: {
9074
- type: "string",
9075
- maxLength: 16
9076
- },
9077
- bucket_start_ms: { type: "integer" }
9078
- }, {
9079
- key: [
9080
- "channel",
9081
- "platform",
9082
- "app_version",
9083
- "release_id",
9084
- "bundle_kind",
9085
- "bucket_start_ms"
9086
- ],
9087
- gauges: ["installations"],
9088
- shards: GAUGE_SHARDS,
9089
- batched: true,
9090
- indexes: { byScope: {
9091
- eq: ["channel", "platform"],
9092
- sort: ["bucket_start_ms"]
9093
- } },
9094
- retention
9095
- });
9096
9001
  /** Latest events by the bundle they came from or went to, bucketed like the distribution. */
9097
9002
  const insightsLatestByBundle = (retention) => defineAggregate({
9098
9003
  platform: {
@@ -9212,8 +9117,6 @@ const createInsightsSchema = ({ rawDays, dailyDays } = DEFAULT_INSIGHTS_RETENTIO
9212
9117
  insights_overview_lifetime: counters(),
9213
9118
  insights_sketches_lifetime: lifetimeSketches(),
9214
9119
  insights_distribution: insightsDistribution(daily),
9215
- bundle_daily_heads: bundleDailyHeads(daily),
9216
- insights_distribution_history: insightsDistributionHistory(daily),
9217
9120
  insights_latest_by_bundle: insightsLatestByBundle(daily),
9218
9121
  insights_outcomes: insightsOutcomes(hourly),
9219
9122
  insights_failures: insightsFailures(hourly)
@@ -9256,12 +9159,14 @@ const insightsOverviewDeltas = (event) => {
9256
9159
  downloads: (previous?.downloads ?? 0) + values.downloads,
9257
9160
  launches: (previous?.launches ?? 0) + values.launches,
9258
9161
  failedLaunches: (previous?.failedLaunches ?? 0) + values.failedLaunches,
9259
- launchIdentity: previous?.launchIdentity ?? values.launchIdentity,
9260
9162
  activityIdentity: previous?.activityIdentity ?? values.activityIdentity
9261
9163
  });
9262
9164
  };
9263
9165
  const metric = (releaseId, values) => {
9264
- const launchIdentity = values.launches > 0 ? event.install_id : void 0;
9166
+ const windowed = {
9167
+ ...values,
9168
+ launches: 0
9169
+ };
9265
9170
  if (releaseId !== null) for (const [periodKind, start] of [["lifetime", 0], ["hour", bucketStartMs]]) append({
9266
9171
  scopeKind: "release",
9267
9172
  releaseKind: "specific",
@@ -9272,10 +9177,7 @@ const insightsOverviewDeltas = (event) => {
9272
9177
  appVersion: "",
9273
9178
  periodKind,
9274
9179
  bucketStartMs: start
9275
- }, {
9276
- ...values,
9277
- ...periodKind === "hour" ? { launchIdentity } : {}
9278
- });
9180
+ }, periodKind === "lifetime" ? values : windowed);
9279
9181
  append({
9280
9182
  scopeKind: "channel",
9281
9183
  releaseKind: "all",
@@ -9286,10 +9188,7 @@ const insightsOverviewDeltas = (event) => {
9286
9188
  appVersion: "",
9287
9189
  periodKind: "hour",
9288
9190
  bucketStartMs
9289
- }, {
9290
- ...values,
9291
- launchIdentity
9292
- });
9191
+ }, windowed);
9293
9192
  };
9294
9193
  if (event.type === "UPDATE_DOWNLOADED") metric(event.to_release_id, {
9295
9194
  downloads: 1,
@@ -9414,11 +9313,10 @@ const patchCounters = (event) => event.type !== "UPDATE_DOWNLOADED" ? {} : {
9414
9313
  };
9415
9314
  /**
9416
9315
  * Counters and sketches for one event: release, channel, and usage rows, with
9417
- * day rollups for channel and usage. Usage rows are written for the event's
9418
- * platform only, since a read for every platform merges the ios and android
9419
- * sketches; a channel's active installations come from its usage rows, so
9420
- * channel rows keep no sketch of their own. A download's patch counters go
9421
- * in the rows that count it.
9316
+ * day rollups for channel and usage. Only usage rows keep a sketch, of active
9317
+ * installations, and only for the event's platform, since a read for every
9318
+ * platform merges the ios and android sketches. A download's patch counters
9319
+ * go in the rows that count it.
9422
9320
  */
9423
9321
  const countEvent = (tx, event) => {
9424
9322
  const shardBy = event.install_id;
@@ -9444,11 +9342,7 @@ const countEvent = (tx, event) => {
9444
9342
  ...delta.downloads === 0 ? {} : patchCounters(event)
9445
9343
  };
9446
9344
  if (Object.keys(counters).length > 0) tx.aggregate(models.counters, key, counters, { shardBy });
9447
- const sketches = {
9448
- ...delta.launchIdentity === void 0 || parts.scopeKind === "channel" ? {} : { launch_users: addDistinct(null, delta.launchIdentity) },
9449
- ...delta.activityIdentity === void 0 ? {} : { activity_users: addDistinct(null, delta.activityIdentity) }
9450
- };
9451
- if (models.sketches !== void 0 && Object.keys(sketches).length > 0) tx.aggregate(models.sketches, key, sketches, { shardBy });
9345
+ if (models.sketches !== void 0 && delta.activityIdentity !== void 0) tx.aggregate(models.sketches, key, { activity_users: addDistinct(null, delta.activityIdentity) }, { shardBy });
9452
9346
  }
9453
9347
  }
9454
9348
  };
@@ -9601,66 +9495,21 @@ const REPEATED_FIELDS = [
9601
9495
  const repeatsHead = (event, head) => event.type === "UNCHANGED" && head.type !== "UPDATE_DOWNLOADED" && dayOf(event.received_at_ms) === dayOf(head.received_at_ms) && REPEATED_FIELDS.every((field) => event[field] === head[field]);
9602
9496
  /**
9603
9497
  * Records one event in one transaction: one batch read of the event and its
9604
- * installation's latest and daily heads, then the gauge and sketch rows it
9605
- * changes and one
9498
+ * installation's head, one of the gauge and sketch rows it changes, then one
9606
9499
  * write. On a database that batches aggregates (DynamoDB, Firestore), the
9607
9500
  * write holds the event's rows and one log row instead, and a compaction
9608
9501
  * reads and writes the aggregate rows. A stored event's id changes nothing
9609
9502
  * when repeated, whichever installation sends it, and neither does the id
9610
9503
  * of the installation's head. An UNCHANGED report that repeats its head on
9611
- * the same UTC day only advances its daily observation. An older event counts in its
9504
+ * the same UTC day writes nothing at all. An older event still counts in its
9612
9505
  * own hour but never replaces the head. An update failure is stored and
9613
9506
  * counted, but changes what no installation runs, so it moves no head.
9614
9507
  */
9615
9508
  const recordEvent = (db, event) => {
9616
9509
  assertBundleEventRow(event);
9617
9510
  return db.transaction(async (tx) => {
9618
- const dailyKey = {
9619
- install_id: event.install_id,
9620
- bucket_start_ms: dayOf(event.received_at_ms)
9621
- };
9622
- const [existing, previous, daily] = await Promise.all([
9623
- tx.findOne("bundle_events", { id: event.id }),
9624
- tx.findOne("bundle_event_heads", { install_id: event.install_id }),
9625
- event.type === "UPDATE_FAILED" ? null : tx.findOne("bundle_daily_heads", dailyKey)
9626
- ]);
9627
- if (existing !== null || previous?.id === event.id || daily?.id === event.id) return;
9628
- if (event.type !== "UPDATE_FAILED" && (daily === null || isNewer(event, daily))) {
9629
- const downloaded = event.type === "UPDATE_DOWNLOADED";
9630
- const releaseId = downloaded ? event.from_release_id : event.to_release_id;
9631
- const bundleId = downloaded ? event.from_bundle_id : event.to_bundle_id;
9632
- const next = {
9633
- ...dailyKey,
9634
- id: event.id,
9635
- received_at_ms: event.received_at_ms,
9636
- channel: event.channel,
9637
- platform: event.platform,
9638
- app_version: event.app_version,
9639
- release_id: releaseId ?? "",
9640
- bundle_kind: bundleId === "00000000-0000-0000-0000-000000000000" ? "builtin" : releaseId === null ? "unknown" : "release"
9641
- };
9642
- const count = (head, delta) => {
9643
- tx.aggregate("insights_distribution_history", {
9644
- channel: head.channel,
9645
- platform: head.platform,
9646
- app_version: head.app_version,
9647
- release_id: head.release_id,
9648
- bundle_kind: head.bundle_kind,
9649
- bucket_start_ms: head.bucket_start_ms
9650
- }, { installations: delta }, { shardBy: head.install_id });
9651
- };
9652
- if (daily !== null) {
9653
- if (daily.channel !== next.channel || daily.platform !== next.platform || daily.app_version !== next.app_version || daily.release_id !== next.release_id || daily.bundle_kind !== next.bundle_kind) {
9654
- count(daily, -1);
9655
- count(next, 1);
9656
- }
9657
- const { install_id: _, bucket_start_ms: __, ...fields } = next;
9658
- tx.update("bundle_daily_heads", daily, fields);
9659
- } else {
9660
- tx.create("bundle_daily_heads", next);
9661
- count(next, 1);
9662
- }
9663
- }
9511
+ const [existing, previous] = await Promise.all([tx.findOne("bundle_events", { id: event.id }), tx.findOne("bundle_event_heads", { install_id: event.install_id })]);
9512
+ if (existing !== null || previous?.id === event.id) return;
9664
9513
  if (previous !== null && repeatsHead(event, previous)) return;
9665
9514
  if (event.type !== "UNCHANGED") {
9666
9515
  tx.create("bundle_events", event);
@@ -9852,6 +9701,30 @@ const countEvents = async (db, input) => {
9852
9701
  }));
9853
9702
  return await countRaw(db, filter, sinceMs, start) + hours.reduce((sum, row) => sum + row.events, 0) + await countRaw(db, filter, end, beforeReceivedAtMs);
9854
9703
  };
9704
+ /**
9705
+ * A bundle filter's stored events in each interval of whole hours: one read
9706
+ * of its hourly counts, every interval present.
9707
+ */
9708
+ const countEventSeries = async (db, input) => {
9709
+ const { filter, intervalMs } = input;
9710
+ const { start, end } = input.timeRange;
9711
+ const events = Array.from({ length: (end - start) / intervalMs }, () => 0);
9712
+ const hours = await drain((page) => db.findAggregates("insights_outcomes", {
9713
+ index: "byRef",
9714
+ where: scopeOf(filter),
9715
+ range: {
9716
+ gte: start,
9717
+ lt: end
9718
+ },
9719
+ limit: PAGE,
9720
+ ...page
9721
+ }));
9722
+ for (const row of hours) events[Math.floor((row.bucket_start_ms - start) / intervalMs)] += row.events;
9723
+ return events.map((count, index) => ({
9724
+ startMs: start + index * intervalMs,
9725
+ events: count
9726
+ }));
9727
+ };
9855
9728
  /** A bundle filter's distinct (field, value, type) predicates. */
9856
9729
  const latestPredicates = (bundle) => {
9857
9730
  if (bundle === void 0) return void 0;
@@ -9923,7 +9796,7 @@ const retained = (now, { rawDays, dailyDays }) => ({
9923
9796
  * Whether the rows a window reads still hold all of it; an older start is
9924
9797
  * partial from `oldest`, the first bucket its rows keep.
9925
9798
  */
9926
- const coverageOf = (start, oldest) => start === void 0 || start >= oldest ? {
9799
+ const coverageOf = (start, oldest) => start >= oldest ? {
9927
9800
  kind: "complete",
9928
9801
  sinceMs: 0
9929
9802
  } : {
@@ -10026,43 +9899,6 @@ const lifetimeMetrics = async (db, release) => {
10026
9899
  failedLaunches: lifetime?.failed_launches ?? 0
10027
9900
  };
10028
9901
  };
10029
- /**
10030
- * Counters, unique users, and a series over a window: a point per UTC day
10031
- * with rows, or, with `intervalMs`, every point of that span from the
10032
- * window's start.
10033
- */
10034
- const rangedMetrics = async (db, parts, users, range, days, hour, intervalMs) => {
10035
- const [counters, sketches] = await Promise.all([counterRows(db, parts, range, days, hour), sketchRows(db, users.parts, range, days, hour)]);
10036
- const series = /* @__PURE__ */ new Map();
10037
- if (intervalMs !== void 0) for (let start = range.start; start < range.end; start += intervalMs) series.set(start, {
10038
- downloads: 0,
10039
- launches: 0,
10040
- failedLaunches: 0
10041
- });
10042
- for (const row of counters) {
10043
- const start = intervalMs === void 0 ? dayFloor(row.bucket_start_ms) : row.bucket_start_ms - (row.bucket_start_ms - range.start) % intervalMs;
10044
- const point = series.get(start) ?? {
10045
- downloads: 0,
10046
- launches: 0,
10047
- failedLaunches: 0
10048
- };
10049
- point.downloads += row.downloads;
10050
- point.launches += row.launches;
10051
- point.failedLaunches += row.failed_launches;
10052
- series.set(start, point);
10053
- }
10054
- const total = (field) => counters.reduce((sum, row) => sum + row[field], 0);
10055
- return {
10056
- downloads: total("downloads"),
10057
- launches: total("launches"),
10058
- failedLaunches: total("failed_launches"),
10059
- uniqueUsers: countDistinct(mergeDistinct(sketches.map((row) => row[users.field]))),
10060
- series: [...series].sort(([left], [right]) => left - right).map(([startMs, point]) => ({
10061
- startMs,
10062
- ...point
10063
- }))
10064
- };
10065
- };
10066
9902
  /** A channel and platform's usage rows, of every app version or of one. */
10067
9903
  const usageParts = (channel, platform, appVersion) => ({
10068
9904
  scopeKind: "usage",
@@ -10073,35 +9909,15 @@ const usageParts = (channel, platform, appVersion) => ({
10073
9909
  appVersionKind: appVersion === void 0 ? "all" : "specific",
10074
9910
  appVersion: appVersion ?? ""
10075
9911
  });
10076
- const getReleaseActivity = async (db, input, now, retention) => {
10077
- const at = now();
10078
- const kept = retained(at, retention);
10079
- const data = input.scope !== void 0 ? [{
10080
- scope: input.scope,
10081
- metrics: await rangedMetrics(db, {
10082
- scopeKind: "channel",
10083
- releaseKind: "all",
10084
- releaseId: "",
10085
- channel: input.scope.channel,
10086
- platform: input.scope.platform,
10087
- appVersionKind: "all",
10088
- appVersion: ""
10089
- }, {
10090
- parts: usageParts(input.scope.channel, input.scope.platform),
10091
- field: "activity_users"
10092
- }, input.timeRange, true, kept.hour)
10093
- }] : await Promise.all(input.releases.map(async (release) => ({
10094
- release,
10095
- metrics: input.timeRange === void 0 ? await lifetimeMetrics(db, release) : await rangedMetrics(db, releaseParts(release), {
10096
- parts: releaseParts(release),
10097
- field: "launch_users"
10098
- }, input.timeRange, false, kept.hour, input.intervalMs)
10099
- })));
10100
- const oldest = kept[input.scope === void 0 ? "hour" : "day"];
9912
+ /** Each release's lifetime counters, which are kept: one logical row apiece. */
9913
+ const getReleaseActivity = async (db, input, now) => {
9914
+ const measuredAtMs = now();
10101
9915
  return {
10102
- coverage: coverageOf(input.timeRange?.start, oldest),
10103
- data,
10104
- measuredAtMs: at
9916
+ data: await Promise.all(input.releases.map(async (release) => ({
9917
+ release,
9918
+ metrics: await lifetimeMetrics(db, release)
9919
+ }))),
9920
+ measuredAtMs
10105
9921
  };
10106
9922
  };
10107
9923
  /**
@@ -10361,43 +10177,6 @@ const getUpdateFailures = async (db, input, now, retention) => {
10361
10177
  }
10362
10178
  };
10363
10179
  };
10364
- /** Reads daily gauges, never raw events or mutable latest-state distribution. */
10365
- const getDistributionHistory = async (db, input, now, retention) => {
10366
- const at = now();
10367
- const { start, end } = input.timeRange;
10368
- const points = /* @__PURE__ */ new Map();
10369
- for (let day = start; day < end; day += DAY_MS) points.set(day, []);
10370
- const rows = await drain((page) => db.findAggregates("insights_distribution_history", {
10371
- index: "byScope",
10372
- where: {
10373
- channel: input.channel,
10374
- platform: input.platform
10375
- },
10376
- range: {
10377
- gte: start,
10378
- lt: end
10379
- },
10380
- limit: PAGE,
10381
- ...page
10382
- }));
10383
- for (const row of rows) {
10384
- if (row.installations <= 0) continue;
10385
- points.get(row.bucket_start_ms).push({
10386
- appVersion: row.app_version,
10387
- releaseId: row.release_id || null,
10388
- bundleKind: row.bundle_kind,
10389
- installations: row.installations
10390
- });
10391
- }
10392
- return {
10393
- coverage: coverageOf(start, retained(at, retention).day),
10394
- measuredAtMs: at,
10395
- points: [...points].map(([startMs, bundles]) => ({
10396
- startMs,
10397
- bundles
10398
- }))
10399
- };
10400
- };
10401
10180
  //#endregion
10402
10181
  //#region ../insights/dist/server/queryInput.mjs
10403
10182
  const MAX_PAGE_LIMIT = 100;
@@ -10588,8 +10367,8 @@ const createInsightsApi = (db, now, retention) => ({
10588
10367
  findLatestEvents: (input) => findLatestEvents(db, input),
10589
10368
  countLatestEvents: (input) => countLatestEvents(db, input),
10590
10369
  countEvents: (input) => countEvents(db, input),
10591
- getReleaseActivity: (input) => getReleaseActivity(db, input, now, retention),
10592
- getDistributionHistory: (input) => getDistributionHistory(db, input, now, retention),
10370
+ countEventSeries: (input) => countEventSeries(db, input),
10371
+ getReleaseActivity: (input) => getReleaseActivity(db, input, now),
10593
10372
  getAppUsage: (input) => getAppUsage(db, input, now, retention),
10594
10373
  /** A release's or a channel's update failures, and their breakdown over a time range. */
10595
10374
  getUpdateFailures: (input) => getUpdateFailures(db, input, now, retention)
@@ -10658,7 +10437,7 @@ const insights = (options = {}) => {
10658
10437
  const plugin = definePlugin({
10659
10438
  id: "insights",
10660
10439
  namespace: false,
10661
- schemaVersion: "1.3.0",
10440
+ schemaVersion: "1.2.0",
10662
10441
  schema: createInsightsSchema(retention),
10663
10442
  init: ({ db, now }) => {
10664
10443
  const api = createInsightsApi(db, now, retention);
@@ -10673,32 +10452,6 @@ const insights = (options = {}) => {
10673
10452
  handler: (request, params) => routes[handler](params, request)
10674
10453
  })),
10675
10454
  failuresEndpoint(api),
10676
- {
10677
- access: "admin",
10678
- method: "GET",
10679
- path: "/distribution-history",
10680
- handler: async (request) => {
10681
- const query = new URL(request.url).searchParams;
10682
- try {
10683
- const result = await createInsightsModel(api).getDistributionHistory({
10684
- platform: query.get("platform"),
10685
- channel: query.get("channel") ?? "",
10686
- timeRange: {
10687
- start: Number(query.get("start") ?? NaN),
10688
- end: Number(query.get("end") ?? NaN)
10689
- }
10690
- });
10691
- return Response.json(result, { headers: { "cache-control": "private, no-store" } });
10692
- } catch (error) {
10693
- if (error instanceof DatabaseAdapterInputError) return Response.json({ error: "Choose a platform, channel, and up to 31 whole UTC days." }, { status: 400 });
10694
- if (!isDatabaseBusyError(error)) throw error;
10695
- return Response.json({ error: "Service unavailable" }, {
10696
- status: 503,
10697
- headers: { "retry-after": "5" }
10698
- });
10699
- }
10700
- }
10701
- },
10702
10455
  {
10703
10456
  access: "admin",
10704
10457
  method: "GET",
package/dist/index.d.cts CHANGED
@@ -167,6 +167,19 @@ interface InsightsCountEventsInput {
167
167
  readonly sinceMs: number;
168
168
  readonly beforeReceivedAtMs: number;
169
169
  }
170
+ /** One bundle filter's stored events in each interval of a time range. */
171
+ interface InsightsCountEventSeriesInput {
172
+ readonly filter: InsightsBundleEventFilter;
173
+ /** Whole UTC hours, at most 90 days, that a whole number of intervals fill. */
174
+ readonly timeRange: InsightsTimeRange;
175
+ /** Whole hours each point spans, from `timeRange.start`. */
176
+ readonly intervalMs: number;
177
+ }
178
+ /** Stored events in [startMs, startMs + intervalMs). */
179
+ interface InsightsEventSeriesPoint {
180
+ readonly startMs: number;
181
+ readonly events: number;
182
+ }
170
183
  interface ReleaseReference {
171
184
  readonly releaseId: string;
172
185
  readonly platform: "ios" | "android";
@@ -185,44 +198,22 @@ type InsightsCoverage = {
185
198
  readonly kind: "partial";
186
199
  readonly sinceMs: number | null;
187
200
  };
201
+ /**
202
+ * A release's counts since its first report, from its lifetime counters,
203
+ * which are kept.
204
+ */
188
205
  interface ReleaseActivityMetrics {
189
206
  readonly downloads: number;
190
207
  readonly launches: number;
191
208
  readonly failedLaunches: number;
192
- /** Omitted for lifetime-only bundle-row reads. */
193
- readonly uniqueUsers?: number;
194
- /**
195
- * Points of each UTC day, or of each `intervalMs` from the period's start
196
- * on a release read that asks for one; present only for period reads.
197
- */
198
- readonly series?: readonly {
199
- readonly startMs: number;
200
- readonly downloads: number;
201
- readonly launches: number;
202
- readonly failedLaunches: number;
203
- }[];
204
209
  }
205
- type InsightsGetReleaseActivityInput = {
210
+ interface InsightsGetReleaseActivityInput {
211
+ /** 1 to 100 releases. */
206
212
  readonly releases: readonly ReleaseReference[];
207
- readonly timeRange?: InsightsTimeRange;
208
- /**
209
- * Whole hours each series point spans, from `timeRange.start`, with
210
- * every point present; omitted, the series has a point per UTC day
211
- * with reports. Only with a `timeRange`.
212
- */
213
- readonly intervalMs?: number;
214
- readonly scope?: never;
215
- } | {
216
- readonly scope: InsightsScope;
217
- readonly timeRange: InsightsTimeRange;
218
- readonly intervalMs?: never;
219
- readonly releases?: never;
220
- };
213
+ }
221
214
  interface InsightsGetReleaseActivityResult {
222
- readonly coverage: InsightsCoverage;
223
215
  readonly data: readonly {
224
- readonly release?: ReleaseReference;
225
- readonly scope?: InsightsScope;
216
+ readonly release: ReleaseReference;
226
217
  readonly metrics: ReleaseActivityMetrics;
227
218
  }[];
228
219
  readonly measuredAtMs: number;
@@ -258,23 +249,6 @@ interface InsightsGetAppUsageResult {
258
249
  }[];
259
250
  readonly measuredAtMs: number;
260
251
  }
261
- /** Whole UTC days, at most 31, including the current unfinished day. */
262
- interface InsightsGetDistributionHistoryInput extends InsightsScope {
263
- readonly timeRange: InsightsTimeRange;
264
- }
265
- interface InsightsGetDistributionHistoryResult {
266
- readonly coverage: InsightsCoverage;
267
- readonly measuredAtMs: number;
268
- readonly points: readonly {
269
- readonly startMs: number;
270
- readonly bundles: readonly {
271
- readonly appVersion: string;
272
- readonly releaseId: string | null;
273
- readonly bundleKind: "release" | "builtin" | "unknown";
274
- readonly installations: number;
275
- }[];
276
- }[];
277
- }
278
252
  //#endregion
279
253
  //#region ../insights/dist/server/schema.d.mts
280
254
  //#region src/server/schema.d.ts
@@ -428,10 +402,6 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
428
402
  movement_install_id: "string";
429
403
  bundle_ref: "string";
430
404
  }, {
431
- readonly recent: {
432
- readonly eq: readonly ["channel", "platform", "day"];
433
- readonly sort: readonly ["received_at_ms"];
434
- };
435
405
  readonly movementsByInstall: {
436
406
  readonly eq: readonly ["movement_install_id"];
437
407
  readonly sort: readonly ["received_at_ms"];
@@ -531,7 +501,7 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
531
501
  readonly eq: readonly ["identity"];
532
502
  readonly sort: readonly ["bucket_start_ms"];
533
503
  };
534
- }, "launch_users" | "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
504
+ }, "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
535
505
  readonly insights_overview_daily: import("@hot-updater/plugin-core").AggregateDefinition<{
536
506
  readonly identity: {
537
507
  readonly type: "string";
@@ -559,7 +529,7 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
559
529
  readonly eq: readonly ["identity"];
560
530
  readonly sort: readonly ["bucket_start_ms"];
561
531
  };
562
- }, "launch_users" | "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
532
+ }, "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
563
533
  readonly insights_overview_lifetime: import("@hot-updater/plugin-core").AggregateDefinition<{
564
534
  readonly identity: {
565
535
  readonly type: "string";
@@ -616,68 +586,6 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
616
586
  readonly sort: readonly ["bucket_start_ms"];
617
587
  };
618
588
  }, never, readonly ["channel", "platform", "app_version", "release_id", "bucket_start_ms"]>;
619
- readonly bundle_daily_heads: import("@hot-updater/plugin-core").TableDefinition<{
620
- readonly install_id: {
621
- readonly type: "string";
622
- readonly maxLength: 255;
623
- };
624
- readonly bucket_start_ms: {
625
- readonly type: "integer";
626
- };
627
- readonly id: {
628
- readonly type: "string";
629
- readonly maxLength: 36;
630
- };
631
- readonly received_at_ms: {
632
- readonly type: "integer";
633
- };
634
- readonly channel: {
635
- readonly type: "string";
636
- };
637
- readonly platform: {
638
- readonly type: "string";
639
- readonly maxLength: 16;
640
- };
641
- readonly app_version: {
642
- readonly type: "string";
643
- };
644
- readonly release_id: {
645
- readonly type: "string";
646
- readonly maxLength: 36;
647
- };
648
- readonly bundle_kind: {
649
- readonly type: "string";
650
- readonly maxLength: 16;
651
- };
652
- }, Readonly<Record<string, import("@hot-updater/plugin-core").FieldType>>, {}, readonly ["install_id", "bucket_start_ms"]>;
653
- readonly insights_distribution_history: import("@hot-updater/plugin-core").AggregateDefinition<{
654
- readonly channel: {
655
- readonly type: "string";
656
- };
657
- readonly platform: {
658
- readonly type: "string";
659
- readonly maxLength: 16;
660
- };
661
- readonly app_version: {
662
- readonly type: "string";
663
- };
664
- readonly release_id: {
665
- readonly type: "string";
666
- readonly maxLength: 36;
667
- };
668
- readonly bundle_kind: {
669
- readonly type: "string";
670
- readonly maxLength: 16;
671
- };
672
- readonly bucket_start_ms: {
673
- readonly type: "integer";
674
- };
675
- }, "installations", {
676
- readonly byScope: {
677
- readonly eq: readonly ["channel", "platform"];
678
- readonly sort: readonly ["bucket_start_ms"];
679
- };
680
- }, never, readonly ["channel", "platform", "app_version", "release_id", "bundle_kind", "bucket_start_ms"]>;
681
589
  readonly insights_latest_by_bundle: import("@hot-updater/plugin-core").AggregateDefinition<{
682
590
  readonly platform: {
683
591
  readonly type: "string";
@@ -773,8 +681,8 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
773
681
  findLatestEvents: (input: InsightsFindLatestEventsInput) => Promise<readonly BundleEventRow[]>;
774
682
  countLatestEvents: (input: InsightsCountLatestEventsInput) => Promise<number>;
775
683
  countEvents: (input: InsightsCountEventsInput) => Promise<number>;
684
+ countEventSeries: (input: InsightsCountEventSeriesInput) => Promise<readonly InsightsEventSeriesPoint[]>;
776
685
  getReleaseActivity: (input: InsightsGetReleaseActivityInput) => Promise<InsightsGetReleaseActivityResult>;
777
- getDistributionHistory: (input: InsightsGetDistributionHistoryInput) => Promise<InsightsGetDistributionHistoryResult>;
778
686
  getAppUsage: (input: InsightsGetAppUsageInput) => Promise<InsightsGetAppUsageResult>;
779
687
  getUpdateFailures: (input: InsightsUpdateFailuresInput) => Promise<InsightsUpdateFailures>;
780
688
  };
package/dist/index.d.mts CHANGED
@@ -167,6 +167,19 @@ interface InsightsCountEventsInput {
167
167
  readonly sinceMs: number;
168
168
  readonly beforeReceivedAtMs: number;
169
169
  }
170
+ /** One bundle filter's stored events in each interval of a time range. */
171
+ interface InsightsCountEventSeriesInput {
172
+ readonly filter: InsightsBundleEventFilter;
173
+ /** Whole UTC hours, at most 90 days, that a whole number of intervals fill. */
174
+ readonly timeRange: InsightsTimeRange;
175
+ /** Whole hours each point spans, from `timeRange.start`. */
176
+ readonly intervalMs: number;
177
+ }
178
+ /** Stored events in [startMs, startMs + intervalMs). */
179
+ interface InsightsEventSeriesPoint {
180
+ readonly startMs: number;
181
+ readonly events: number;
182
+ }
170
183
  interface ReleaseReference {
171
184
  readonly releaseId: string;
172
185
  readonly platform: "ios" | "android";
@@ -185,44 +198,22 @@ type InsightsCoverage = {
185
198
  readonly kind: "partial";
186
199
  readonly sinceMs: number | null;
187
200
  };
201
+ /**
202
+ * A release's counts since its first report, from its lifetime counters,
203
+ * which are kept.
204
+ */
188
205
  interface ReleaseActivityMetrics {
189
206
  readonly downloads: number;
190
207
  readonly launches: number;
191
208
  readonly failedLaunches: number;
192
- /** Omitted for lifetime-only bundle-row reads. */
193
- readonly uniqueUsers?: number;
194
- /**
195
- * Points of each UTC day, or of each `intervalMs` from the period's start
196
- * on a release read that asks for one; present only for period reads.
197
- */
198
- readonly series?: readonly {
199
- readonly startMs: number;
200
- readonly downloads: number;
201
- readonly launches: number;
202
- readonly failedLaunches: number;
203
- }[];
204
209
  }
205
- type InsightsGetReleaseActivityInput = {
210
+ interface InsightsGetReleaseActivityInput {
211
+ /** 1 to 100 releases. */
206
212
  readonly releases: readonly ReleaseReference[];
207
- readonly timeRange?: InsightsTimeRange;
208
- /**
209
- * Whole hours each series point spans, from `timeRange.start`, with
210
- * every point present; omitted, the series has a point per UTC day
211
- * with reports. Only with a `timeRange`.
212
- */
213
- readonly intervalMs?: number;
214
- readonly scope?: never;
215
- } | {
216
- readonly scope: InsightsScope;
217
- readonly timeRange: InsightsTimeRange;
218
- readonly intervalMs?: never;
219
- readonly releases?: never;
220
- };
213
+ }
221
214
  interface InsightsGetReleaseActivityResult {
222
- readonly coverage: InsightsCoverage;
223
215
  readonly data: readonly {
224
- readonly release?: ReleaseReference;
225
- readonly scope?: InsightsScope;
216
+ readonly release: ReleaseReference;
226
217
  readonly metrics: ReleaseActivityMetrics;
227
218
  }[];
228
219
  readonly measuredAtMs: number;
@@ -258,23 +249,6 @@ interface InsightsGetAppUsageResult {
258
249
  }[];
259
250
  readonly measuredAtMs: number;
260
251
  }
261
- /** Whole UTC days, at most 31, including the current unfinished day. */
262
- interface InsightsGetDistributionHistoryInput extends InsightsScope {
263
- readonly timeRange: InsightsTimeRange;
264
- }
265
- interface InsightsGetDistributionHistoryResult {
266
- readonly coverage: InsightsCoverage;
267
- readonly measuredAtMs: number;
268
- readonly points: readonly {
269
- readonly startMs: number;
270
- readonly bundles: readonly {
271
- readonly appVersion: string;
272
- readonly releaseId: string | null;
273
- readonly bundleKind: "release" | "builtin" | "unknown";
274
- readonly installations: number;
275
- }[];
276
- }[];
277
- }
278
252
  //#endregion
279
253
  //#region ../insights/dist/server/schema.d.mts
280
254
  //#region src/server/schema.d.ts
@@ -428,10 +402,6 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
428
402
  movement_install_id: "string";
429
403
  bundle_ref: "string";
430
404
  }, {
431
- readonly recent: {
432
- readonly eq: readonly ["channel", "platform", "day"];
433
- readonly sort: readonly ["received_at_ms"];
434
- };
435
405
  readonly movementsByInstall: {
436
406
  readonly eq: readonly ["movement_install_id"];
437
407
  readonly sort: readonly ["received_at_ms"];
@@ -531,7 +501,7 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
531
501
  readonly eq: readonly ["identity"];
532
502
  readonly sort: readonly ["bucket_start_ms"];
533
503
  };
534
- }, "launch_users" | "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
504
+ }, "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
535
505
  readonly insights_overview_daily: import("@hot-updater/plugin-core").AggregateDefinition<{
536
506
  readonly identity: {
537
507
  readonly type: "string";
@@ -559,7 +529,7 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
559
529
  readonly eq: readonly ["identity"];
560
530
  readonly sort: readonly ["bucket_start_ms"];
561
531
  };
562
- }, "launch_users" | "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
532
+ }, "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
563
533
  readonly insights_overview_lifetime: import("@hot-updater/plugin-core").AggregateDefinition<{
564
534
  readonly identity: {
565
535
  readonly type: "string";
@@ -616,68 +586,6 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
616
586
  readonly sort: readonly ["bucket_start_ms"];
617
587
  };
618
588
  }, never, readonly ["channel", "platform", "app_version", "release_id", "bucket_start_ms"]>;
619
- readonly bundle_daily_heads: import("@hot-updater/plugin-core").TableDefinition<{
620
- readonly install_id: {
621
- readonly type: "string";
622
- readonly maxLength: 255;
623
- };
624
- readonly bucket_start_ms: {
625
- readonly type: "integer";
626
- };
627
- readonly id: {
628
- readonly type: "string";
629
- readonly maxLength: 36;
630
- };
631
- readonly received_at_ms: {
632
- readonly type: "integer";
633
- };
634
- readonly channel: {
635
- readonly type: "string";
636
- };
637
- readonly platform: {
638
- readonly type: "string";
639
- readonly maxLength: 16;
640
- };
641
- readonly app_version: {
642
- readonly type: "string";
643
- };
644
- readonly release_id: {
645
- readonly type: "string";
646
- readonly maxLength: 36;
647
- };
648
- readonly bundle_kind: {
649
- readonly type: "string";
650
- readonly maxLength: 16;
651
- };
652
- }, Readonly<Record<string, import("@hot-updater/plugin-core").FieldType>>, {}, readonly ["install_id", "bucket_start_ms"]>;
653
- readonly insights_distribution_history: import("@hot-updater/plugin-core").AggregateDefinition<{
654
- readonly channel: {
655
- readonly type: "string";
656
- };
657
- readonly platform: {
658
- readonly type: "string";
659
- readonly maxLength: 16;
660
- };
661
- readonly app_version: {
662
- readonly type: "string";
663
- };
664
- readonly release_id: {
665
- readonly type: "string";
666
- readonly maxLength: 36;
667
- };
668
- readonly bundle_kind: {
669
- readonly type: "string";
670
- readonly maxLength: 16;
671
- };
672
- readonly bucket_start_ms: {
673
- readonly type: "integer";
674
- };
675
- }, "installations", {
676
- readonly byScope: {
677
- readonly eq: readonly ["channel", "platform"];
678
- readonly sort: readonly ["bucket_start_ms"];
679
- };
680
- }, never, readonly ["channel", "platform", "app_version", "release_id", "bundle_kind", "bucket_start_ms"]>;
681
589
  readonly insights_latest_by_bundle: import("@hot-updater/plugin-core").AggregateDefinition<{
682
590
  readonly platform: {
683
591
  readonly type: "string";
@@ -773,8 +681,8 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
773
681
  findLatestEvents: (input: InsightsFindLatestEventsInput) => Promise<readonly BundleEventRow[]>;
774
682
  countLatestEvents: (input: InsightsCountLatestEventsInput) => Promise<number>;
775
683
  countEvents: (input: InsightsCountEventsInput) => Promise<number>;
684
+ countEventSeries: (input: InsightsCountEventSeriesInput) => Promise<readonly InsightsEventSeriesPoint[]>;
776
685
  getReleaseActivity: (input: InsightsGetReleaseActivityInput) => Promise<InsightsGetReleaseActivityResult>;
777
- getDistributionHistory: (input: InsightsGetDistributionHistoryInput) => Promise<InsightsGetDistributionHistoryResult>;
778
686
  getAppUsage: (input: InsightsGetAppUsageInput) => Promise<InsightsGetAppUsageResult>;
779
687
  getUpdateFailures: (input: InsightsUpdateFailuresInput) => Promise<InsightsUpdateFailures>;
780
688
  };
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.30",
4
+ "version": "1.0.0-rc.31",
5
5
  "description": "React Native OTA solution for self-hosted",
6
6
  "main": "dist/index.cjs",
7
7
  "types": "dist/index.d.cts",
@@ -37,9 +37,9 @@
37
37
  ],
38
38
  "dependencies": {
39
39
  "hono": "4.12.34",
40
- "@hot-updater/cli-tools": "1.0.0-rc.30",
40
+ "@hot-updater/cli-tools": "1.0.0-rc.31",
41
41
  "@hot-updater/plugin-core": "1.0.0-rc.28",
42
- "@hot-updater/server": "1.0.0-rc.30",
42
+ "@hot-updater/server": "1.0.0-rc.31",
43
43
  "@hot-updater/protocol": "1.0.0-rc.28"
44
44
  },
45
45
  "publishConfig": {
@@ -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/test-utils": "1.0.0-rc.29",
59
- "@hot-updater/mock": "1.0.0-rc.28"
58
+ "@hot-updater/mock": "1.0.0-rc.28",
59
+ "@hot-updater/test-utils": "1.0.0-rc.30"
60
60
  },
61
61
  "peerDependencies": {
62
62
  "firebase-admin": "*",