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