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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -101,6 +101,8 @@ type DatabaseBundleEventMetadata = DatabaseJsonObject & {
101
101
  readonly update_strategy: "fingerprint" | "appVersion" | null;
102
102
  readonly fingerprint_hash: string | null;
103
103
  readonly sdk_version: string | null;
104
+ /** The native build's built-in bundle ID, when the SDK reports it. */
105
+ readonly min_bundle_id?: string;
104
106
  /** `UPDATE_FAILED`: what failed. */
105
107
  readonly failure?: BundleEventFailure;
106
108
  readonly http_response?: DatabaseHttpResponse;
@@ -276,7 +278,10 @@ interface InsightsGetAppUsageResult {
276
278
  readonly bundleDistribution: readonly {
277
279
  readonly appVersion: string;
278
280
  readonly platform: "ios" | "android";
281
+ /** The release the installations run; null for none. */
279
282
  readonly releaseId: string | null;
283
+ /** The native build's built-in bundle, when they run it. */
284
+ readonly builtinBundleId: string | null;
280
285
  readonly installations: number;
281
286
  }[];
282
287
  readonly measuredAtMs: number;
@@ -607,6 +612,38 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
607
612
  readonly sort: readonly ["bucket_start_ms"];
608
613
  };
609
614
  }, never, readonly ["channel", "platform", "app_version", "release_id", "bucket_start_ms"]>;
