@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 +7 -19
- package/dist/index.d.mts +7 -19
- package/dist/lambda/index.cjs +97 -147
- package/package.json +7 -7
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
|
-
|
|
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" | "
|
|
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" | "
|
|
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" | "
|
|
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
|
-
|
|
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" | "
|
|
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" | "
|
|
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" | "
|
|
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/lambda/index.cjs
CHANGED
|
@@ -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.
|
|
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)
|
|
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")
|
|
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.
|
|
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
|
|
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":
|
|
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":
|
|
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
|
|
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
|
-
*
|
|
10972
|
-
*
|
|
10973
|
-
*
|
|
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
|
-
|
|
10983
|
-
"
|
|
10984
|
-
"
|
|
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,
|
|
11017
|
-
*
|
|
11018
|
-
*
|
|
11019
|
-
*
|
|
11020
|
-
*
|
|
11021
|
-
*
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
11286
|
+
applies: 0,
|
|
11302
11287
|
failedLaunches: 0
|
|
11303
11288
|
});
|
|
11304
|
-
else if (event.type
|
|
11305
|
-
|
|
11306
|
-
|
|
11307
|
-
|
|
11308
|
-
|
|
11309
|
-
|
|
11310
|
-
|
|
11311
|
-
|
|
11312
|
-
|
|
11313
|
-
|
|
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
|
-
|
|
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
|
-
["
|
|
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
|
-
|
|
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
|
-
/**
|
|
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
|
-
|
|
12148
|
-
|
|
12149
|
-
|
|
12150
|
-
|
|
12151
|
-
|
|
12152
|
-
|
|
12153
|
-
|
|
12154
|
-
|
|
12155
|
-
|
|
12156
|
-
|
|
12157
|
-
|
|
12158
|
-
|
|
12159
|
-
|
|
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,
|
|
12175
|
-
*
|
|
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 =
|
|
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"
|
|
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.
|
|
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.
|
|
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.
|
|
49
|
-
"@hot-updater/mock": "1.0.0-rc.
|
|
50
|
-
"@hot-updater/test-utils": "1.0.0-rc.
|
|
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.
|
|
66
|
-
"@hot-updater/plugin-core": "1.0.0-rc.
|
|
67
|
-
"@hot-updater/server": "1.0.0-rc.
|
|
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",
|