@hot-updater/aws 1.0.0-rc.29 → 1.0.0-rc.30
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 +92 -1
- package/dist/index.d.mts +92 -1
- package/dist/lambda/index.cjs +225 -16
- package/package.json +4 -4
package/dist/index.d.cts
CHANGED
|
@@ -224,9 +224,13 @@ interface ReleaseActivityMetrics {
|
|
|
224
224
|
readonly failedLaunches: number;
|
|
225
225
|
/** Omitted for lifetime-only bundle-row reads. */
|
|
226
226
|
readonly uniqueUsers?: number;
|
|
227
|
-
/**
|
|
227
|
+
/**
|
|
228
|
+
* Points of each UTC day, or of each `intervalMs` from the period's start
|
|
229
|
+
* on a release read that asks for one; present only for period reads.
|
|
230
|
+
*/
|
|
228
231
|
readonly series?: readonly {
|
|
229
232
|
readonly startMs: number;
|
|
233
|
+
readonly downloads: number;
|
|
230
234
|
readonly launches: number;
|
|
231
235
|
readonly failedLaunches: number;
|
|
232
236
|
}[];
|
|
@@ -234,10 +238,17 @@ interface ReleaseActivityMetrics {
|
|
|
234
238
|
type InsightsGetReleaseActivityInput = {
|
|
235
239
|
readonly releases: readonly ReleaseReference[];
|
|
236
240
|
readonly timeRange?: InsightsTimeRange;
|
|
241
|
+
/**
|
|
242
|
+
* Whole hours each series point spans, from `timeRange.start`, with
|
|
243
|
+
* every point present; omitted, the series has a point per UTC day
|
|
244
|
+
* with reports. Only with a `timeRange`.
|
|
245
|
+
*/
|
|
246
|
+
readonly intervalMs?: number;
|
|
237
247
|
readonly scope?: never;
|
|
238
248
|
} | {
|
|
239
249
|
readonly scope: InsightsScope;
|
|
240
250
|
readonly timeRange: InsightsTimeRange;
|
|
251
|
+
readonly intervalMs?: never;
|
|
241
252
|
readonly releases?: never;
|
|
242
253
|
};
|
|
243
254
|
interface InsightsGetReleaseActivityResult {
|
|
@@ -280,6 +291,23 @@ interface InsightsGetAppUsageResult {
|
|
|
280
291
|
}[];
|
|
281
292
|
readonly measuredAtMs: number;
|
|
282
293
|
}
|
|
294
|
+
/** Whole UTC days, at most 31, including the current unfinished day. */
|
|
295
|
+
interface InsightsGetDistributionHistoryInput extends InsightsScope {
|
|
296
|
+
readonly timeRange: InsightsTimeRange;
|
|
297
|
+
}
|
|
298
|
+
interface InsightsGetDistributionHistoryResult {
|
|
299
|
+
readonly coverage: InsightsCoverage;
|
|
300
|
+
readonly measuredAtMs: number;
|
|
301
|
+
readonly points: readonly {
|
|
302
|
+
readonly startMs: number;
|
|
303
|
+
readonly bundles: readonly {
|
|
304
|
+
readonly appVersion: string;
|
|
305
|
+
readonly releaseId: string | null;
|
|
306
|
+
readonly bundleKind: "release" | "builtin" | "unknown";
|
|
307
|
+
readonly installations: number;
|
|
308
|
+
}[];
|
|
309
|
+
}[];
|
|
310
|
+
}
|
|
283
311
|
//#endregion
|
|
284
312
|
//#region ../insights/dist/server/schema.d.mts
|
|
285
313
|
//#region src/server/schema.d.ts
|
|
@@ -621,6 +649,68 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
|
|
|
621
649
|
readonly sort: readonly ["bucket_start_ms"];
|
|
622
650
|
};
|
|
623
651
|
}, never, readonly ["channel", "platform", "app_version", "release_id", "bucket_start_ms"]>;
|
|
652
|
+
readonly bundle_daily_heads: import("@hot-updater/plugin-core").TableDefinition<{
|
|
653
|
+
readonly install_id: {
|
|
654
|
+
readonly type: "string";
|
|
655
|
+
readonly maxLength: 255;
|
|
656
|
+
};
|
|
657
|
+
readonly bucket_start_ms: {
|
|
658
|
+
readonly type: "integer";
|
|
659
|
+
};
|
|
660
|
+
readonly id: {
|
|
661
|
+
readonly type: "string";
|
|
662
|
+
readonly maxLength: 36;
|
|
663
|
+
};
|
|
664
|
+
readonly received_at_ms: {
|
|
665
|
+
readonly type: "integer";
|
|
666
|
+
};
|
|
667
|
+
readonly channel: {
|
|
668
|
+
readonly type: "string";
|
|
669
|
+
};
|
|
670
|
+
readonly platform: {
|
|
671
|
+
readonly type: "string";
|
|
672
|
+
readonly maxLength: 16;
|
|
673
|
+
};
|
|
674
|
+
readonly app_version: {
|
|
675
|
+
readonly type: "string";
|
|
676
|
+
};
|
|
677
|
+
readonly release_id: {
|
|
678
|
+
readonly type: "string";
|
|
679
|
+
readonly maxLength: 36;
|
|
680
|
+
};
|
|
681
|
+
readonly bundle_kind: {
|
|
682
|
+
readonly type: "string";
|
|
683
|
+
readonly maxLength: 16;
|
|
684
|
+
};
|
|
685
|
+
}, Readonly<Record<string, import("@hot-updater/plugin-core").FieldType>>, {}, readonly ["install_id", "bucket_start_ms"]>;
|
|
686
|
+
readonly insights_distribution_history: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
687
|
+
readonly channel: {
|
|
688
|
+
readonly type: "string";
|
|
689
|
+
};
|
|
690
|
+
readonly platform: {
|
|
691
|
+
readonly type: "string";
|
|
692
|
+
readonly maxLength: 16;
|
|
693
|
+
};
|
|
694
|
+
readonly app_version: {
|
|
695
|
+
readonly type: "string";
|
|
696
|
+
};
|
|
697
|
+
readonly release_id: {
|
|
698
|
+
readonly type: "string";
|
|
699
|
+
readonly maxLength: 36;
|
|
700
|
+
};
|
|
701
|
+
readonly bundle_kind: {
|
|
702
|
+
readonly type: "string";
|
|
703
|
+
readonly maxLength: 16;
|
|
704
|
+
};
|
|
705
|
+
readonly bucket_start_ms: {
|
|
706
|
+
readonly type: "integer";
|
|
707
|
+
};
|
|
708
|
+
}, "installations", {
|
|
709
|
+
readonly byScope: {
|
|
710
|
+
readonly eq: readonly ["channel", "platform"];
|
|
711
|
+
readonly sort: readonly ["bucket_start_ms"];
|
|
712
|
+
};
|
|
713
|
+
}, never, readonly ["channel", "platform", "app_version", "release_id", "bundle_kind", "bucket_start_ms"]>;
|
|
624
714
|
readonly insights_latest_by_bundle: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
625
715
|
readonly platform: {
|
|
626
716
|
readonly type: "string";
|
|
@@ -717,6 +807,7 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
|
|
|
717
807
|
countLatestEvents: (input: InsightsCountLatestEventsInput) => Promise<number>;
|
|
718
808
|
countEvents: (input: InsightsCountEventsInput) => Promise<number>;
|
|
719
809
|
getReleaseActivity: (input: InsightsGetReleaseActivityInput) => Promise<InsightsGetReleaseActivityResult>;
|
|
810
|
+
getDistributionHistory: (input: InsightsGetDistributionHistoryInput) => Promise<InsightsGetDistributionHistoryResult>;
|
|
720
811
|
getAppUsage: (input: InsightsGetAppUsageInput) => Promise<InsightsGetAppUsageResult>;
|
|
721
812
|
getUpdateFailures: (input: InsightsUpdateFailuresInput) => Promise<InsightsUpdateFailures>;
|
|
722
813
|
};
|
package/dist/index.d.mts
CHANGED
|
@@ -224,9 +224,13 @@ interface ReleaseActivityMetrics {
|
|
|
224
224
|
readonly failedLaunches: number;
|
|
225
225
|
/** Omitted for lifetime-only bundle-row reads. */
|
|
226
226
|
readonly uniqueUsers?: number;
|
|
227
|
-
/**
|
|
227
|
+
/**
|
|
228
|
+
* Points of each UTC day, or of each `intervalMs` from the period's start
|
|
229
|
+
* on a release read that asks for one; present only for period reads.
|
|
230
|
+
*/
|
|
228
231
|
readonly series?: readonly {
|
|
229
232
|
readonly startMs: number;
|
|
233
|
+
readonly downloads: number;
|
|
230
234
|
readonly launches: number;
|
|
231
235
|
readonly failedLaunches: number;
|
|
232
236
|
}[];
|
|
@@ -234,10 +238,17 @@ interface ReleaseActivityMetrics {
|
|
|
234
238
|
type InsightsGetReleaseActivityInput = {
|
|
235
239
|
readonly releases: readonly ReleaseReference[];
|
|
236
240
|
readonly timeRange?: InsightsTimeRange;
|
|
241
|
+
/**
|
|
242
|
+
* Whole hours each series point spans, from `timeRange.start`, with
|
|
243
|
+
* every point present; omitted, the series has a point per UTC day
|
|
244
|
+
* with reports. Only with a `timeRange`.
|
|
245
|
+
*/
|
|
246
|
+
readonly intervalMs?: number;
|
|
237
247
|
readonly scope?: never;
|
|
238
248
|
} | {
|
|
239
249
|
readonly scope: InsightsScope;
|
|
240
250
|
readonly timeRange: InsightsTimeRange;
|
|
251
|
+
readonly intervalMs?: never;
|
|
241
252
|
readonly releases?: never;
|
|
242
253
|
};
|
|
243
254
|
interface InsightsGetReleaseActivityResult {
|
|
@@ -280,6 +291,23 @@ interface InsightsGetAppUsageResult {
|
|
|
280
291
|
}[];
|
|
281
292
|
readonly measuredAtMs: number;
|
|
282
293
|
}
|
|
294
|
+
/** Whole UTC days, at most 31, including the current unfinished day. */
|
|
295
|
+
interface InsightsGetDistributionHistoryInput extends InsightsScope {
|
|
296
|
+
readonly timeRange: InsightsTimeRange;
|
|
297
|
+
}
|
|
298
|
+
interface InsightsGetDistributionHistoryResult {
|
|
299
|
+
readonly coverage: InsightsCoverage;
|
|
300
|
+
readonly measuredAtMs: number;
|
|
301
|
+
readonly points: readonly {
|
|
302
|
+
readonly startMs: number;
|
|
303
|
+
readonly bundles: readonly {
|
|
304
|
+
readonly appVersion: string;
|
|
305
|
+
readonly releaseId: string | null;
|
|
306
|
+
readonly bundleKind: "release" | "builtin" | "unknown";
|
|
307
|
+
readonly installations: number;
|
|
308
|
+
}[];
|
|
309
|
+
}[];
|
|
310
|
+
}
|
|
283
311
|
//#endregion
|
|
284
312
|
//#region ../insights/dist/server/schema.d.mts
|
|
285
313
|
//#region src/server/schema.d.ts
|
|
@@ -621,6 +649,68 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
|
|
|
621
649
|
readonly sort: readonly ["bucket_start_ms"];
|
|
622
650
|
};
|
|
623
651
|
}, never, readonly ["channel", "platform", "app_version", "release_id", "bucket_start_ms"]>;
|
|
652
|
+
readonly bundle_daily_heads: import("@hot-updater/plugin-core").TableDefinition<{
|
|
653
|
+
readonly install_id: {
|
|
654
|
+
readonly type: "string";
|
|
655
|
+
readonly maxLength: 255;
|
|
656
|
+
};
|
|
657
|
+
readonly bucket_start_ms: {
|
|
658
|
+
readonly type: "integer";
|
|
659
|
+
};
|
|
660
|
+
readonly id: {
|
|
661
|
+
readonly type: "string";
|
|
662
|
+
readonly maxLength: 36;
|
|
663
|
+
};
|
|
664
|
+
readonly received_at_ms: {
|
|
665
|
+
readonly type: "integer";
|
|
666
|
+
};
|
|
667
|
+
readonly channel: {
|
|
668
|
+
readonly type: "string";
|
|
669
|
+
};
|
|
670
|
+
readonly platform: {
|
|
671
|
+
readonly type: "string";
|
|
672
|
+
readonly maxLength: 16;
|
|
673
|
+
};
|
|
674
|
+
readonly app_version: {
|
|
675
|
+
readonly type: "string";
|
|
676
|
+
};
|
|
677
|
+
readonly release_id: {
|
|
678
|
+
readonly type: "string";
|
|
679
|
+
readonly maxLength: 36;
|
|
680
|
+
};
|
|
681
|
+
readonly bundle_kind: {
|
|
682
|
+
readonly type: "string";
|
|
683
|
+
readonly maxLength: 16;
|
|
684
|
+
};
|
|
685
|
+
}, Readonly<Record<string, import("@hot-updater/plugin-core").FieldType>>, {}, readonly ["install_id", "bucket_start_ms"]>;
|
|
686
|
+
readonly insights_distribution_history: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
687
|
+
readonly channel: {
|
|
688
|
+
readonly type: "string";
|
|
689
|
+
};
|
|
690
|
+
readonly platform: {
|
|
691
|
+
readonly type: "string";
|
|
692
|
+
readonly maxLength: 16;
|
|
693
|
+
};
|
|
694
|
+
readonly app_version: {
|
|
695
|
+
readonly type: "string";
|
|
696
|
+
};
|
|
697
|
+
readonly release_id: {
|
|
698
|
+
readonly type: "string";
|
|
699
|
+
readonly maxLength: 36;
|
|
700
|
+
};
|
|
701
|
+
readonly bundle_kind: {
|
|
702
|
+
readonly type: "string";
|
|
703
|
+
readonly maxLength: 16;
|
|
704
|
+
};
|
|
705
|
+
readonly bucket_start_ms: {
|
|
706
|
+
readonly type: "integer";
|
|
707
|
+
};
|
|
708
|
+
}, "installations", {
|
|
709
|
+
readonly byScope: {
|
|
710
|
+
readonly eq: readonly ["channel", "platform"];
|
|
711
|
+
readonly sort: readonly ["bucket_start_ms"];
|
|
712
|
+
};
|
|
713
|
+
}, never, readonly ["channel", "platform", "app_version", "release_id", "bundle_kind", "bucket_start_ms"]>;
|
|
624
714
|
readonly insights_latest_by_bundle: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
625
715
|
readonly platform: {
|
|
626
716
|
readonly type: "string";
|
|
@@ -717,6 +807,7 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
|
|
|
717
807
|
countLatestEvents: (input: InsightsCountLatestEventsInput) => Promise<number>;
|
|
718
808
|
countEvents: (input: InsightsCountEventsInput) => Promise<number>;
|
|
719
809
|
getReleaseActivity: (input: InsightsGetReleaseActivityInput) => Promise<InsightsGetReleaseActivityResult>;
|
|
810
|
+
getDistributionHistory: (input: InsightsGetDistributionHistoryInput) => Promise<InsightsGetDistributionHistoryResult>;
|
|
720
811
|
getAppUsage: (input: InsightsGetAppUsageInput) => Promise<InsightsGetAppUsageResult>;
|
|
721
812
|
getUpdateFailures: (input: InsightsUpdateFailuresInput) => Promise<InsightsUpdateFailures>;
|
|
722
813
|
};
|
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.30";
|
|
4574
4574
|
//#endregion
|
|
4575
4575
|
//#region ../../packages/server/dist/handlerVersionRoutes.mjs
|
|
4576
4576
|
/**
|
|
@@ -9986,19 +9986,40 @@ const createValidatedInsightsModel = (model) => ({
|
|
|
9986
9986
|
if (!isRecord$2(input) || !hasOnlyKeys(input, [
|
|
9987
9987
|
"releases",
|
|
9988
9988
|
"scope",
|
|
9989
|
-
"timeRange"
|
|
9989
|
+
"timeRange",
|
|
9990
|
+
"intervalMs"
|
|
9990
9991
|
])) invalidQuery();
|
|
9991
9992
|
const releases = input.releases;
|
|
9992
9993
|
const scope = input.scope;
|
|
9993
9994
|
const timeRange = input.timeRange;
|
|
9994
|
-
const
|
|
9995
|
-
const
|
|
9995
|
+
const intervalMs = input.intervalMs;
|
|
9996
|
+
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));
|
|
9997
|
+
const scopeQuery = releases === void 0 && intervalMs === void 0 && isRecord$2(scope) && hasOnlyKeys(scope, ["platform", "channel"]) && hasScope(scope) && isTimeRange(timeRange);
|
|
9996
9998
|
if (!releaseQuery && !scopeQuery) invalidQuery();
|
|
9997
9999
|
const result = await model.getReleaseActivity(input);
|
|
9998
10000
|
validateAggregateResult(result);
|
|
9999
10001
|
if (!Array.isArray(result.data)) invalidResult();
|
|
10000
10002
|
return result;
|
|
10001
10003
|
},
|
|
10004
|
+
async getDistributionHistory(input) {
|
|
10005
|
+
if (!isRecord$2(input) || !hasOnlyKeys(input, [
|
|
10006
|
+
"channel",
|
|
10007
|
+
"platform",
|
|
10008
|
+
"timeRange"
|
|
10009
|
+
]) || !hasScope(input) || !isTimeRange(input.timeRange) || input.timeRange.start % 864e5 !== 0 || input.timeRange.end % 864e5 !== 0 || input.timeRange.end - input.timeRange.start > 26784e5) invalidQuery();
|
|
10010
|
+
const result = await model.getDistributionHistory(input);
|
|
10011
|
+
validateAggregateResult(result);
|
|
10012
|
+
if (!Array.isArray(result.points)) invalidResult();
|
|
10013
|
+
for (const point of result.points) {
|
|
10014
|
+
if (!isTimestamp(point.startMs) || !Array.isArray(point.bundles)) invalidResult();
|
|
10015
|
+
for (const bundle of point.bundles) if (!isText$1(bundle.appVersion) || bundle.releaseId !== null && !isText$1(bundle.releaseId) || ![
|
|
10016
|
+
"release",
|
|
10017
|
+
"builtin",
|
|
10018
|
+
"unknown"
|
|
10019
|
+
].includes(bundle.bundleKind) || !isTimestamp(bundle.installations)) invalidResult();
|
|
10020
|
+
}
|
|
10021
|
+
return result;
|
|
10022
|
+
},
|
|
10002
10023
|
async getAppUsage(input) {
|
|
10003
10024
|
if (!isRecord$2(input) || !hasOnlyKeys(input, [
|
|
10004
10025
|
"channel",
|
|
@@ -10030,7 +10051,8 @@ const createInsightsModel = (api) => createValidatedInsightsModel({
|
|
|
10030
10051
|
countLatestEvents: api.countLatestEvents,
|
|
10031
10052
|
countEvents: api.countEvents,
|
|
10032
10053
|
getReleaseActivity: api.getReleaseActivity,
|
|
10033
|
-
getAppUsage: api.getAppUsage
|
|
10054
|
+
getAppUsage: api.getAppUsage,
|
|
10055
|
+
getDistributionHistory: api.getDistributionHistory
|
|
10034
10056
|
});
|
|
10035
10057
|
//#endregion
|
|
10036
10058
|
//#region ../insights/dist/server/official.mjs
|
|
@@ -11112,6 +11134,71 @@ const insightsDistribution = (retention) => defineAggregate({
|
|
|
11112
11134
|
},
|
|
11113
11135
|
retention
|
|
11114
11136
|
});
|
|
11137
|
+
/** The last running bundle observed per installation and UTC day. */
|
|
11138
|
+
const bundleDailyHeads = (retention) => defineTable({
|
|
11139
|
+
install_id: {
|
|
11140
|
+
type: "string",
|
|
11141
|
+
maxLength: 255
|
|
11142
|
+
},
|
|
11143
|
+
bucket_start_ms: { type: "integer" },
|
|
11144
|
+
id: {
|
|
11145
|
+
type: "string",
|
|
11146
|
+
maxLength: 36
|
|
11147
|
+
},
|
|
11148
|
+
received_at_ms: { type: "integer" },
|
|
11149
|
+
channel: { type: "string" },
|
|
11150
|
+
platform: {
|
|
11151
|
+
type: "string",
|
|
11152
|
+
maxLength: 16
|
|
11153
|
+
},
|
|
11154
|
+
app_version: { type: "string" },
|
|
11155
|
+
release_id: {
|
|
11156
|
+
type: "string",
|
|
11157
|
+
maxLength: 36
|
|
11158
|
+
},
|
|
11159
|
+
bundle_kind: {
|
|
11160
|
+
type: "string",
|
|
11161
|
+
maxLength: 16
|
|
11162
|
+
}
|
|
11163
|
+
}, {
|
|
11164
|
+
key: ["install_id", "bucket_start_ms"],
|
|
11165
|
+
retention
|
|
11166
|
+
});
|
|
11167
|
+
/** Daily observations stay in their day when an installation reports again. */
|
|
11168
|
+
const insightsDistributionHistory = (retention) => defineAggregate({
|
|
11169
|
+
channel: { type: "string" },
|
|
11170
|
+
platform: {
|
|
11171
|
+
type: "string",
|
|
11172
|
+
maxLength: 16
|
|
11173
|
+
},
|
|
11174
|
+
app_version: { type: "string" },
|
|
11175
|
+
release_id: {
|
|
11176
|
+
type: "string",
|
|
11177
|
+
maxLength: 36
|
|
11178
|
+
},
|
|
11179
|
+
bundle_kind: {
|
|
11180
|
+
type: "string",
|
|
11181
|
+
maxLength: 16
|
|
11182
|
+
},
|
|
11183
|
+
bucket_start_ms: { type: "integer" }
|
|
11184
|
+
}, {
|
|
11185
|
+
key: [
|
|
11186
|
+
"channel",
|
|
11187
|
+
"platform",
|
|
11188
|
+
"app_version",
|
|
11189
|
+
"release_id",
|
|
11190
|
+
"bundle_kind",
|
|
11191
|
+
"bucket_start_ms"
|
|
11192
|
+
],
|
|
11193
|
+
gauges: ["installations"],
|
|
11194
|
+
shards: GAUGE_SHARDS,
|
|
11195
|
+
batched: true,
|
|
11196
|
+
indexes: { byScope: {
|
|
11197
|
+
eq: ["channel", "platform"],
|
|
11198
|
+
sort: ["bucket_start_ms"]
|
|
11199
|
+
} },
|
|
11200
|
+
retention
|
|
11201
|
+
});
|
|
11115
11202
|
/** Latest events by the bundle they came from or went to, bucketed like the distribution. */
|
|
11116
11203
|
const insightsLatestByBundle = (retention) => defineAggregate({
|
|
11117
11204
|
platform: {
|
|
@@ -11231,6 +11318,8 @@ const createInsightsSchema = ({ rawDays, dailyDays } = DEFAULT_INSIGHTS_RETENTIO
|
|
|
11231
11318
|
insights_overview_lifetime: counters(),
|
|
11232
11319
|
insights_sketches_lifetime: lifetimeSketches(),
|
|
11233
11320
|
insights_distribution: insightsDistribution(daily),
|
|
11321
|
+
bundle_daily_heads: bundleDailyHeads(daily),
|
|
11322
|
+
insights_distribution_history: insightsDistributionHistory(daily),
|
|
11234
11323
|
insights_latest_by_bundle: insightsLatestByBundle(daily),
|
|
11235
11324
|
insights_outcomes: insightsOutcomes(hourly),
|
|
11236
11325
|
insights_failures: insightsFailures(hourly)
|
|
@@ -11618,21 +11707,66 @@ const REPEATED_FIELDS = [
|
|
|
11618
11707
|
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]);
|
|
11619
11708
|
/**
|
|
11620
11709
|
* Records one event in one transaction: one batch read of the event and its
|
|
11621
|
-
* installation's
|
|
11710
|
+
* installation's latest and daily heads, then the gauge and sketch rows it
|
|
11711
|
+
* changes and one
|
|
11622
11712
|
* write. On a database that batches aggregates (DynamoDB, Firestore), the
|
|
11623
11713
|
* write holds the event's rows and one log row instead, and a compaction
|
|
11624
11714
|
* reads and writes the aggregate rows. A stored event's id changes nothing
|
|
11625
11715
|
* when repeated, whichever installation sends it, and neither does the id
|
|
11626
11716
|
* of the installation's head. An UNCHANGED report that repeats its head on
|
|
11627
|
-
* the same UTC day
|
|
11717
|
+
* the same UTC day only advances its daily observation. An older event counts in its
|
|
11628
11718
|
* own hour but never replaces the head. An update failure is stored and
|
|
11629
11719
|
* counted, but changes what no installation runs, so it moves no head.
|
|
11630
11720
|
*/
|
|
11631
11721
|
const recordEvent = (db, event) => {
|
|
11632
11722
|
assertBundleEventRow(event);
|
|
11633
11723
|
return db.transaction(async (tx) => {
|
|
11634
|
-
const
|
|
11635
|
-
|
|
11724
|
+
const dailyKey = {
|
|
11725
|
+
install_id: event.install_id,
|
|
11726
|
+
bucket_start_ms: dayOf(event.received_at_ms)
|
|
11727
|
+
};
|
|
11728
|
+
const [existing, previous, daily] = await Promise.all([
|
|
11729
|
+
tx.findOne("bundle_events", { id: event.id }),
|
|
11730
|
+
tx.findOne("bundle_event_heads", { install_id: event.install_id }),
|
|
11731
|
+
event.type === "UPDATE_FAILED" ? null : tx.findOne("bundle_daily_heads", dailyKey)
|
|
11732
|
+
]);
|
|
11733
|
+
if (existing !== null || previous?.id === event.id || daily?.id === event.id) return;
|
|
11734
|
+
if (event.type !== "UPDATE_FAILED" && (daily === null || isNewer(event, daily))) {
|
|
11735
|
+
const downloaded = event.type === "UPDATE_DOWNLOADED";
|
|
11736
|
+
const releaseId = downloaded ? event.from_release_id : event.to_release_id;
|
|
11737
|
+
const bundleId = downloaded ? event.from_bundle_id : event.to_bundle_id;
|
|
11738
|
+
const next = {
|
|
11739
|
+
...dailyKey,
|
|
11740
|
+
id: event.id,
|
|
11741
|
+
received_at_ms: event.received_at_ms,
|
|
11742
|
+
channel: event.channel,
|
|
11743
|
+
platform: event.platform,
|
|
11744
|
+
app_version: event.app_version,
|
|
11745
|
+
release_id: releaseId ?? "",
|
|
11746
|
+
bundle_kind: bundleId === "00000000-0000-0000-0000-000000000000" ? "builtin" : releaseId === null ? "unknown" : "release"
|
|
11747
|
+
};
|
|
11748
|
+
const count = (head, delta) => {
|
|
11749
|
+
tx.aggregate("insights_distribution_history", {
|
|
11750
|
+
channel: head.channel,
|
|
11751
|
+
platform: head.platform,
|
|
11752
|
+
app_version: head.app_version,
|
|
11753
|
+
release_id: head.release_id,
|
|
11754
|
+
bundle_kind: head.bundle_kind,
|
|
11755
|
+
bucket_start_ms: head.bucket_start_ms
|
|
11756
|
+
}, { installations: delta }, { shardBy: head.install_id });
|
|
11757
|
+
};
|
|
11758
|
+
if (daily !== null) {
|
|
11759
|
+
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) {
|
|
11760
|
+
count(daily, -1);
|
|
11761
|
+
count(next, 1);
|
|
11762
|
+
}
|
|
11763
|
+
const { install_id: _, bucket_start_ms: __, ...fields } = next;
|
|
11764
|
+
tx.update("bundle_daily_heads", daily, fields);
|
|
11765
|
+
} else {
|
|
11766
|
+
tx.create("bundle_daily_heads", next);
|
|
11767
|
+
count(next, 1);
|
|
11768
|
+
}
|
|
11769
|
+
}
|
|
11636
11770
|
if (previous !== null && repeatsHead(event, previous)) return;
|
|
11637
11771
|
if (event.type !== "UNCHANGED") {
|
|
11638
11772
|
tx.create("bundle_events", event);
|
|
@@ -11998,19 +12132,30 @@ const lifetimeMetrics = async (db, release) => {
|
|
|
11998
12132
|
failedLaunches: lifetime?.failed_launches ?? 0
|
|
11999
12133
|
};
|
|
12000
12134
|
};
|
|
12001
|
-
/**
|
|
12002
|
-
|
|
12135
|
+
/**
|
|
12136
|
+
* Counters, unique users, and a series over a window: a point per UTC day
|
|
12137
|
+
* with rows, or, with `intervalMs`, every point of that span from the
|
|
12138
|
+
* window's start.
|
|
12139
|
+
*/
|
|
12140
|
+
const rangedMetrics = async (db, parts, users, range, days, hour, intervalMs) => {
|
|
12003
12141
|
const [counters, sketches] = await Promise.all([counterRows(db, parts, range, days, hour), sketchRows(db, users.parts, range, days, hour)]);
|
|
12004
12142
|
const series = /* @__PURE__ */ new Map();
|
|
12143
|
+
if (intervalMs !== void 0) for (let start = range.start; start < range.end; start += intervalMs) series.set(start, {
|
|
12144
|
+
downloads: 0,
|
|
12145
|
+
launches: 0,
|
|
12146
|
+
failedLaunches: 0
|
|
12147
|
+
});
|
|
12005
12148
|
for (const row of counters) {
|
|
12006
|
-
const
|
|
12007
|
-
const point = series.get(
|
|
12149
|
+
const start = intervalMs === void 0 ? dayFloor(row.bucket_start_ms) : row.bucket_start_ms - (row.bucket_start_ms - range.start) % intervalMs;
|
|
12150
|
+
const point = series.get(start) ?? {
|
|
12151
|
+
downloads: 0,
|
|
12008
12152
|
launches: 0,
|
|
12009
12153
|
failedLaunches: 0
|
|
12010
12154
|
};
|
|
12155
|
+
point.downloads += row.downloads;
|
|
12011
12156
|
point.launches += row.launches;
|
|
12012
12157
|
point.failedLaunches += row.failed_launches;
|
|
12013
|
-
series.set(
|
|
12158
|
+
series.set(start, point);
|
|
12014
12159
|
}
|
|
12015
12160
|
const total = (field) => counters.reduce((sum, row) => sum + row[field], 0);
|
|
12016
12161
|
return {
|
|
@@ -12056,7 +12201,7 @@ const getReleaseActivity = async (db, input, now, retention) => {
|
|
|
12056
12201
|
metrics: input.timeRange === void 0 ? await lifetimeMetrics(db, release) : await rangedMetrics(db, releaseParts(release), {
|
|
12057
12202
|
parts: releaseParts(release),
|
|
12058
12203
|
field: "launch_users"
|
|
12059
|
-
}, input.timeRange, false, kept.hour)
|
|
12204
|
+
}, input.timeRange, false, kept.hour, input.intervalMs)
|
|
12060
12205
|
})));
|
|
12061
12206
|
const oldest = kept[input.scope === void 0 ? "hour" : "day"];
|
|
12062
12207
|
return {
|
|
@@ -12322,6 +12467,43 @@ const getUpdateFailures = async (db, input, now, retention) => {
|
|
|
12322
12467
|
}
|
|
12323
12468
|
};
|
|
12324
12469
|
};
|
|
12470
|
+
/** Reads daily gauges, never raw events or mutable latest-state distribution. */
|
|
12471
|
+
const getDistributionHistory = async (db, input, now, retention) => {
|
|
12472
|
+
const at = now();
|
|
12473
|
+
const { start, end } = input.timeRange;
|
|
12474
|
+
const points = /* @__PURE__ */ new Map();
|
|
12475
|
+
for (let day = start; day < end; day += DAY_MS) points.set(day, []);
|
|
12476
|
+
const rows = await drain((page) => db.findAggregates("insights_distribution_history", {
|
|
12477
|
+
index: "byScope",
|
|
12478
|
+
where: {
|
|
12479
|
+
channel: input.channel,
|
|
12480
|
+
platform: input.platform
|
|
12481
|
+
},
|
|
12482
|
+
range: {
|
|
12483
|
+
gte: start,
|
|
12484
|
+
lt: end
|
|
12485
|
+
},
|
|
12486
|
+
limit: PAGE,
|
|
12487
|
+
...page
|
|
12488
|
+
}));
|
|
12489
|
+
for (const row of rows) {
|
|
12490
|
+
if (row.installations <= 0) continue;
|
|
12491
|
+
points.get(row.bucket_start_ms).push({
|
|
12492
|
+
appVersion: row.app_version,
|
|
12493
|
+
releaseId: row.release_id || null,
|
|
12494
|
+
bundleKind: row.bundle_kind,
|
|
12495
|
+
installations: row.installations
|
|
12496
|
+
});
|
|
12497
|
+
}
|
|
12498
|
+
return {
|
|
12499
|
+
coverage: coverageOf(start, retained(at, retention).day),
|
|
12500
|
+
measuredAtMs: at,
|
|
12501
|
+
points: [...points].map(([startMs, bundles]) => ({
|
|
12502
|
+
startMs,
|
|
12503
|
+
bundles
|
|
12504
|
+
}))
|
|
12505
|
+
};
|
|
12506
|
+
};
|
|
12325
12507
|
//#endregion
|
|
12326
12508
|
//#region ../insights/dist/server/queryInput.mjs
|
|
12327
12509
|
const MAX_PAGE_LIMIT = 100;
|
|
@@ -12513,6 +12695,7 @@ const createInsightsApi = (db, now, retention) => ({
|
|
|
12513
12695
|
countLatestEvents: (input) => countLatestEvents(db, input),
|
|
12514
12696
|
countEvents: (input) => countEvents(db, input),
|
|
12515
12697
|
getReleaseActivity: (input) => getReleaseActivity(db, input, now, retention),
|
|
12698
|
+
getDistributionHistory: (input) => getDistributionHistory(db, input, now, retention),
|
|
12516
12699
|
getAppUsage: (input) => getAppUsage(db, input, now, retention),
|
|
12517
12700
|
/** A release's or a channel's update failures, and their breakdown over a time range. */
|
|
12518
12701
|
getUpdateFailures: (input) => getUpdateFailures(db, input, now, retention)
|
|
@@ -12581,7 +12764,7 @@ const insights = (options = {}) => {
|
|
|
12581
12764
|
const plugin = definePlugin({
|
|
12582
12765
|
id: "insights",
|
|
12583
12766
|
namespace: false,
|
|
12584
|
-
schemaVersion: "1.
|
|
12767
|
+
schemaVersion: "1.3.0",
|
|
12585
12768
|
schema: createInsightsSchema(retention),
|
|
12586
12769
|
init: ({ db, now }) => {
|
|
12587
12770
|
const api = createInsightsApi(db, now, retention);
|
|
@@ -12596,6 +12779,32 @@ const insights = (options = {}) => {
|
|
|
12596
12779
|
handler: (request, params) => routes[handler](params, request)
|
|
12597
12780
|
})),
|
|
12598
12781
|
failuresEndpoint(api),
|
|
12782
|
+
{
|
|
12783
|
+
access: "admin",
|
|
12784
|
+
method: "GET",
|
|
12785
|
+
path: "/distribution-history",
|
|
12786
|
+
handler: async (request) => {
|
|
12787
|
+
const query = new URL(request.url).searchParams;
|
|
12788
|
+
try {
|
|
12789
|
+
const result = await createInsightsModel(api).getDistributionHistory({
|
|
12790
|
+
platform: query.get("platform"),
|
|
12791
|
+
channel: query.get("channel") ?? "",
|
|
12792
|
+
timeRange: {
|
|
12793
|
+
start: Number(query.get("start") ?? NaN),
|
|
12794
|
+
end: Number(query.get("end") ?? NaN)
|
|
12795
|
+
}
|
|
12796
|
+
});
|
|
12797
|
+
return Response.json(result, { headers: { "cache-control": "private, no-store" } });
|
|
12798
|
+
} catch (error) {
|
|
12799
|
+
if (error instanceof DatabaseAdapterInputError) return Response.json({ error: "Choose a platform, channel, and up to 31 whole UTC days." }, { status: 400 });
|
|
12800
|
+
if (!isDatabaseBusyError(error)) throw error;
|
|
12801
|
+
return Response.json({ error: "Service unavailable" }, {
|
|
12802
|
+
status: 503,
|
|
12803
|
+
headers: { "retry-after": "5" }
|
|
12804
|
+
});
|
|
12805
|
+
}
|
|
12806
|
+
}
|
|
12807
|
+
},
|
|
12599
12808
|
{
|
|
12600
12809
|
access: "admin",
|
|
12601
12810
|
method: "GET",
|
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.30",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
7
7
|
"module": "dist/index.mjs",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"mime": "^4.0.4",
|
|
48
48
|
"@hot-updater/protocol": "1.0.0-rc.28",
|
|
49
49
|
"@hot-updater/mock": "1.0.0-rc.28",
|
|
50
|
-
"@hot-updater/test-utils": "1.0.0-rc.
|
|
50
|
+
"@hot-updater/test-utils": "1.0.0-rc.29"
|
|
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.
|
|
65
|
+
"@hot-updater/cli-tools": "1.0.0-rc.30",
|
|
66
66
|
"@hot-updater/plugin-core": "1.0.0-rc.28",
|
|
67
|
-
"@hot-updater/server": "1.0.0-rc.
|
|
67
|
+
"@hot-updater/server": "1.0.0-rc.30"
|
|
68
68
|
},
|
|
69
69
|
"scripts": {
|
|
70
70
|
"build": "tsdown",
|