615
+ readonly insights_builtin_distribution: import("@hot-updater/plugin-core").AggregateDefinition<{
616
+ readonly channel: {
617
+ readonly type: "string";
618
+ };
619
+ readonly platform: {
620
+ readonly type: "string";
621
+ readonly maxLength: 16;
622
+ };
623
+ readonly app_version: {
624
+ readonly type: "string";
625
+ };
626
+ readonly release_id: {
627
+ readonly type: "string";
628
+ readonly maxLength: 36;
629
+ };
630
+ readonly builtin_bundle_id: {
631
+ readonly type: "string";
632
+ readonly maxLength: 36;
633
+ };
634
+ readonly bucket_start_ms: {
635
+ readonly type: "integer";
636
+ };
637
+ }, "latest_installations", {
638
+ readonly byScope: {
639
+ readonly eq: readonly ["channel", "platform"];
640
+ readonly sort: readonly ["bucket_start_ms"];
641
+ };
642
+ readonly byVersion: {
643
+ readonly eq: readonly ["channel", "platform", "app_version"];
644
+ readonly sort: readonly ["bucket_start_ms"];
645
+ };
646
+ }, never, readonly ["channel", "platform", "app_version", "release_id", "builtin_bundle_id", "bucket_start_ms"]>;
610
647
  readonly insights_latest_by_bundle: import("@hot-updater/plugin-core").AggregateDefinition<{
611
648
  readonly platform: {
612
649
  readonly type: "string";
package/dist/index.d.mts CHANGED
@@ -101,6 +101,8 @@ type DatabaseBundleEventMetadata = DatabaseJsonObject & {
101
101
  readonly update_strategy: "fingerprint" | "appVersion" | null;
102
102
  readonly fingerprint_hash: string | null;
103
103
  readonly sdk_version: string | null;
104
+ /** The native build's built-in bundle ID, when the SDK reports it. */
105
+ readonly min_bundle_id?: string;
104
106
  /** `UPDATE_FAILED`: what failed. */
105
107
  readonly failure?: BundleEventFailure;
106
108
  readonly http_response?: DatabaseHttpResponse;
@@ -276,7 +278,10 @@ interface InsightsGetAppUsageResult {
276
278
  readonly bundleDistribution: readonly {
277
279
  readonly appVersion: string;
278
280
  readonly platform: "ios" | "android";
281
+ /** The release the installations run; null for none. */
279
282
  readonly releaseId: string | null;
283
+ /** The native build's built-in bundle, when they run it. */
284
+ readonly builtinBundleId: string | null;
280
285
  readonly installations: number;
281
286
  }[];
282
287
  readonly measuredAtMs: number;
@@ -607,6 +612,38 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
607
612
  readonly sort: readonly ["bucket_start_ms"];
608
613
  };
609
614
  }, never, readonly ["channel", "platform", "app_version", "release_id", "bucket_start_ms"]>;
615
+ readonly insights_builtin_distribution: import("@hot-updater/plugin-core").AggregateDefinition<{
616
+ readonly channel: {
617
+ readonly type: "string";
618
+ };
619
+ readonly platform: {
620
+ readonly type: "string";
621
+ readonly maxLength: 16;
622
+ };
623
+ readonly app_version: {
624
+ readonly type: "string";
625
+ };
626
+ readonly release_id: {
627
+ readonly type: "string";
628
+ readonly maxLength: 36;
629
+ };
630
+ readonly builtin_bundle_id: {
631
+ readonly type: "string";
632
+ readonly maxLength: 36;
633
+ };
634
+ readonly bucket_start_ms: {
635
+ readonly type: "integer";
636
+ };
637
+ }, "latest_installations", {
638
+ readonly byScope: {
639
+ readonly eq: readonly ["channel", "platform"];
640
+ readonly sort: readonly ["bucket_start_ms"];
641
+ };
642
+ readonly byVersion: {
643
+ readonly eq: readonly ["channel", "platform", "app_version"];
644
+ readonly sort: readonly ["bucket_start_ms"];
645
+ };
646
+ }, never, readonly ["channel", "platform", "app_version", "release_id", "builtin_bundle_id", "bucket_start_ms"]>;
610
647
  readonly insights_latest_by_bundle: import("@hot-updater/plugin-core").AggregateDefinition<{
611
648
  readonly platform: {
612
649
  readonly type: "string";
@@ -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.34";
4573
+ const HOT_UPDATER_SERVER_VERSION = "1.0.0-rc.36";
4574
4574
  //#endregion
4575
4575
  //#region ../../packages/server/dist/handlerVersionRoutes.mjs
4576
4576
  /**
@@ -9793,8 +9793,19 @@ 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
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");
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, "min_bundle_id", isString) && isOptional(value, "failure", isDatabaseBundleEventFailure) && isOptional(value, "http_response", isDatabaseHttpResponse) && isOptional(value, "delivery", isString) && isOptional(value, "patch_fallback", (flag) => typeof flag === "boolean");
9797
9797
  const isRecord$2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
9798
+ /**
9799
+ * The built-in bundle a report says its installation runs: the running
9800
+ * bundle, which a download leaves at its source, when it is the native
9801
+ * build's built-in bundle, whatever Release selected it. Null for any other
9802
+ * bundle, and for a report without the build's built-in bundle ID.
9803
+ */
9804
+ const runningBuiltinBundleId = (row) => {
9805
+ const running = row.type === "UPDATE_DOWNLOADED" ? row.from_bundle_id : row.to_bundle_id;
9806
+ const builtin = isRecord$2(row.metadata) ? row.metadata.min_bundle_id : null;
9807
+ return typeof builtin === "string" && running === builtin ? builtin : null;
9808
+ };
9798
9809
  const isIdentity$1 = (value) => typeof value === "string" && value.length > 0 && value.length <= 255;
9799
9810
  const isTextOrNull = (value) => value === null || typeof value === "string";
9800
9811
  /** Each bundle event field and what it holds. */
@@ -10095,6 +10106,11 @@ function readEventId(payload) {
10095
10106
  if (!isUUIDv7(value)) throw new InsightsBadRequestError("Invalid event field: eventId");
10096
10107
  return value;
10097
10108
  }
10109
+ /**
10110
+ * The native build's built-in bundle ID: a bundle ID's length at most, and
10111
+ * left out when it is missing or malformed, so the report still records.
10112
+ */
10113
+ const readMinBundleId = (value) => typeof value === "string" && value.length > 0 && value.length <= 36 ? value : null;
10098
10114
  /** One value of an open set: any other value reads as `unknown`. */
10099
10115
  const oneOf = (values, value) => values.includes(value) ? value : "unknown";
10100
10116
  const STAGES = [
@@ -10220,6 +10236,7 @@ function requireEvent(payload) {
10220
10236
  cohort: requireStringField(payload, "cohort"),
10221
10237
  fingerprintHash: requireNullableStringField(payload, "fingerprintHash"),
10222
10238
  sdkVersion: payload.sdkVersion === void 0 ? null : requireNullableStringField(payload, "sdkVersion"),
10239
+ minBundleId: readMinBundleId(payload.minBundleId),
10223
10240
  fromReleaseId: requireNullableStringField(payload, "fromReleaseId"),
10224
10241
  toReleaseId: requireNullableStringField(payload, "toReleaseId")
10225
10242
  };
@@ -10301,6 +10318,7 @@ function createBundleEventRow(input) {
10301
10318
  cohort: input.cohort,
10302
10319
  fingerprint_hash: input.fingerprintHash,
10303
10320
  sdk_version: input.sdkVersion ?? null,
10321
+ ...input.minBundleId ? { min_bundle_id: input.minBundleId } : {},
10304
10322
  update_strategy: input.updateStrategy
10305
10323
  };
10306
10324
  switch (input.type) {
@@ -10523,6 +10541,7 @@ const eventDetails = ({ type, metadata }) => ({
10523
10541
  const toEventHistoryRow = (row) => ({
10524
10542
  ...row.to_release_id == null ? {} : { toReleaseId: row.to_release_id },
10525
10543
  ...row.metadata.sdk_version == null ? {} : { sdkVersion: row.metadata.sdk_version },
10544
+ ...row.metadata.min_bundle_id === void 0 ? {} : { minBundleId: row.metadata.min_bundle_id },
10526
10545
  appVersion: row.app_version,
10527
10546
  channel: row.channel,
10528
10547
  cohort: row.metadata.cohort,
@@ -10538,6 +10557,7 @@ const toEventHistoryRow = (row) => ({
10538
10557
  });
10539
10558
  const toInstallationRow = (row) => ({
10540
10559
  ...eventDetails(row).httpResponse ? { httpResponse: eventDetails(row).httpResponse } : {},
10560
+ ...row.metadata.min_bundle_id === void 0 ? {} : { minBundleId: row.metadata.min_bundle_id },
10541
10561
  appVersion: row.app_version,
10542
10562
  channel: row.channel,
10543
10563
  cohort: row.metadata.cohort,
@@ -11089,6 +11109,55 @@ const insightsDistribution = (retention) => defineAggregate({
11089
11109
  },
11090
11110
  retention
11091
11111
  });
11112
+ /**
11113
+ * The installations of `insights_distribution` that run their native build's
11114
+ * built-in bundle, by its ID (`builtin_bundle_id`), within the same rows:
11115
+ * reads move them out of the row of their release, or of no release.
11116
+ */
11117
+ const insightsBuiltinDistribution = (retention) => defineAggregate({
11118
+ channel: { type: "string" },
11119
+ platform: {
11120
+ type: "string",
11121
+ maxLength: 16
11122
+ },
11123
+ app_version: { type: "string" },
11124
+ release_id: {
11125
+ type: "string",
11126
+ maxLength: 36
11127
+ },
11128
+ builtin_bundle_id: {
11129
+ type: "string",
11130
+ maxLength: 36
11131
+ },
11132
+ bucket_start_ms: { type: "integer" }
11133
+ }, {
11134
+ key: [
11135
+ "channel",
11136
+ "platform",
11137
+ "app_version",
11138
+ "release_id",
11139
+ "builtin_bundle_id",
11140
+ "bucket_start_ms"
11141
+ ],
11142
+ gauges: ["latest_installations"],
11143
+ shards: GAUGE_SHARDS,
11144
+ batched: true,
11145
+ indexes: {
11146
+ byScope: {
11147
+ eq: ["channel", "platform"],
11148
+ sort: ["bucket_start_ms"]
11149
+ },
11150
+ byVersion: {
11151
+ eq: [
11152
+ "channel",
11153
+ "platform",
11154
+ "app_version"
11155
+ ],
11156
+ sort: ["bucket_start_ms"]
11157
+ }
11158
+ },
11159
+ retention
11160
+ });
11092
11161
  /** Latest events by the bundle they came from or went to, bucketed like the distribution. */
11093
11162
  const insightsLatestByBundle = (retention) => defineAggregate({
11094
11163
  platform: {
@@ -11208,6 +11277,7 @@ const createInsightsSchema = ({ rawDays, dailyDays } = DEFAULT_INSIGHTS_RETENTIO
11208
11277
  insights_overview_lifetime: lifetimeCounters(),
11209
11278
  insights_sketches_lifetime: lifetimeSketches(),
11210
11279
  insights_distribution: insightsDistribution(daily),
11280
+ insights_builtin_distribution: insightsBuiltinDistribution(daily),
11211
11281
  insights_latest_by_bundle: insightsLatestByBundle(daily),
11212
11282
  insights_outcomes: insightsOutcomes(hourly),
11213
11283
  insights_failures: insightsFailures(hourly)
@@ -11326,13 +11396,16 @@ const insightsIdentity = (identity) => insightsOverviewId({
11326
11396
  bucketStartMs: 0
11327
11397
  });
11328
11398
  /**
11329
- * The bucket a head's gauges count it in, the UTC day of its event, and the
11330
- * release its distribution row names: the one it runs, which a download
11331
- * leaves at its source.
11399
+ * The bucket a head's gauges count it in, the UTC day of its event, and what
11400
+ * its distribution row names: the release it runs, which a download leaves
11401
+ * at its source, and the built-in bundle, when it runs the native build's.
11402
+ * A head stored without the built-in bundle ID counts in no built-in row, so
11403
+ * taking it back moves none.
11332
11404
  */
11333
11405
  const gaugeSlot = (head) => ({
11334
11406
  bucket: dayOf(head.received_at_ms),
11335
- releaseId: head.type === "UPDATE_DOWNLOADED" ? head.from_release_id : head.to_release_id
11407
+ releaseId: head.type === "UPDATE_DOWNLOADED" ? head.from_release_id : head.to_release_id,
11408
+ builtinBundleId: runningBuiltinBundleId(head)
11336
11409
  });
11337
11410
  /**
11338
11411
  * The `insights_latest_by_bundle` field of a head's (from, to) pair. A head
@@ -11343,11 +11416,12 @@ const PAIR_FIELD = "from_to";
11343
11416
  /** The pair gauge's `bundle_id`: a hash of the two bundle ids, which fit no single 36-character column. */
11344
11417
  const bundlePairKey = (from, to) => insightsKey(`${from.length}:${from}${to.length}:${to}`);
11345
11418
  /**
11346
- * Moves a head's gauges: the distribution row, one row per bundle it
11347
- * references, and its pair; -1 takes back the head an event replaces.
11419
+ * Moves a head's gauges: the distribution row, and the built-in one when it
11420
+ * runs its build's built-in bundle, one row per bundle it references, and
11421
+ * its pair; -1 takes back the head an event replaces.
11348
11422
  */
11349
11423
  const countHead = (tx, head, delta) => {
11350
- const { bucket, releaseId } = gaugeSlot(head);
11424
+ const { bucket, releaseId, builtinBundleId } = gaugeSlot(head);
11351
11425
  const shardBy = head.install_id;
11352
11426
  tx.aggregate("insights_distribution", {
11353
11427
  channel: head.channel,
@@ -11356,6 +11430,14 @@ const countHead = (tx, head, delta) => {
11356
11430
  release_id: releaseId ?? "",
11357
11431
  bucket_start_ms: bucket
11358
11432
  }, { latest_installations: delta }, { shardBy });
11433
+ if (builtinBundleId !== null) tx.aggregate("insights_builtin_distribution", {
11434
+ channel: head.channel,
11435
+ platform: head.platform,
11436
+ app_version: head.app_version,
11437
+ release_id: releaseId ?? "",
11438
+ builtin_bundle_id: builtinBundleId,
11439
+ bucket_start_ms: bucket
11440
+ }, { latest_installations: delta }, { shardBy });
11359
11441
  for (const field of ["from_bundle_id", "to_bundle_id"]) {
11360
11442
  const bundleId = head[field];
11361
11443
  if (bundleId === null) continue;
@@ -12011,26 +12093,49 @@ const getAppUsage = async (db, input, now, retention) => {
12011
12093
  lt: timeRange.end
12012
12094
  };
12013
12095
  const distribution = [];
12014
- for (const platform of reported) distribution.push(...await drain((page) => input.appVersion === void 0 ? db.findAggregates("insights_distribution", {
12015
- index: "byScope",
12016
- where: {
12017
- channel: input.channel,
12018
- platform
12019
- },
12020
- range,
12021
- limit: PAGE,
12022
- ...page
12023
- }) : db.findAggregates("insights_distribution", {
12024
- index: "byVersion",
12025
- where: {
12026
- channel: input.channel,
12027
- platform,
12028
- app_version: input.appVersion
12029
- },
12030
- range,
12031
- limit: PAGE,
12032
- ...page
12033
- })));
12096
+ const builtinDistribution = [];
12097
+ for (const platform of reported) {
12098
+ distribution.push(...await drain((page) => input.appVersion === void 0 ? db.findAggregates("insights_distribution", {
12099
+ index: "byScope",
12100
+ where: {
12101
+ channel: input.channel,
12102
+ platform
12103
+ },
12104
+ range,
12105
+ limit: PAGE,
12106
+ ...page
12107
+ }) : db.findAggregates("insights_distribution", {
12108
+ index: "byVersion",
12109
+ where: {
12110
+ channel: input.channel,
12111
+ platform,
12112
+ app_version: input.appVersion
12113
+ },
12114
+ range,
12115
+ limit: PAGE,
12116
+ ...page
12117
+ })));
12118
+ builtinDistribution.push(...await drain((page) => input.appVersion === void 0 ? db.findAggregates("insights_builtin_distribution", {
12119
+ index: "byScope",
12120
+ where: {
12121
+ channel: input.channel,
12122
+ platform
12123
+ },
12124
+ range,
12125
+ limit: PAGE,
12126
+ ...page
12127
+ }) : db.findAggregates("insights_builtin_distribution", {
12128
+ index: "byVersion",
12129
+ where: {
12130
+ channel: input.channel,
12131
+ platform,
12132
+ app_version: input.appVersion
12133
+ },
12134
+ range,
12135
+ limit: PAGE,
12136
+ ...page
12137
+ })));
12138
+ }
12034
12139
  const points = [];
12035
12140
  for (let start = timeRange.start; start < timeRange.end; start += intervalMs) points.push({
12036
12141
  startMs: start,
@@ -12039,22 +12144,31 @@ const getAppUsage = async (db, input, now, retention) => {
12039
12144
  const versions = /* @__PURE__ */ new Map();
12040
12145
  const platforms = /* @__PURE__ */ new Map();
12041
12146
  const bundles = /* @__PURE__ */ new Map();
12042
- for (const row of distribution) {
12043
- const count = row.latest_installations;
12044
- versions.set(row.app_version, (versions.get(row.app_version) ?? 0) + count);
12045
- platforms.set(row.platform, (platforms.get(row.platform) ?? 0) + count);
12046
- const releaseId = row.release_id === "" ? null : row.release_id;
12147
+ const countBundle = (row, releaseId, builtinBundleId, count) => {
12047
12148
  const key = JSON.stringify([
12048
12149
  row.app_version,
12049
12150
  row.platform,
12050
- releaseId
12151
+ releaseId,
12152
+ builtinBundleId
12051
12153
  ]);
12052
12154
  bundles.set(key, {
12053
12155
  appVersion: row.app_version,
12054
12156
  platform: row.platform,
12055
12157
  releaseId,
12158
+ builtinBundleId,
12056
12159
  installations: (bundles.get(key)?.installations ?? 0) + count
12057
12160
  });
12161
+ };
12162
+ for (const row of distribution) {
12163
+ const count = row.latest_installations;
12164
+ versions.set(row.app_version, (versions.get(row.app_version) ?? 0) + count);
12165
+ platforms.set(row.platform, (platforms.get(row.platform) ?? 0) + count);
12166
+ countBundle(row, row.release_id || null, null, count);
12167
+ }
12168
+ for (const row of builtinDistribution) {
12169
+ const releaseId = row.release_id || null;
12170
+ countBundle(row, releaseId, null, -row.latest_installations);
12171
+ countBundle(row, releaseId, row.builtin_bundle_id, row.latest_installations);
12058
12172
  }
12059
12173
  const sortedVersions = byInstallations(versions);
12060
12174
  return {
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.34",
4
+ "version": "1.0.0-rc.36",
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.30",
49
- "@hot-updater/mock": "1.0.0-rc.30",
50
- "@hot-updater/test-utils": "1.0.0-rc.32"
48
+ "@hot-updater/protocol": "1.0.0-rc.36",
49
+ "@hot-updater/mock": "1.0.0-rc.36",
50
+ "@hot-updater/test-utils": "1.0.0-rc.36"
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.34",
66
- "@hot-updater/plugin-core": "1.0.0-rc.30",
67
- "@hot-updater/server": "1.0.0-rc.34"
65
+ "@hot-updater/cli-tools": "1.0.0-rc.36",
66
+ "@hot-updater/plugin-core": "1.0.0-rc.36",
67
+ "@hot-updater/server": "1.0.0-rc.36"
68
68
  },
69
69
  "scripts": {
70
70
  "build": "tsdown",