@hot-updater/plugin-insights 1.0.0-rc.29 → 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.
- package/dist/server/contract.cjs +15 -13
- package/dist/server/contract.mjs +15 -13
- package/dist/server/index.cjs +2 -1
- package/dist/server/index.d.cts +6 -8
- package/dist/server/index.d.mts +6 -8
- package/dist/server/index.mjs +3 -2
- package/dist/server/model.cjs +1 -0
- package/dist/server/model.mjs +1 -0
- package/dist/server/modelTypes.d.cts +28 -21
- package/dist/server/modelTypes.d.mts +28 -21
- package/dist/server/overview.cjs +6 -10
- package/dist/server/overview.mjs +6 -10
- package/dist/server/reads.cjs +34 -55
- package/dist/server/reads.mjs +34 -56
- package/dist/server/recordEvent.cjs +5 -10
- package/dist/server/recordEvent.mjs +5 -10
- package/dist/server/schema.cjs +9 -20
- package/dist/server/schema.d.cts +4 -12
- package/dist/server/schema.d.mts +4 -12
- package/dist/server/schema.mjs +9 -20
- package/package.json +2 -2
package/dist/server/contract.cjs
CHANGED
|
@@ -150,21 +150,23 @@ const createValidatedInsightsModel = (model) => ({
|
|
|
150
150
|
]) || !isBundleFilter(input.filter) || !isTimestamp(input.sinceMs) || !isTimestamp(input.beforeReceivedAtMs) || input.sinceMs > input.beforeReceivedAtMs) invalidQuery();
|
|
151
151
|
return validateCount(await model.countEvents(input));
|
|
152
152
|
},
|
|
153
|
-
async
|
|
153
|
+
async countEventSeries(input) {
|
|
154
154
|
if (!require_eventRow.isRecord(input) || !hasOnlyKeys(input, [
|
|
155
|
-
"
|
|
156
|
-
"
|
|
157
|
-
"
|
|
158
|
-
])) invalidQuery();
|
|
159
|
-
const
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
155
|
+
"filter",
|
|
156
|
+
"timeRange",
|
|
157
|
+
"intervalMs"
|
|
158
|
+
]) || !isBundleFilter(input.filter) || !isTimeRange(input.timeRange) || !isTimestamp(input.intervalMs)) invalidQuery();
|
|
159
|
+
const { start, end } = input.timeRange;
|
|
160
|
+
const { intervalMs } = input;
|
|
161
|
+
if (intervalMs === 0 || intervalMs % 36e5 !== 0 || start % 36e5 !== 0 || (end - start) % intervalMs !== 0 || end - start > 7776e6) invalidQuery();
|
|
162
|
+
const points = await model.countEventSeries(input);
|
|
163
|
+
if (!Array.isArray(points) || points.length !== (end - start) / intervalMs || points.some((point, index) => !require_eventRow.isRecord(point) || !hasOnlyKeys(point, ["startMs", "events"]) || point.startMs !== start + index * intervalMs || !isTimestamp(point.events))) invalidResult();
|
|
164
|
+
return points;
|
|
165
|
+
},
|
|
166
|
+
async getReleaseActivity(input) {
|
|
167
|
+
if (!require_eventRow.isRecord(input) || !hasOnlyKeys(input, ["releases"]) || !Array.isArray(input.releases) || input.releases.length < 1 || input.releases.length > 100 || !input.releases.every(isReleaseReference)) invalidQuery();
|
|
165
168
|
const result = await model.getReleaseActivity(input);
|
|
166
|
-
|
|
167
|
-
if (!Array.isArray(result.data)) invalidResult();
|
|
169
|
+
if (!require_eventRow.isRecord(result) || !isTimestamp(result.measuredAtMs) || !Array.isArray(result.data) || result.data.some((item) => !require_eventRow.isRecord(item) || !isReleaseReference(item.release) || !require_eventRow.isRecord(item.metrics) || !isTimestamp(item.metrics.downloads) || !isTimestamp(item.metrics.launches) || !isTimestamp(item.metrics.failedLaunches))) invalidResult();
|
|
168
170
|
return result;
|
|
169
171
|
},
|
|
170
172
|
async getAppUsage(input) {
|
package/dist/server/contract.mjs
CHANGED
|
@@ -150,21 +150,23 @@ const createValidatedInsightsModel = (model) => ({
|
|
|
150
150
|
]) || !isBundleFilter(input.filter) || !isTimestamp(input.sinceMs) || !isTimestamp(input.beforeReceivedAtMs) || input.sinceMs > input.beforeReceivedAtMs) invalidQuery();
|
|
151
151
|
return validateCount(await model.countEvents(input));
|
|
152
152
|
},
|
|
153
|
-
async
|
|
153
|
+
async countEventSeries(input) {
|
|
154
154
|
if (!isRecord(input) || !hasOnlyKeys(input, [
|
|
155
|
-
"
|
|
156
|
-
"
|
|
157
|
-
"
|
|
158
|
-
])) invalidQuery();
|
|
159
|
-
const
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
155
|
+
"filter",
|
|
156
|
+
"timeRange",
|
|
157
|
+
"intervalMs"
|
|
158
|
+
]) || !isBundleFilter(input.filter) || !isTimeRange(input.timeRange) || !isTimestamp(input.intervalMs)) invalidQuery();
|
|
159
|
+
const { start, end } = input.timeRange;
|
|
160
|
+
const { intervalMs } = input;
|
|
161
|
+
if (intervalMs === 0 || intervalMs % 36e5 !== 0 || start % 36e5 !== 0 || (end - start) % intervalMs !== 0 || end - start > 7776e6) invalidQuery();
|
|
162
|
+
const points = await model.countEventSeries(input);
|
|
163
|
+
if (!Array.isArray(points) || points.length !== (end - start) / intervalMs || points.some((point, index) => !isRecord(point) || !hasOnlyKeys(point, ["startMs", "events"]) || point.startMs !== start + index * intervalMs || !isTimestamp(point.events))) invalidResult();
|
|
164
|
+
return points;
|
|
165
|
+
},
|
|
166
|
+
async getReleaseActivity(input) {
|
|
167
|
+
if (!isRecord(input) || !hasOnlyKeys(input, ["releases"]) || !Array.isArray(input.releases) || input.releases.length < 1 || input.releases.length > 100 || !input.releases.every(isReleaseReference)) invalidQuery();
|
|
165
168
|
const result = await model.getReleaseActivity(input);
|
|
166
|
-
|
|
167
|
-
if (!Array.isArray(result.data)) invalidResult();
|
|
169
|
+
if (!isRecord(result) || !isTimestamp(result.measuredAtMs) || !Array.isArray(result.data) || result.data.some((item) => !isRecord(item) || !isReleaseReference(item.release) || !isRecord(item.metrics) || !isTimestamp(item.metrics.downloads) || !isTimestamp(item.metrics.launches) || !isTimestamp(item.metrics.failedLaunches))) invalidResult();
|
|
168
170
|
return result;
|
|
169
171
|
},
|
|
170
172
|
async getAppUsage(input) {
|
package/dist/server/index.cjs
CHANGED
|
@@ -18,7 +18,8 @@ const createInsightsApi = (db, now, retention) => ({
|
|
|
18
18
|
findLatestEvents: (input) => require_reads.findLatestEvents(db, input),
|
|
19
19
|
countLatestEvents: (input) => require_reads.countLatestEvents(db, input),
|
|
20
20
|
countEvents: (input) => require_reads.countEvents(db, input),
|
|
21
|
-
|
|
21
|
+
countEventSeries: (input) => require_reads.countEventSeries(db, input),
|
|
22
|
+
getReleaseActivity: (input) => require_reads.getReleaseActivity(db, input, now),
|
|
22
23
|
getAppUsage: (input) => require_reads.getAppUsage(db, input, now, retention),
|
|
23
24
|
/** A release's or a channel's update failures, and their breakdown over a time range. */
|
|
24
25
|
getUpdateFailures: (input) => require_reads.getUpdateFailures(db, input, now, retention)
|
package/dist/server/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BundleEventFailure, BundleEventFailureReason, BundleEventFailureStage, BundleEventRow, BundleEventRowBase, DatabaseBundleEventMetadata } from "./eventRow.cjs";
|
|
2
|
-
import { InsightsBundleEventFilter, InsightsCountEventsInput, InsightsCountLatestEventsInput, InsightsCoverage, InsightsEventCursor, InsightsEventFilter, InsightsFindLatestEventsInput, InsightsGetAppUsageInput, InsightsGetAppUsageResult, InsightsGetReleaseActivityInput, InsightsGetReleaseActivityResult, InsightsListEventsInput, InsightsModel, InsightsRecordEventInput, InsightsScope, InsightsTimeRange, ReleaseActivityMetrics, ReleaseReference } from "./modelTypes.cjs";
|
|
2
|
+
import { InsightsBundleEventFilter, InsightsCountEventSeriesInput, InsightsCountEventsInput, InsightsCountLatestEventsInput, InsightsCoverage, InsightsEventCursor, InsightsEventFilter, InsightsEventSeriesPoint, InsightsFindLatestEventsInput, InsightsGetAppUsageInput, InsightsGetAppUsageResult, InsightsGetReleaseActivityInput, InsightsGetReleaseActivityResult, InsightsListEventsInput, InsightsModel, InsightsRecordEventInput, InsightsScope, InsightsTimeRange, ReleaseActivityMetrics, ReleaseReference } from "./modelTypes.cjs";
|
|
3
3
|
import { DEFAULT_INSIGHTS_RETENTION, InsightsRetention, InsightsSchema, createInsightsSchema, insightsSchema } from "./schema.cjs";
|
|
4
4
|
import { InsightsFailureBreakdown, InsightsUpdateFailures, InsightsUpdateFailuresInput } from "./reads.cjs";
|
|
5
5
|
import { ActiveInstallationWindow, BundleEventFailureInput, CreateBundleEventRequest, CreateBundleEventRequestBase, CursorPage, EventCursorPage, EventHistoryRow, InsightsBundleSelection, InsightsCountMeasurement, InsightsHttpResponse, InstallationHistoryRow, InstallationRow, ReportingOverview } from "./domain.cjs";
|
|
@@ -19,6 +19,7 @@ declare const createInsightsApi: (db: HotUpdaterDatabase<InsightsSchema>, now: (
|
|
|
19
19
|
findLatestEvents: (input: InsightsFindLatestEventsInput) => Promise<readonly BundleEventRow[]>;
|
|
20
20
|
countLatestEvents: (input: InsightsCountLatestEventsInput) => Promise<number>;
|
|
21
21
|
countEvents: (input: InsightsCountEventsInput) => Promise<number>;
|
|
22
|
+
countEventSeries: (input: InsightsCountEventSeriesInput) => Promise<readonly InsightsEventSeriesPoint[]>;
|
|
22
23
|
getReleaseActivity: (input: InsightsGetReleaseActivityInput) => Promise<InsightsGetReleaseActivityResult>;
|
|
23
24
|
getAppUsage: (input: InsightsGetAppUsageInput) => Promise<InsightsGetAppUsageResult>;
|
|
24
25
|
/** A release's or a channel's update failures, and their breakdown over a time range. */
|
|
@@ -99,10 +100,6 @@ declare const insights: (options?: InsightsOptions) => import("@hot-updater/plug
|
|
|
99
100
|
movement_install_id: "string";
|
|
100
101
|
bundle_ref: "string";
|
|
101
102
|
}, {
|
|
102
|
-
readonly recent: {
|
|
103
|
-
readonly eq: readonly ["channel", "platform", "day"];
|
|
104
|
-
readonly sort: readonly ["received_at_ms"];
|
|
105
|
-
};
|
|
106
103
|
readonly movementsByInstall: {
|
|
107
104
|
readonly eq: readonly ["movement_install_id"];
|
|
108
105
|
readonly sort: readonly ["received_at_ms"];
|
|
@@ -202,7 +199,7 @@ declare const insights: (options?: InsightsOptions) => import("@hot-updater/plug
|
|
|
202
199
|
readonly eq: readonly ["identity"];
|
|
203
200
|
readonly sort: readonly ["bucket_start_ms"];
|
|
204
201
|
};
|
|
205
|
-
}, "
|
|
202
|
+
}, "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
|
|
206
203
|
readonly insights_overview_daily: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
207
204
|
readonly identity: {
|
|
208
205
|
readonly type: "string";
|
|
@@ -230,7 +227,7 @@ declare const insights: (options?: InsightsOptions) => import("@hot-updater/plug
|
|
|
230
227
|
readonly eq: readonly ["identity"];
|
|
231
228
|
readonly sort: readonly ["bucket_start_ms"];
|
|
232
229
|
};
|
|
233
|
-
}, "
|
|
230
|
+
}, "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
|
|
234
231
|
readonly insights_overview_lifetime: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
235
232
|
readonly identity: {
|
|
236
233
|
readonly type: "string";
|
|
@@ -384,6 +381,7 @@ declare const insights: (options?: InsightsOptions) => import("@hot-updater/plug
|
|
|
384
381
|
findLatestEvents: (input: InsightsFindLatestEventsInput) => Promise<readonly BundleEventRow[]>;
|
|
385
382
|
countLatestEvents: (input: InsightsCountLatestEventsInput) => Promise<number>;
|
|
386
383
|
countEvents: (input: InsightsCountEventsInput) => Promise<number>;
|
|
384
|
+
countEventSeries: (input: InsightsCountEventSeriesInput) => Promise<readonly InsightsEventSeriesPoint[]>;
|
|
387
385
|
getReleaseActivity: (input: InsightsGetReleaseActivityInput) => Promise<InsightsGetReleaseActivityResult>;
|
|
388
386
|
getAppUsage: (input: InsightsGetAppUsageInput) => Promise<InsightsGetAppUsageResult>;
|
|
389
387
|
/** A release's or a channel's update failures, and their breakdown over a time range. */
|
|
@@ -392,4 +390,4 @@ declare const insights: (options?: InsightsOptions) => import("@hot-updater/plug
|
|
|
392
390
|
endpoints: PluginEndpoint[];
|
|
393
391
|
}, {}>;
|
|
394
392
|
//#endregion
|
|
395
|
-
export { type ActiveInstallationWindow, type BundleEventFailure, type BundleEventFailureInput, type BundleEventFailureReason, type BundleEventFailureStage, type BundleEventRow, type BundleEventRowBase, type CreateBundleEventRequest, type CreateBundleEventRequestBase, type CursorPage, DEFAULT_INSIGHTS_RETENTION, type DatabaseBundleEventMetadata, type EventCursorPage, type EventHistoryRow, InsightsApi, InsightsBadRequestError, type InsightsBundleEventFilter, type InsightsBundleSelection, type InsightsCountEventsInput, type InsightsCountLatestEventsInput, type InsightsCountMeasurement, type InsightsCoverage, type InsightsEventCursor, type InsightsEventFilter, type InsightsEventPageInput, type InsightsFailureBreakdown, type InsightsFindLatestEventsInput, type InsightsGetAppUsageInput, type InsightsGetAppUsageResult, type InsightsGetReleaseActivityInput, type InsightsGetReleaseActivityResult, type InsightsHttpResponse, type InsightsIdentityParts, type InsightsInstallationEventPageInput, type InsightsListEventsInput, type InsightsModel, InsightsOptions, type InsightsProvider, type InsightsRecordEventInput, type InsightsRetention, type InsightsSchema, type InsightsScope, type InsightsTimeRange, type InsightsUpdateFailures, type InsightsUpdateFailuresInput, type InsightsUserInstallationPageInput, type InstallationHistoryRow, type InstallationRow, type ReleaseActivityMetrics, type ReleaseReference, type ReportingOverview, createInsightsModel, createInsightsProvider, createInsightsSchema, insights, insightsIdentity, insightsSchema };
|
|
393
|
+
export { type ActiveInstallationWindow, type BundleEventFailure, type BundleEventFailureInput, type BundleEventFailureReason, type BundleEventFailureStage, type BundleEventRow, type BundleEventRowBase, type CreateBundleEventRequest, type CreateBundleEventRequestBase, type CursorPage, DEFAULT_INSIGHTS_RETENTION, type DatabaseBundleEventMetadata, type EventCursorPage, type EventHistoryRow, InsightsApi, InsightsBadRequestError, type InsightsBundleEventFilter, type InsightsBundleSelection, type InsightsCountEventSeriesInput, type InsightsCountEventsInput, type InsightsCountLatestEventsInput, type InsightsCountMeasurement, type InsightsCoverage, type InsightsEventCursor, type InsightsEventFilter, type InsightsEventPageInput, type InsightsEventSeriesPoint, type InsightsFailureBreakdown, type InsightsFindLatestEventsInput, type InsightsGetAppUsageInput, type InsightsGetAppUsageResult, type InsightsGetReleaseActivityInput, type InsightsGetReleaseActivityResult, type InsightsHttpResponse, type InsightsIdentityParts, type InsightsInstallationEventPageInput, type InsightsListEventsInput, type InsightsModel, InsightsOptions, type InsightsProvider, type InsightsRecordEventInput, type InsightsRetention, type InsightsSchema, type InsightsScope, type InsightsTimeRange, type InsightsUpdateFailures, type InsightsUpdateFailuresInput, type InsightsUserInstallationPageInput, type InstallationHistoryRow, type InstallationRow, type ReleaseActivityMetrics, type ReleaseReference, type ReportingOverview, createInsightsModel, createInsightsProvider, createInsightsSchema, insights, insightsIdentity, insightsSchema };
|
package/dist/server/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BundleEventFailure, BundleEventFailureReason, BundleEventFailureStage, BundleEventRow, BundleEventRowBase, DatabaseBundleEventMetadata } from "./eventRow.mjs";
|
|
2
|
-
import { InsightsBundleEventFilter, InsightsCountEventsInput, InsightsCountLatestEventsInput, InsightsCoverage, InsightsEventCursor, InsightsEventFilter, InsightsFindLatestEventsInput, InsightsGetAppUsageInput, InsightsGetAppUsageResult, InsightsGetReleaseActivityInput, InsightsGetReleaseActivityResult, InsightsListEventsInput, InsightsModel, InsightsRecordEventInput, InsightsScope, InsightsTimeRange, ReleaseActivityMetrics, ReleaseReference } from "./modelTypes.mjs";
|
|
2
|
+
import { InsightsBundleEventFilter, InsightsCountEventSeriesInput, InsightsCountEventsInput, InsightsCountLatestEventsInput, InsightsCoverage, InsightsEventCursor, InsightsEventFilter, InsightsEventSeriesPoint, InsightsFindLatestEventsInput, InsightsGetAppUsageInput, InsightsGetAppUsageResult, InsightsGetReleaseActivityInput, InsightsGetReleaseActivityResult, InsightsListEventsInput, InsightsModel, InsightsRecordEventInput, InsightsScope, InsightsTimeRange, ReleaseActivityMetrics, ReleaseReference } from "./modelTypes.mjs";
|
|
3
3
|
import { DEFAULT_INSIGHTS_RETENTION, InsightsRetention, InsightsSchema, createInsightsSchema, insightsSchema } from "./schema.mjs";
|
|
4
4
|
import { InsightsFailureBreakdown, InsightsUpdateFailures, InsightsUpdateFailuresInput } from "./reads.mjs";
|
|
5
5
|
import { ActiveInstallationWindow, BundleEventFailureInput, CreateBundleEventRequest, CreateBundleEventRequestBase, CursorPage, EventCursorPage, EventHistoryRow, InsightsBundleSelection, InsightsCountMeasurement, InsightsHttpResponse, InstallationHistoryRow, InstallationRow, ReportingOverview } from "./domain.mjs";
|
|
@@ -19,6 +19,7 @@ declare const createInsightsApi: (db: HotUpdaterDatabase<InsightsSchema>, now: (
|
|
|
19
19
|
findLatestEvents: (input: InsightsFindLatestEventsInput) => Promise<readonly BundleEventRow[]>;
|
|
20
20
|
countLatestEvents: (input: InsightsCountLatestEventsInput) => Promise<number>;
|
|
21
21
|
countEvents: (input: InsightsCountEventsInput) => Promise<number>;
|
|
22
|
+
countEventSeries: (input: InsightsCountEventSeriesInput) => Promise<readonly InsightsEventSeriesPoint[]>;
|
|
22
23
|
getReleaseActivity: (input: InsightsGetReleaseActivityInput) => Promise<InsightsGetReleaseActivityResult>;
|
|
23
24
|
getAppUsage: (input: InsightsGetAppUsageInput) => Promise<InsightsGetAppUsageResult>;
|
|
24
25
|
/** A release's or a channel's update failures, and their breakdown over a time range. */
|
|
@@ -99,10 +100,6 @@ declare const insights: (options?: InsightsOptions) => import("@hot-updater/plug
|
|
|
99
100
|
movement_install_id: "string";
|
|
100
101
|
bundle_ref: "string";
|
|
101
102
|
}, {
|
|
102
|
-
readonly recent: {
|
|
103
|
-
readonly eq: readonly ["channel", "platform", "day"];
|
|
104
|
-
readonly sort: readonly ["received_at_ms"];
|
|
105
|
-
};
|
|
106
103
|
readonly movementsByInstall: {
|
|
107
104
|
readonly eq: readonly ["movement_install_id"];
|
|
108
105
|
readonly sort: readonly ["received_at_ms"];
|
|
@@ -202,7 +199,7 @@ declare const insights: (options?: InsightsOptions) => import("@hot-updater/plug
|
|
|
202
199
|
readonly eq: readonly ["identity"];
|
|
203
200
|
readonly sort: readonly ["bucket_start_ms"];
|
|
204
201
|
};
|
|
205
|
-
}, "
|
|
202
|
+
}, "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
|
|
206
203
|
readonly insights_overview_daily: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
207
204
|
readonly identity: {
|
|
208
205
|
readonly type: "string";
|
|
@@ -230,7 +227,7 @@ declare const insights: (options?: InsightsOptions) => import("@hot-updater/plug
|
|
|
230
227
|
readonly eq: readonly ["identity"];
|
|
231
228
|
readonly sort: readonly ["bucket_start_ms"];
|
|
232
229
|
};
|
|
233
|
-
}, "
|
|
230
|
+
}, "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
|
|
234
231
|
readonly insights_overview_lifetime: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
235
232
|
readonly identity: {
|
|
236
233
|
readonly type: "string";
|
|
@@ -384,6 +381,7 @@ declare const insights: (options?: InsightsOptions) => import("@hot-updater/plug
|
|
|
384
381
|
findLatestEvents: (input: InsightsFindLatestEventsInput) => Promise<readonly BundleEventRow[]>;
|
|
385
382
|
countLatestEvents: (input: InsightsCountLatestEventsInput) => Promise<number>;
|
|
386
383
|
countEvents: (input: InsightsCountEventsInput) => Promise<number>;
|
|
384
|
+
countEventSeries: (input: InsightsCountEventSeriesInput) => Promise<readonly InsightsEventSeriesPoint[]>;
|
|
387
385
|
getReleaseActivity: (input: InsightsGetReleaseActivityInput) => Promise<InsightsGetReleaseActivityResult>;
|
|
388
386
|
getAppUsage: (input: InsightsGetAppUsageInput) => Promise<InsightsGetAppUsageResult>;
|
|
389
387
|
/** A release's or a channel's update failures, and their breakdown over a time range. */
|
|
@@ -392,4 +390,4 @@ declare const insights: (options?: InsightsOptions) => import("@hot-updater/plug
|
|
|
392
390
|
endpoints: PluginEndpoint[];
|
|
393
391
|
}, {}>;
|
|
394
392
|
//#endregion
|
|
395
|
-
export { type ActiveInstallationWindow, type BundleEventFailure, type BundleEventFailureInput, type BundleEventFailureReason, type BundleEventFailureStage, type BundleEventRow, type BundleEventRowBase, type CreateBundleEventRequest, type CreateBundleEventRequestBase, type CursorPage, DEFAULT_INSIGHTS_RETENTION, type DatabaseBundleEventMetadata, type EventCursorPage, type EventHistoryRow, InsightsApi, InsightsBadRequestError, type InsightsBundleEventFilter, type InsightsBundleSelection, type InsightsCountEventsInput, type InsightsCountLatestEventsInput, type InsightsCountMeasurement, type InsightsCoverage, type InsightsEventCursor, type InsightsEventFilter, type InsightsEventPageInput, type InsightsFailureBreakdown, type InsightsFindLatestEventsInput, type InsightsGetAppUsageInput, type InsightsGetAppUsageResult, type InsightsGetReleaseActivityInput, type InsightsGetReleaseActivityResult, type InsightsHttpResponse, type InsightsIdentityParts, type InsightsInstallationEventPageInput, type InsightsListEventsInput, type InsightsModel, InsightsOptions, type InsightsProvider, type InsightsRecordEventInput, type InsightsRetention, type InsightsSchema, type InsightsScope, type InsightsTimeRange, type InsightsUpdateFailures, type InsightsUpdateFailuresInput, type InsightsUserInstallationPageInput, type InstallationHistoryRow, type InstallationRow, type ReleaseActivityMetrics, type ReleaseReference, type ReportingOverview, createInsightsModel, createInsightsProvider, createInsightsSchema, insights, insightsIdentity, insightsSchema };
|
|
393
|
+
export { type ActiveInstallationWindow, type BundleEventFailure, type BundleEventFailureInput, type BundleEventFailureReason, type BundleEventFailureStage, type BundleEventRow, type BundleEventRowBase, type CreateBundleEventRequest, type CreateBundleEventRequestBase, type CursorPage, DEFAULT_INSIGHTS_RETENTION, type DatabaseBundleEventMetadata, type EventCursorPage, type EventHistoryRow, InsightsApi, InsightsBadRequestError, type InsightsBundleEventFilter, type InsightsBundleSelection, type InsightsCountEventSeriesInput, type InsightsCountEventsInput, type InsightsCountLatestEventsInput, type InsightsCountMeasurement, type InsightsCoverage, type InsightsEventCursor, type InsightsEventFilter, type InsightsEventPageInput, type InsightsEventSeriesPoint, type InsightsFailureBreakdown, type InsightsFindLatestEventsInput, type InsightsGetAppUsageInput, type InsightsGetAppUsageResult, type InsightsGetReleaseActivityInput, type InsightsGetReleaseActivityResult, type InsightsHttpResponse, type InsightsIdentityParts, type InsightsInstallationEventPageInput, type InsightsListEventsInput, type InsightsModel, InsightsOptions, type InsightsProvider, type InsightsRecordEventInput, type InsightsRetention, type InsightsSchema, type InsightsScope, type InsightsTimeRange, type InsightsUpdateFailures, type InsightsUpdateFailuresInput, type InsightsUserInstallationPageInput, type InstallationHistoryRow, type InstallationRow, type ReleaseActivityMetrics, type ReleaseReference, type ReportingOverview, createInsightsModel, createInsightsProvider, createInsightsSchema, insights, insightsIdentity, insightsSchema };
|
package/dist/server/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { InsightsBadRequestError } from "./errors.mjs";
|
|
|
4
4
|
import { createInsightsProvider } from "./provider.mjs";
|
|
5
5
|
import { DEFAULT_INSIGHTS_RETENTION, createInsightsSchema, insightsSchema } from "./schema.mjs";
|
|
6
6
|
import { insightsIdentity, recordEvent } from "./recordEvent.mjs";
|
|
7
|
-
import { countEvents, countLatestEvents, findLatestEvents, getAppUsage, getReleaseActivity, getUpdateFailures, listEvents } from "./reads.mjs";
|
|
7
|
+
import { countEventSeries, countEvents, countLatestEvents, findLatestEvents, getAppUsage, getReleaseActivity, getUpdateFailures, listEvents } from "./reads.mjs";
|
|
8
8
|
import { INSIGHTS_ROUTES, createInsightsRouteHandlers } from "./routes.mjs";
|
|
9
9
|
import { DatabaseAdapterInputError, HotUpdaterConfigError, definePlugin, isDatabaseBusyError } from "@hot-updater/plugin-core";
|
|
10
10
|
//#region src/server/index.ts
|
|
@@ -17,7 +17,8 @@ const createInsightsApi = (db, now, retention) => ({
|
|
|
17
17
|
findLatestEvents: (input) => findLatestEvents(db, input),
|
|
18
18
|
countLatestEvents: (input) => countLatestEvents(db, input),
|
|
19
19
|
countEvents: (input) => countEvents(db, input),
|
|
20
|
-
|
|
20
|
+
countEventSeries: (input) => countEventSeries(db, input),
|
|
21
|
+
getReleaseActivity: (input) => getReleaseActivity(db, input, now),
|
|
21
22
|
getAppUsage: (input) => getAppUsage(db, input, now, retention),
|
|
22
23
|
/** A release's or a channel's update failures, and their breakdown over a time range. */
|
|
23
24
|
getUpdateFailures: (input) => getUpdateFailures(db, input, now, retention)
|
package/dist/server/model.cjs
CHANGED
|
@@ -10,6 +10,7 @@ const createInsightsModel = (api) => require_contract.createValidatedInsightsMod
|
|
|
10
10
|
findLatestEvents: api.findLatestEvents,
|
|
11
11
|
countLatestEvents: api.countLatestEvents,
|
|
12
12
|
countEvents: api.countEvents,
|
|
13
|
+
countEventSeries: api.countEventSeries,
|
|
13
14
|
getReleaseActivity: api.getReleaseActivity,
|
|
14
15
|
getAppUsage: api.getAppUsage
|
|
15
16
|
});
|
package/dist/server/model.mjs
CHANGED
|
@@ -10,6 +10,7 @@ const createInsightsModel = (api) => createValidatedInsightsModel({
|
|
|
10
10
|
findLatestEvents: api.findLatestEvents,
|
|
11
11
|
countLatestEvents: api.countLatestEvents,
|
|
12
12
|
countEvents: api.countEvents,
|
|
13
|
+
countEventSeries: api.countEventSeries,
|
|
13
14
|
getReleaseActivity: api.getReleaseActivity,
|
|
14
15
|
getAppUsage: api.getAppUsage
|
|
15
16
|
});
|
|
@@ -59,6 +59,19 @@ interface InsightsCountEventsInput {
|
|
|
59
59
|
readonly sinceMs: number;
|
|
60
60
|
readonly beforeReceivedAtMs: number;
|
|
61
61
|
}
|
|
62
|
+
/** One bundle filter's stored events in each interval of a time range. */
|
|
63
|
+
interface InsightsCountEventSeriesInput {
|
|
64
|
+
readonly filter: InsightsBundleEventFilter;
|
|
65
|
+
/** Whole UTC hours, at most 90 days, that a whole number of intervals fill. */
|
|
66
|
+
readonly timeRange: InsightsTimeRange;
|
|
67
|
+
/** Whole hours each point spans, from `timeRange.start`. */
|
|
68
|
+
readonly intervalMs: number;
|
|
69
|
+
}
|
|
70
|
+
/** Stored events in [startMs, startMs + intervalMs). */
|
|
71
|
+
interface InsightsEventSeriesPoint {
|
|
72
|
+
readonly startMs: number;
|
|
73
|
+
readonly events: number;
|
|
74
|
+
}
|
|
62
75
|
interface ReleaseReference {
|
|
63
76
|
readonly releaseId: string;
|
|
64
77
|
readonly platform: "ios" | "android";
|
|
@@ -77,33 +90,22 @@ type InsightsCoverage = {
|
|
|
77
90
|
readonly kind: "partial";
|
|
78
91
|
readonly sinceMs: number | null;
|
|
79
92
|
};
|
|
93
|
+
/**
|
|
94
|
+
* A release's counts since its first report, from its lifetime counters,
|
|
95
|
+
* which are kept.
|
|
96
|
+
*/
|
|
80
97
|
interface ReleaseActivityMetrics {
|
|
81
98
|
readonly downloads: number;
|
|
82
99
|
readonly launches: number;
|
|
83
100
|
readonly failedLaunches: number;
|
|
84
|
-
/** Omitted for lifetime-only bundle-row reads. */
|
|
85
|
-
readonly uniqueUsers?: number;
|
|
86
|
-
/** Daily points, present only for period reads. */
|
|
87
|
-
readonly series?: readonly {
|
|
88
|
-
readonly startMs: number;
|
|
89
|
-
readonly launches: number;
|
|
90
|
-
readonly failedLaunches: number;
|
|
91
|
-
}[];
|
|
92
101
|
}
|
|
93
|
-
|
|
102
|
+
interface InsightsGetReleaseActivityInput {
|
|
103
|
+
/** 1 to 100 releases. */
|
|
94
104
|
readonly releases: readonly ReleaseReference[];
|
|
95
|
-
|
|
96
|
-
readonly scope?: never;
|
|
97
|
-
} | {
|
|
98
|
-
readonly scope: InsightsScope;
|
|
99
|
-
readonly timeRange: InsightsTimeRange;
|
|
100
|
-
readonly releases?: never;
|
|
101
|
-
};
|
|
105
|
+
}
|
|
102
106
|
interface InsightsGetReleaseActivityResult {
|
|
103
|
-
readonly coverage: InsightsCoverage;
|
|
104
107
|
readonly data: readonly {
|
|
105
|
-
readonly release
|
|
106
|
-
readonly scope?: InsightsScope;
|
|
108
|
+
readonly release: ReleaseReference;
|
|
107
109
|
readonly metrics: ReleaseActivityMetrics;
|
|
108
110
|
}[];
|
|
109
111
|
readonly measuredAtMs: number;
|
|
@@ -167,10 +169,15 @@ interface InsightsModel {
|
|
|
167
169
|
countLatestEvents(input: InsightsCountLatestEventsInput): Promise<number>;
|
|
168
170
|
/** Count accepted reports in [sinceMs, beforeReceivedAtMs), across all pages. */
|
|
169
171
|
countEvents(input: InsightsCountEventsInput): Promise<number>;
|
|
170
|
-
/**
|
|
172
|
+
/**
|
|
173
|
+
* A bundle filter's stored events in each interval, every interval
|
|
174
|
+
* present in order, from the hourly counts that `countEvents` reads.
|
|
175
|
+
*/
|
|
176
|
+
countEventSeries(input: InsightsCountEventSeriesInput): Promise<readonly InsightsEventSeriesPoint[]>;
|
|
177
|
+
/** Releases' lifetime counters, without scanning raw events. */
|
|
171
178
|
getReleaseActivity(input: InsightsGetReleaseActivityInput): Promise<InsightsGetReleaseActivityResult>;
|
|
172
179
|
/** Read maintained App usage summaries and latest-report distribution. */
|
|
173
180
|
getAppUsage(input: InsightsGetAppUsageInput): Promise<InsightsGetAppUsageResult>;
|
|
174
181
|
}
|
|
175
182
|
//#endregion
|
|
176
|
-
export { InsightsBundleEventFilter, InsightsCountEventsInput, InsightsCountLatestEventsInput, InsightsCoverage, InsightsEventCursor, InsightsEventFilter, InsightsFindLatestEventsInput, InsightsGetAppUsageInput, InsightsGetAppUsageResult, InsightsGetReleaseActivityInput, InsightsGetReleaseActivityResult, InsightsListEventsInput, InsightsModel, InsightsRecordEventInput, InsightsScope, InsightsTimeRange, ReleaseActivityMetrics, ReleaseReference };
|
|
183
|
+
export { InsightsBundleEventFilter, InsightsCountEventSeriesInput, InsightsCountEventsInput, InsightsCountLatestEventsInput, InsightsCoverage, InsightsEventCursor, InsightsEventFilter, InsightsEventSeriesPoint, InsightsFindLatestEventsInput, InsightsGetAppUsageInput, InsightsGetAppUsageResult, InsightsGetReleaseActivityInput, InsightsGetReleaseActivityResult, InsightsListEventsInput, InsightsModel, InsightsRecordEventInput, InsightsScope, InsightsTimeRange, ReleaseActivityMetrics, ReleaseReference };
|
|
@@ -59,6 +59,19 @@ interface InsightsCountEventsInput {
|
|
|
59
59
|
readonly sinceMs: number;
|
|
60
60
|
readonly beforeReceivedAtMs: number;
|
|
61
61
|
}
|
|
62
|
+
/** One bundle filter's stored events in each interval of a time range. */
|
|
63
|
+
interface InsightsCountEventSeriesInput {
|
|
64
|
+
readonly filter: InsightsBundleEventFilter;
|
|
65
|
+
/** Whole UTC hours, at most 90 days, that a whole number of intervals fill. */
|
|
66
|
+
readonly timeRange: InsightsTimeRange;
|
|
67
|
+
/** Whole hours each point spans, from `timeRange.start`. */
|
|
68
|
+
readonly intervalMs: number;
|
|
69
|
+
}
|
|
70
|
+
/** Stored events in [startMs, startMs + intervalMs). */
|
|
71
|
+
interface InsightsEventSeriesPoint {
|
|
72
|
+
readonly startMs: number;
|
|
73
|
+
readonly events: number;
|
|
74
|
+
}
|
|
62
75
|
interface ReleaseReference {
|
|
63
76
|
readonly releaseId: string;
|
|
64
77
|
readonly platform: "ios" | "android";
|
|
@@ -77,33 +90,22 @@ type InsightsCoverage = {
|
|
|
77
90
|
readonly kind: "partial";
|
|
78
91
|
readonly sinceMs: number | null;
|
|
79
92
|
};
|
|
93
|
+
/**
|
|
94
|
+
* A release's counts since its first report, from its lifetime counters,
|
|
95
|
+
* which are kept.
|
|
96
|
+
*/
|
|
80
97
|
interface ReleaseActivityMetrics {
|
|
81
98
|
readonly downloads: number;
|
|
82
99
|
readonly launches: number;
|
|
83
100
|
readonly failedLaunches: number;
|
|
84
|
-
/** Omitted for lifetime-only bundle-row reads. */
|
|
85
|
-
readonly uniqueUsers?: number;
|
|
86
|
-
/** Daily points, present only for period reads. */
|
|
87
|
-
readonly series?: readonly {
|
|
88
|
-
readonly startMs: number;
|
|
89
|
-
readonly launches: number;
|
|
90
|
-
readonly failedLaunches: number;
|
|
91
|
-
}[];
|
|
92
101
|
}
|
|
93
|
-
|
|
102
|
+
interface InsightsGetReleaseActivityInput {
|
|
103
|
+
/** 1 to 100 releases. */
|
|
94
104
|
readonly releases: readonly ReleaseReference[];
|
|
95
|
-
|
|
96
|
-
readonly scope?: never;
|
|
97
|
-
} | {
|
|
98
|
-
readonly scope: InsightsScope;
|
|
99
|
-
readonly timeRange: InsightsTimeRange;
|
|
100
|
-
readonly releases?: never;
|
|
101
|
-
};
|
|
105
|
+
}
|
|
102
106
|
interface InsightsGetReleaseActivityResult {
|
|
103
|
-
readonly coverage: InsightsCoverage;
|
|
104
107
|
readonly data: readonly {
|
|
105
|
-
readonly release
|
|
106
|
-
readonly scope?: InsightsScope;
|
|
108
|
+
readonly release: ReleaseReference;
|
|
107
109
|
readonly metrics: ReleaseActivityMetrics;
|
|
108
110
|
}[];
|
|
109
111
|
readonly measuredAtMs: number;
|
|
@@ -167,10 +169,15 @@ interface InsightsModel {
|
|
|
167
169
|
countLatestEvents(input: InsightsCountLatestEventsInput): Promise<number>;
|
|
168
170
|
/** Count accepted reports in [sinceMs, beforeReceivedAtMs), across all pages. */
|
|
169
171
|
countEvents(input: InsightsCountEventsInput): Promise<number>;
|
|
170
|
-
/**
|
|
172
|
+
/**
|
|
173
|
+
* A bundle filter's stored events in each interval, every interval
|
|
174
|
+
* present in order, from the hourly counts that `countEvents` reads.
|
|
175
|
+
*/
|
|
176
|
+
countEventSeries(input: InsightsCountEventSeriesInput): Promise<readonly InsightsEventSeriesPoint[]>;
|
|
177
|
+
/** Releases' lifetime counters, without scanning raw events. */
|
|
171
178
|
getReleaseActivity(input: InsightsGetReleaseActivityInput): Promise<InsightsGetReleaseActivityResult>;
|
|
172
179
|
/** Read maintained App usage summaries and latest-report distribution. */
|
|
173
180
|
getAppUsage(input: InsightsGetAppUsageInput): Promise<InsightsGetAppUsageResult>;
|
|
174
181
|
}
|
|
175
182
|
//#endregion
|
|
176
|
-
export { InsightsBundleEventFilter, InsightsCountEventsInput, InsightsCountLatestEventsInput, InsightsCoverage, InsightsEventCursor, InsightsEventFilter, InsightsFindLatestEventsInput, InsightsGetAppUsageInput, InsightsGetAppUsageResult, InsightsGetReleaseActivityInput, InsightsGetReleaseActivityResult, InsightsListEventsInput, InsightsModel, InsightsRecordEventInput, InsightsScope, InsightsTimeRange, ReleaseActivityMetrics, ReleaseReference };
|
|
183
|
+
export { InsightsBundleEventFilter, InsightsCountEventSeriesInput, InsightsCountEventsInput, InsightsCountLatestEventsInput, InsightsCoverage, InsightsEventCursor, InsightsEventFilter, InsightsEventSeriesPoint, InsightsFindLatestEventsInput, InsightsGetAppUsageInput, InsightsGetAppUsageResult, InsightsGetReleaseActivityInput, InsightsGetReleaseActivityResult, InsightsListEventsInput, InsightsModel, InsightsRecordEventInput, InsightsScope, InsightsTimeRange, ReleaseActivityMetrics, ReleaseReference };
|
package/dist/server/overview.cjs
CHANGED
|
@@ -33,12 +33,14 @@ const insightsOverviewDeltas = (event) => {
|
|
|
33
33
|
downloads: (previous?.downloads ?? 0) + values.downloads,
|
|
34
34
|
launches: (previous?.launches ?? 0) + values.launches,
|
|
35
35
|
failedLaunches: (previous?.failedLaunches ?? 0) + values.failedLaunches,
|
|
36
|
-
launchIdentity: previous?.launchIdentity ?? values.launchIdentity,
|
|
37
36
|
activityIdentity: previous?.activityIdentity ?? values.activityIdentity
|
|
38
37
|
});
|
|
39
38
|
};
|
|
40
39
|
const metric = (releaseId, values) => {
|
|
41
|
-
const
|
|
40
|
+
const windowed = {
|
|
41
|
+
...values,
|
|
42
|
+
launches: 0
|
|
43
|
+
};
|
|
42
44
|
if (releaseId !== null) for (const [periodKind, start] of [["lifetime", 0], ["hour", bucketStartMs]]) append({
|
|
43
45
|
scopeKind: "release",
|
|
44
46
|
releaseKind: "specific",
|
|
@@ -49,10 +51,7 @@ const insightsOverviewDeltas = (event) => {
|
|
|
49
51
|
appVersion: "",
|
|
50
52
|
periodKind,
|
|
51
53
|
bucketStartMs: start
|
|
52
|
-
},
|
|
53
|
-
...values,
|
|
54
|
-
...periodKind === "hour" ? { launchIdentity } : {}
|
|
55
|
-
});
|
|
54
|
+
}, periodKind === "lifetime" ? values : windowed);
|
|
56
55
|
append({
|
|
57
56
|
scopeKind: "channel",
|
|
58
57
|
releaseKind: "all",
|
|
@@ -63,10 +62,7 @@ const insightsOverviewDeltas = (event) => {
|
|
|
63
62
|
appVersion: "",
|
|
64
63
|
periodKind: "hour",
|
|
65
64
|
bucketStartMs
|
|
66
|
-
},
|
|
67
|
-
...values,
|
|
68
|
-
launchIdentity
|
|
69
|
-
});
|
|
65
|
+
}, windowed);
|
|
70
66
|
};
|
|
71
67
|
if (event.type === "UPDATE_DOWNLOADED") metric(event.to_release_id, {
|
|
72
68
|
downloads: 1,
|
package/dist/server/overview.mjs
CHANGED
|
@@ -33,12 +33,14 @@ const insightsOverviewDeltas = (event) => {
|
|
|
33
33
|
downloads: (previous?.downloads ?? 0) + values.downloads,
|
|
34
34
|
launches: (previous?.launches ?? 0) + values.launches,
|
|
35
35
|
failedLaunches: (previous?.failedLaunches ?? 0) + values.failedLaunches,
|
|
36
|
-
launchIdentity: previous?.launchIdentity ?? values.launchIdentity,
|
|
37
36
|
activityIdentity: previous?.activityIdentity ?? values.activityIdentity
|
|
38
37
|
});
|
|
39
38
|
};
|
|
40
39
|
const metric = (releaseId, values) => {
|
|
41
|
-
const
|
|
40
|
+
const windowed = {
|
|
41
|
+
...values,
|
|
42
|
+
launches: 0
|
|
43
|
+
};
|
|
42
44
|
if (releaseId !== null) for (const [periodKind, start] of [["lifetime", 0], ["hour", bucketStartMs]]) append({
|
|
43
45
|
scopeKind: "release",
|
|
44
46
|
releaseKind: "specific",
|
|
@@ -49,10 +51,7 @@ const insightsOverviewDeltas = (event) => {
|
|
|
49
51
|
appVersion: "",
|
|
50
52
|
periodKind,
|
|
51
53
|
bucketStartMs: start
|
|
52
|
-
},
|
|
53
|
-
...values,
|
|
54
|
-
...periodKind === "hour" ? { launchIdentity } : {}
|
|
55
|
-
});
|
|
54
|
+
}, periodKind === "lifetime" ? values : windowed);
|
|
56
55
|
append({
|
|
57
56
|
scopeKind: "channel",
|
|
58
57
|
releaseKind: "all",
|
|
@@ -63,10 +62,7 @@ const insightsOverviewDeltas = (event) => {
|
|
|
63
62
|
appVersion: "",
|
|
64
63
|
periodKind: "hour",
|
|
65
64
|
bucketStartMs
|
|
66
|
-
},
|
|
67
|
-
...values,
|
|
68
|
-
launchIdentity
|
|
69
|
-
});
|
|
65
|
+
}, windowed);
|
|
70
66
|
};
|
|
71
67
|
if (event.type === "UPDATE_DOWNLOADED") metric(event.to_release_id, {
|
|
72
68
|
downloads: 1,
|
package/dist/server/reads.cjs
CHANGED
|
@@ -171,6 +171,30 @@ const countEvents = async (db, input) => {
|
|
|
171
171
|
}));
|
|
172
172
|
return await countRaw(db, filter, sinceMs, start) + hours.reduce((sum, row) => sum + row.events, 0) + await countRaw(db, filter, end, beforeReceivedAtMs);
|
|
173
173
|
};
|
|
174
|
+
/**
|
|
175
|
+
* A bundle filter's stored events in each interval of whole hours: one read
|
|
176
|
+
* of its hourly counts, every interval present.
|
|
177
|
+
*/
|
|
178
|
+
const countEventSeries = async (db, input) => {
|
|
179
|
+
const { filter, intervalMs } = input;
|
|
180
|
+
const { start, end } = input.timeRange;
|
|
181
|
+
const events = Array.from({ length: (end - start) / intervalMs }, () => 0);
|
|
182
|
+
const hours = await drain((page) => db.findAggregates("insights_outcomes", {
|
|
183
|
+
index: "byRef",
|
|
184
|
+
where: scopeOf(filter),
|
|
185
|
+
range: {
|
|
186
|
+
gte: start,
|
|
187
|
+
lt: end
|
|
188
|
+
},
|
|
189
|
+
limit: PAGE,
|
|
190
|
+
...page
|
|
191
|
+
}));
|
|
192
|
+
for (const row of hours) events[Math.floor((row.bucket_start_ms - start) / intervalMs)] += row.events;
|
|
193
|
+
return events.map((count, index) => ({
|
|
194
|
+
startMs: start + index * intervalMs,
|
|
195
|
+
events: count
|
|
196
|
+
}));
|
|
197
|
+
};
|
|
174
198
|
/** A bundle filter's distinct (field, value, type) predicates. */
|
|
175
199
|
const latestPredicates = (bundle) => {
|
|
176
200
|
if (bundle === void 0) return void 0;
|
|
@@ -242,7 +266,7 @@ const retained = (now, { rawDays, dailyDays }) => ({
|
|
|
242
266
|
* Whether the rows a window reads still hold all of it; an older start is
|
|
243
267
|
* partial from `oldest`, the first bucket its rows keep.
|
|
244
268
|
*/
|
|
245
|
-
const coverageOf = (start, oldest) => start
|
|
269
|
+
const coverageOf = (start, oldest) => start >= oldest ? {
|
|
246
270
|
kind: "complete",
|
|
247
271
|
sinceMs: 0
|
|
248
272
|
} : {
|
|
@@ -345,32 +369,6 @@ const lifetimeMetrics = async (db, release) => {
|
|
|
345
369
|
failedLaunches: lifetime?.failed_launches ?? 0
|
|
346
370
|
};
|
|
347
371
|
};
|
|
348
|
-
/** Counters, unique users, and a per-UTC-day series over a window. */
|
|
349
|
-
const rangedMetrics = async (db, parts, users, range, days, hour) => {
|
|
350
|
-
const [counters, sketches] = await Promise.all([counterRows(db, parts, range, days, hour), sketchRows(db, users.parts, range, days, hour)]);
|
|
351
|
-
const series = /* @__PURE__ */ new Map();
|
|
352
|
-
for (const row of counters) {
|
|
353
|
-
const day = dayFloor(row.bucket_start_ms);
|
|
354
|
-
const point = series.get(day) ?? {
|
|
355
|
-
launches: 0,
|
|
356
|
-
failedLaunches: 0
|
|
357
|
-
};
|
|
358
|
-
point.launches += row.launches;
|
|
359
|
-
point.failedLaunches += row.failed_launches;
|
|
360
|
-
series.set(day, point);
|
|
361
|
-
}
|
|
362
|
-
const total = (field) => counters.reduce((sum, row) => sum + row[field], 0);
|
|
363
|
-
return {
|
|
364
|
-
downloads: total("downloads"),
|
|
365
|
-
launches: total("launches"),
|
|
366
|
-
failedLaunches: total("failed_launches"),
|
|
367
|
-
uniqueUsers: (0, _hot_updater_plugin_core.countDistinct)((0, _hot_updater_plugin_core.mergeDistinct)(sketches.map((row) => row[users.field]))),
|
|
368
|
-
series: [...series].sort(([left], [right]) => left - right).map(([startMs, point]) => ({
|
|
369
|
-
startMs,
|
|
370
|
-
...point
|
|
371
|
-
}))
|
|
372
|
-
};
|
|
373
|
-
};
|
|
374
372
|
/** A channel and platform's usage rows, of every app version or of one. */
|
|
375
373
|
const usageParts = (channel, platform, appVersion) => ({
|
|
376
374
|
scopeKind: "usage",
|
|
@@ -381,35 +379,15 @@ const usageParts = (channel, platform, appVersion) => ({
|
|
|
381
379
|
appVersionKind: appVersion === void 0 ? "all" : "specific",
|
|
382
380
|
appVersion: appVersion ?? ""
|
|
383
381
|
});
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
const
|
|
387
|
-
const data = input.scope !== void 0 ? [{
|
|
388
|
-
scope: input.scope,
|
|
389
|
-
metrics: await rangedMetrics(db, {
|
|
390
|
-
scopeKind: "channel",
|
|
391
|
-
releaseKind: "all",
|
|
392
|
-
releaseId: "",
|
|
393
|
-
channel: input.scope.channel,
|
|
394
|
-
platform: input.scope.platform,
|
|
395
|
-
appVersionKind: "all",
|
|
396
|
-
appVersion: ""
|
|
397
|
-
}, {
|
|
398
|
-
parts: usageParts(input.scope.channel, input.scope.platform),
|
|
399
|
-
field: "activity_users"
|
|
400
|
-
}, input.timeRange, true, kept.hour)
|
|
401
|
-
}] : await Promise.all(input.releases.map(async (release) => ({
|
|
402
|
-
release,
|
|
403
|
-
metrics: input.timeRange === void 0 ? await lifetimeMetrics(db, release) : await rangedMetrics(db, releaseParts(release), {
|
|
404
|
-
parts: releaseParts(release),
|
|
405
|
-
field: "launch_users"
|
|
406
|
-
}, input.timeRange, false, kept.hour)
|
|
407
|
-
})));
|
|
408
|
-
const oldest = kept[input.scope === void 0 ? "hour" : "day"];
|
|
382
|
+
/** Each release's lifetime counters, which are kept: one logical row apiece. */
|
|
383
|
+
const getReleaseActivity = async (db, input, now) => {
|
|
384
|
+
const measuredAtMs = now();
|
|
409
385
|
return {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
386
|
+
data: await Promise.all(input.releases.map(async (release) => ({
|
|
387
|
+
release,
|
|
388
|
+
metrics: await lifetimeMetrics(db, release)
|
|
389
|
+
}))),
|
|
390
|
+
measuredAtMs
|
|
413
391
|
};
|
|
414
392
|
};
|
|
415
393
|
/**
|
|
@@ -671,6 +649,7 @@ const getUpdateFailures = async (db, input, now, retention) => {
|
|
|
671
649
|
};
|
|
672
650
|
//#endregion
|
|
673
651
|
exports.UPDATE_FAILURES_RANGE_MS = UPDATE_FAILURES_RANGE_MS;
|
|
652
|
+
exports.countEventSeries = countEventSeries;
|
|
674
653
|
exports.countEvents = countEvents;
|
|
675
654
|
exports.countLatestEvents = countLatestEvents;
|
|
676
655
|
exports.findLatestEvents = findLatestEvents;
|
package/dist/server/reads.mjs
CHANGED
|
@@ -171,6 +171,30 @@ const countEvents = async (db, input) => {
|
|
|
171
171
|
}));
|
|
172
172
|
return await countRaw(db, filter, sinceMs, start) + hours.reduce((sum, row) => sum + row.events, 0) + await countRaw(db, filter, end, beforeReceivedAtMs);
|
|
173
173
|
};
|
|
174
|
+
/**
|
|
175
|
+
* A bundle filter's stored events in each interval of whole hours: one read
|
|
176
|
+
* of its hourly counts, every interval present.
|
|
177
|
+
*/
|
|
178
|
+
const countEventSeries = async (db, input) => {
|
|
179
|
+
const { filter, intervalMs } = input;
|
|
180
|
+
const { start, end } = input.timeRange;
|
|
181
|
+
const events = Array.from({ length: (end - start) / intervalMs }, () => 0);
|
|
182
|
+
const hours = await drain((page) => db.findAggregates("insights_outcomes", {
|
|
183
|
+
index: "byRef",
|
|
184
|
+
where: scopeOf(filter),
|
|
185
|
+
range: {
|
|
186
|
+
gte: start,
|
|
187
|
+
lt: end
|
|
188
|
+
},
|
|
189
|
+
limit: PAGE,
|
|
190
|
+
...page
|
|
191
|
+
}));
|
|
192
|
+
for (const row of hours) events[Math.floor((row.bucket_start_ms - start) / intervalMs)] += row.events;
|
|
193
|
+
return events.map((count, index) => ({
|
|
194
|
+
startMs: start + index * intervalMs,
|
|
195
|
+
events: count
|
|
196
|
+
}));
|
|
197
|
+
};
|
|
174
198
|
/** A bundle filter's distinct (field, value, type) predicates. */
|
|
175
199
|
const latestPredicates = (bundle) => {
|
|
176
200
|
if (bundle === void 0) return void 0;
|
|
@@ -242,7 +266,7 @@ const retained = (now, { rawDays, dailyDays }) => ({
|
|
|
242
266
|
* Whether the rows a window reads still hold all of it; an older start is
|
|
243
267
|
* partial from `oldest`, the first bucket its rows keep.
|
|
244
268
|
*/
|
|
245
|
-
const coverageOf = (start, oldest) => start
|
|
269
|
+
const coverageOf = (start, oldest) => start >= oldest ? {
|
|
246
270
|
kind: "complete",
|
|
247
271
|
sinceMs: 0
|
|
248
272
|
} : {
|
|
@@ -345,32 +369,6 @@ const lifetimeMetrics = async (db, release) => {
|
|
|
345
369
|
failedLaunches: lifetime?.failed_launches ?? 0
|
|
346
370
|
};
|
|
347
371
|
};
|
|
348
|
-
/** Counters, unique users, and a per-UTC-day series over a window. */
|
|
349
|
-
const rangedMetrics = async (db, parts, users, range, days, hour) => {
|
|
350
|
-
const [counters, sketches] = await Promise.all([counterRows(db, parts, range, days, hour), sketchRows(db, users.parts, range, days, hour)]);
|
|
351
|
-
const series = /* @__PURE__ */ new Map();
|
|
352
|
-
for (const row of counters) {
|
|
353
|
-
const day = dayFloor(row.bucket_start_ms);
|
|
354
|
-
const point = series.get(day) ?? {
|
|
355
|
-
launches: 0,
|
|
356
|
-
failedLaunches: 0
|
|
357
|
-
};
|
|
358
|
-
point.launches += row.launches;
|
|
359
|
-
point.failedLaunches += row.failed_launches;
|
|
360
|
-
series.set(day, point);
|
|
361
|
-
}
|
|
362
|
-
const total = (field) => counters.reduce((sum, row) => sum + row[field], 0);
|
|
363
|
-
return {
|
|
364
|
-
downloads: total("downloads"),
|
|
365
|
-
launches: total("launches"),
|
|
366
|
-
failedLaunches: total("failed_launches"),
|
|
367
|
-
uniqueUsers: countDistinct(mergeDistinct(sketches.map((row) => row[users.field]))),
|
|
368
|
-
series: [...series].sort(([left], [right]) => left - right).map(([startMs, point]) => ({
|
|
369
|
-
startMs,
|
|
370
|
-
...point
|
|
371
|
-
}))
|
|
372
|
-
};
|
|
373
|
-
};
|
|
374
372
|
/** A channel and platform's usage rows, of every app version or of one. */
|
|
375
373
|
const usageParts = (channel, platform, appVersion) => ({
|
|
376
374
|
scopeKind: "usage",
|
|
@@ -381,35 +379,15 @@ const usageParts = (channel, platform, appVersion) => ({
|
|
|
381
379
|
appVersionKind: appVersion === void 0 ? "all" : "specific",
|
|
382
380
|
appVersion: appVersion ?? ""
|
|
383
381
|
});
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
const
|
|
387
|
-
const data = input.scope !== void 0 ? [{
|
|
388
|
-
scope: input.scope,
|
|
389
|
-
metrics: await rangedMetrics(db, {
|
|
390
|
-
scopeKind: "channel",
|
|
391
|
-
releaseKind: "all",
|
|
392
|
-
releaseId: "",
|
|
393
|
-
channel: input.scope.channel,
|
|
394
|
-
platform: input.scope.platform,
|
|
395
|
-
appVersionKind: "all",
|
|
396
|
-
appVersion: ""
|
|
397
|
-
}, {
|
|
398
|
-
parts: usageParts(input.scope.channel, input.scope.platform),
|
|
399
|
-
field: "activity_users"
|
|
400
|
-
}, input.timeRange, true, kept.hour)
|
|
401
|
-
}] : await Promise.all(input.releases.map(async (release) => ({
|
|
402
|
-
release,
|
|
403
|
-
metrics: input.timeRange === void 0 ? await lifetimeMetrics(db, release) : await rangedMetrics(db, releaseParts(release), {
|
|
404
|
-
parts: releaseParts(release),
|
|
405
|
-
field: "launch_users"
|
|
406
|
-
}, input.timeRange, false, kept.hour)
|
|
407
|
-
})));
|
|
408
|
-
const oldest = kept[input.scope === void 0 ? "hour" : "day"];
|
|
382
|
+
/** Each release's lifetime counters, which are kept: one logical row apiece. */
|
|
383
|
+
const getReleaseActivity = async (db, input, now) => {
|
|
384
|
+
const measuredAtMs = now();
|
|
409
385
|
return {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
386
|
+
data: await Promise.all(input.releases.map(async (release) => ({
|
|
387
|
+
release,
|
|
388
|
+
metrics: await lifetimeMetrics(db, release)
|
|
389
|
+
}))),
|
|
390
|
+
measuredAtMs
|
|
413
391
|
};
|
|
414
392
|
};
|
|
415
393
|
/**
|
|
@@ -670,4 +648,4 @@ const getUpdateFailures = async (db, input, now, retention) => {
|
|
|
670
648
|
};
|
|
671
649
|
};
|
|
672
650
|
//#endregion
|
|
673
|
-
export { UPDATE_FAILURES_RANGE_MS, countEvents, countLatestEvents, findLatestEvents, getAppUsage, getReleaseActivity, getUpdateFailures, listEvents };
|
|
651
|
+
export { UPDATE_FAILURES_RANGE_MS, countEventSeries, countEvents, countLatestEvents, findLatestEvents, getAppUsage, getReleaseActivity, getUpdateFailures, listEvents };
|
|
@@ -89,11 +89,10 @@ const patchCounters = (event) => event.type !== "UPDATE_DOWNLOADED" ? {} : {
|
|
|
89
89
|
};
|
|
90
90
|
/**
|
|
91
91
|
* Counters and sketches for one event: release, channel, and usage rows, with
|
|
92
|
-
* day rollups for channel and usage.
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* in the rows that count it.
|
|
92
|
+
* day rollups for channel and usage. Only usage rows keep a sketch, of active
|
|
93
|
+
* installations, and only for the event's platform, since a read for every
|
|
94
|
+
* platform merges the ios and android sketches. A download's patch counters
|
|
95
|
+
* go in the rows that count it.
|
|
97
96
|
*/
|
|
98
97
|
const countEvent = (tx, event) => {
|
|
99
98
|
const shardBy = event.install_id;
|
|
@@ -119,11 +118,7 @@ const countEvent = (tx, event) => {
|
|
|
119
118
|
...delta.downloads === 0 ? {} : patchCounters(event)
|
|
120
119
|
};
|
|
121
120
|
if (Object.keys(counters).length > 0) tx.aggregate(models.counters, key, counters, { shardBy });
|
|
122
|
-
|
|
123
|
-
...delta.launchIdentity === void 0 || parts.scopeKind === "channel" ? {} : { launch_users: (0, _hot_updater_plugin_core.addDistinct)(null, delta.launchIdentity) },
|
|
124
|
-
...delta.activityIdentity === void 0 ? {} : { activity_users: (0, _hot_updater_plugin_core.addDistinct)(null, delta.activityIdentity) }
|
|
125
|
-
};
|
|
126
|
-
if (models.sketches !== void 0 && Object.keys(sketches).length > 0) tx.aggregate(models.sketches, key, sketches, { shardBy });
|
|
121
|
+
if (models.sketches !== void 0 && delta.activityIdentity !== void 0) tx.aggregate(models.sketches, key, { activity_users: (0, _hot_updater_plugin_core.addDistinct)(null, delta.activityIdentity) }, { shardBy });
|
|
127
122
|
}
|
|
128
123
|
}
|
|
129
124
|
};
|
|
@@ -89,11 +89,10 @@ const patchCounters = (event) => event.type !== "UPDATE_DOWNLOADED" ? {} : {
|
|
|
89
89
|
};
|
|
90
90
|
/**
|
|
91
91
|
* Counters and sketches for one event: release, channel, and usage rows, with
|
|
92
|
-
* day rollups for channel and usage.
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* in the rows that count it.
|
|
92
|
+
* day rollups for channel and usage. Only usage rows keep a sketch, of active
|
|
93
|
+
* installations, and only for the event's platform, since a read for every
|
|
94
|
+
* platform merges the ios and android sketches. A download's patch counters
|
|
95
|
+
* go in the rows that count it.
|
|
97
96
|
*/
|
|
98
97
|
const countEvent = (tx, event) => {
|
|
99
98
|
const shardBy = event.install_id;
|
|
@@ -119,11 +118,7 @@ const countEvent = (tx, event) => {
|
|
|
119
118
|
...delta.downloads === 0 ? {} : patchCounters(event)
|
|
120
119
|
};
|
|
121
120
|
if (Object.keys(counters).length > 0) tx.aggregate(models.counters, key, counters, { shardBy });
|
|
122
|
-
|
|
123
|
-
...delta.launchIdentity === void 0 || parts.scopeKind === "channel" ? {} : { launch_users: addDistinct(null, delta.launchIdentity) },
|
|
124
|
-
...delta.activityIdentity === void 0 ? {} : { activity_users: addDistinct(null, delta.activityIdentity) }
|
|
125
|
-
};
|
|
126
|
-
if (models.sketches !== void 0 && Object.keys(sketches).length > 0) tx.aggregate(models.sketches, key, sketches, { shardBy });
|
|
121
|
+
if (models.sketches !== void 0 && delta.activityIdentity !== void 0) tx.aggregate(models.sketches, key, { activity_users: addDistinct(null, delta.activityIdentity) }, { shardBy });
|
|
127
122
|
}
|
|
128
123
|
}
|
|
129
124
|
};
|
package/dist/server/schema.cjs
CHANGED
|
@@ -92,14 +92,6 @@ const bundleEvents = (days) => (0, _hot_updater_plugin_core.defineTable)({
|
|
|
92
92
|
}
|
|
93
93
|
},
|
|
94
94
|
indexes: {
|
|
95
|
-
recent: {
|
|
96
|
-
eq: [
|
|
97
|
-
"channel",
|
|
98
|
-
"platform",
|
|
99
|
-
"day"
|
|
100
|
-
],
|
|
101
|
-
sort: ["received_at_ms"]
|
|
102
|
-
},
|
|
103
95
|
movementsByInstall: {
|
|
104
96
|
eq: ["movement_install_id"],
|
|
105
97
|
sort: ["received_at_ms"]
|
|
@@ -216,10 +208,11 @@ const window = {
|
|
|
216
208
|
};
|
|
217
209
|
/**
|
|
218
210
|
* Counters of one period kind: hourly, daily, or lifetime at bucket 0. A
|
|
219
|
-
* release or channel row counts downloads
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
211
|
+
* release or channel row counts downloads and failed launches, and downloads
|
|
212
|
+
* a patch delivered and those that fell back from one. Only a release's
|
|
213
|
+
* lifetime row counts launches, which the bundle list reads and no windowed
|
|
214
|
+
* read sums. It also counts the release's failed updates (`failed_updates`),
|
|
215
|
+
* since no breakdown row outlives the raw period; windowed reads sum
|
|
223
216
|
* `insights_failures` instead.
|
|
224
217
|
*/
|
|
225
218
|
const counters = (retention) => (0, _hot_updater_plugin_core.defineAggregate)(identityFields, {
|
|
@@ -238,17 +231,13 @@ const counters = (retention) => (0, _hot_updater_plugin_core.defineAggregate)(id
|
|
|
238
231
|
...retention === void 0 ? {} : { retention }
|
|
239
232
|
});
|
|
240
233
|
/**
|
|
241
|
-
* Unique-installation sketches of one period kind
|
|
242
|
-
*
|
|
243
|
-
*
|
|
234
|
+
* Unique-installation sketches of one period kind: a usage row's active
|
|
235
|
+
* installations (`activity_users`), and a `failure` or `check` row's
|
|
236
|
+
* installations whose update or update check failed (`failed_users`).
|
|
244
237
|
*/
|
|
245
238
|
const sketches = (retention) => (0, _hot_updater_plugin_core.defineAggregate)(identityFields, {
|
|
246
239
|
key: ["identity", "bucket_start_ms"],
|
|
247
|
-
distinct: [
|
|
248
|
-
"launch_users",
|
|
249
|
-
"activity_users",
|
|
250
|
-
"failed_users"
|
|
251
|
-
],
|
|
240
|
+
distinct: ["activity_users", "failed_users"],
|
|
252
241
|
shards: SKETCH_SHARDS,
|
|
253
242
|
batched: true,
|
|
254
243
|
indexes: { window },
|
package/dist/server/schema.d.cts
CHANGED
|
@@ -71,10 +71,6 @@ declare const createInsightsSchema: ({ rawDays, dailyDays }?: InsightsRetention)
|
|
|
71
71
|
movement_install_id: "string";
|
|
72
72
|
bundle_ref: "string";
|
|
73
73
|
}, {
|
|
74
|
-
readonly recent: {
|
|
75
|
-
readonly eq: readonly ["channel", "platform", "day"];
|
|
76
|
-
readonly sort: readonly ["received_at_ms"];
|
|
77
|
-
};
|
|
78
74
|
readonly movementsByInstall: {
|
|
79
75
|
readonly eq: readonly ["movement_install_id"];
|
|
80
76
|
readonly sort: readonly ["received_at_ms"];
|
|
@@ -176,7 +172,7 @@ declare const createInsightsSchema: ({ rawDays, dailyDays }?: InsightsRetention)
|
|
|
176
172
|
readonly eq: readonly ["identity"];
|
|
177
173
|
readonly sort: readonly ["bucket_start_ms"];
|
|
178
174
|
};
|
|
179
|
-
}, "
|
|
175
|
+
}, "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
|
|
180
176
|
/** Daily counters and sketches: the daily period. */
|
|
181
177
|
readonly insights_overview_daily: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
182
178
|
readonly identity: {
|
|
@@ -205,7 +201,7 @@ declare const createInsightsSchema: ({ rawDays, dailyDays }?: InsightsRetention)
|
|
|
205
201
|
readonly eq: readonly ["identity"];
|
|
206
202
|
readonly sort: readonly ["bucket_start_ms"];
|
|
207
203
|
};
|
|
208
|
-
}, "
|
|
204
|
+
}, "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
|
|
209
205
|
/** A release's counters since its first event, and its failed installations: kept. */
|
|
210
206
|
readonly insights_overview_lifetime: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
211
207
|
readonly identity: {
|
|
@@ -410,10 +406,6 @@ declare const insightsSchema: {
|
|
|
410
406
|
movement_install_id: "string";
|
|
411
407
|
bundle_ref: "string";
|
|
412
408
|
}, {
|
|
413
|
-
readonly recent: {
|
|
414
|
-
readonly eq: readonly ["channel", "platform", "day"];
|
|
415
|
-
readonly sort: readonly ["received_at_ms"];
|
|
416
|
-
};
|
|
417
409
|
readonly movementsByInstall: {
|
|
418
410
|
readonly eq: readonly ["movement_install_id"];
|
|
419
411
|
readonly sort: readonly ["received_at_ms"];
|
|
@@ -515,7 +507,7 @@ declare const insightsSchema: {
|
|
|
515
507
|
readonly eq: readonly ["identity"];
|
|
516
508
|
readonly sort: readonly ["bucket_start_ms"];
|
|
517
509
|
};
|
|
518
|
-
}, "
|
|
510
|
+
}, "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
|
|
519
511
|
/** Daily counters and sketches: the daily period. */
|
|
520
512
|
readonly insights_overview_daily: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
521
513
|
readonly identity: {
|
|
@@ -544,7 +536,7 @@ declare const insightsSchema: {
|
|
|
544
536
|
readonly eq: readonly ["identity"];
|
|
545
537
|
readonly sort: readonly ["bucket_start_ms"];
|
|
546
538
|
};
|
|
547
|
-
}, "
|
|
539
|
+
}, "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
|
|
548
540
|
/** A release's counters since its first event, and its failed installations: kept. */
|
|
549
541
|
readonly insights_overview_lifetime: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
550
542
|
readonly identity: {
|
package/dist/server/schema.d.mts
CHANGED
|
@@ -71,10 +71,6 @@ declare const createInsightsSchema: ({ rawDays, dailyDays }?: InsightsRetention)
|
|
|
71
71
|
movement_install_id: "string";
|
|
72
72
|
bundle_ref: "string";
|
|
73
73
|
}, {
|
|
74
|
-
readonly recent: {
|
|
75
|
-
readonly eq: readonly ["channel", "platform", "day"];
|
|
76
|
-
readonly sort: readonly ["received_at_ms"];
|
|
77
|
-
};
|
|
78
74
|
readonly movementsByInstall: {
|
|
79
75
|
readonly eq: readonly ["movement_install_id"];
|
|
80
76
|
readonly sort: readonly ["received_at_ms"];
|
|
@@ -176,7 +172,7 @@ declare const createInsightsSchema: ({ rawDays, dailyDays }?: InsightsRetention)
|
|
|
176
172
|
readonly eq: readonly ["identity"];
|
|
177
173
|
readonly sort: readonly ["bucket_start_ms"];
|
|
178
174
|
};
|
|
179
|
-
}, "
|
|
175
|
+
}, "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
|
|
180
176
|
/** Daily counters and sketches: the daily period. */
|
|
181
177
|
readonly insights_overview_daily: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
182
178
|
readonly identity: {
|
|
@@ -205,7 +201,7 @@ declare const createInsightsSchema: ({ rawDays, dailyDays }?: InsightsRetention)
|
|
|
205
201
|
readonly eq: readonly ["identity"];
|
|
206
202
|
readonly sort: readonly ["bucket_start_ms"];
|
|
207
203
|
};
|
|
208
|
-
}, "
|
|
204
|
+
}, "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
|
|
209
205
|
/** A release's counters since its first event, and its failed installations: kept. */
|
|
210
206
|
readonly insights_overview_lifetime: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
211
207
|
readonly identity: {
|
|
@@ -410,10 +406,6 @@ declare const insightsSchema: {
|
|
|
410
406
|
movement_install_id: "string";
|
|
411
407
|
bundle_ref: "string";
|
|
412
408
|
}, {
|
|
413
|
-
readonly recent: {
|
|
414
|
-
readonly eq: readonly ["channel", "platform", "day"];
|
|
415
|
-
readonly sort: readonly ["received_at_ms"];
|
|
416
|
-
};
|
|
417
409
|
readonly movementsByInstall: {
|
|
418
410
|
readonly eq: readonly ["movement_install_id"];
|
|
419
411
|
readonly sort: readonly ["received_at_ms"];
|
|
@@ -515,7 +507,7 @@ declare const insightsSchema: {
|
|
|
515
507
|
readonly eq: readonly ["identity"];
|
|
516
508
|
readonly sort: readonly ["bucket_start_ms"];
|
|
517
509
|
};
|
|
518
|
-
}, "
|
|
510
|
+
}, "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
|
|
519
511
|
/** Daily counters and sketches: the daily period. */
|
|
520
512
|
readonly insights_overview_daily: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
521
513
|
readonly identity: {
|
|
@@ -544,7 +536,7 @@ declare const insightsSchema: {
|
|
|
544
536
|
readonly eq: readonly ["identity"];
|
|
545
537
|
readonly sort: readonly ["bucket_start_ms"];
|
|
546
538
|
};
|
|
547
|
-
}, "
|
|
539
|
+
}, "activity_users" | "failed_users", readonly ["identity", "bucket_start_ms"]>;
|
|
548
540
|
/** A release's counters since its first event, and its failed installations: kept. */
|
|
549
541
|
readonly insights_overview_lifetime: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
550
542
|
readonly identity: {
|
package/dist/server/schema.mjs
CHANGED
|
@@ -92,14 +92,6 @@ const bundleEvents = (days) => defineTable({
|
|
|
92
92
|
}
|
|
93
93
|
},
|
|
94
94
|
indexes: {
|
|
95
|
-
recent: {
|
|
96
|
-
eq: [
|
|
97
|
-
"channel",
|
|
98
|
-
"platform",
|
|
99
|
-
"day"
|
|
100
|
-
],
|
|
101
|
-
sort: ["received_at_ms"]
|
|
102
|
-
},
|
|
103
95
|
movementsByInstall: {
|
|
104
96
|
eq: ["movement_install_id"],
|
|
105
97
|
sort: ["received_at_ms"]
|
|
@@ -216,10 +208,11 @@ const window = {
|
|
|
216
208
|
};
|
|
217
209
|
/**
|
|
218
210
|
* Counters of one period kind: hourly, daily, or lifetime at bucket 0. A
|
|
219
|
-
* release or channel row counts downloads
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
211
|
+
* release or channel row counts downloads and failed launches, and downloads
|
|
212
|
+
* a patch delivered and those that fell back from one. Only a release's
|
|
213
|
+
* lifetime row counts launches, which the bundle list reads and no windowed
|
|
214
|
+
* read sums. It also counts the release's failed updates (`failed_updates`),
|
|
215
|
+
* since no breakdown row outlives the raw period; windowed reads sum
|
|
223
216
|
* `insights_failures` instead.
|
|
224
217
|
*/
|
|
225
218
|
const counters = (retention) => defineAggregate(identityFields, {
|
|
@@ -238,17 +231,13 @@ const counters = (retention) => defineAggregate(identityFields, {
|
|
|
238
231
|
...retention === void 0 ? {} : { retention }
|
|
239
232
|
});
|
|
240
233
|
/**
|
|
241
|
-
* Unique-installation sketches of one period kind
|
|
242
|
-
*
|
|
243
|
-
*
|
|
234
|
+
* Unique-installation sketches of one period kind: a usage row's active
|
|
235
|
+
* installations (`activity_users`), and a `failure` or `check` row's
|
|
236
|
+
* installations whose update or update check failed (`failed_users`).
|
|
244
237
|
*/
|
|
245
238
|
const sketches = (retention) => defineAggregate(identityFields, {
|
|
246
239
|
key: ["identity", "bucket_start_ms"],
|
|
247
|
-
distinct: [
|
|
248
|
-
"launch_users",
|
|
249
|
-
"activity_users",
|
|
250
|
-
"failed_users"
|
|
251
|
-
],
|
|
240
|
+
distinct: ["activity_users", "failed_users"],
|
|
252
241
|
shards: SKETCH_SHARDS,
|
|
253
242
|
batched: true,
|
|
254
243
|
indexes: { window },
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/plugin-insights",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.0-rc.
|
|
4
|
+
"version": "1.0.0-rc.31",
|
|
5
5
|
"description": "Hot Updater's Insights plugin: launch, download, and update failure events, for the server and the React Native app",
|
|
6
6
|
"exports": {
|
|
7
7
|
"./client": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"@types/node": "^20",
|
|
48
48
|
"vitest": "4.1.4",
|
|
49
49
|
"@hot-updater/plugin-core": "1.0.0-rc.28",
|
|
50
|
-
"@hot-updater/test-utils": "1.0.0-rc.
|
|
50
|
+
"@hot-updater/test-utils": "1.0.0-rc.30"
|
|
51
51
|
},
|
|
52
52
|
"nx": {
|
|
53
53
|
"implicitDependencies": [
|