@hot-updater/firebase 1.0.0-rc.29 → 1.0.0-rc.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/firebase/functions/index.cjs +225 -16
- package/dist/index.d.cts +92 -1
- package/dist/index.d.mts +92 -1
- package/package.json +6 -6
|
@@ -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.30";
|
|
4528
4528
|
//#endregion
|
|
4529
4529
|
//#region ../../packages/server/dist/handlerVersionRoutes.mjs
|
|
4530
4530
|
/**
|
|
@@ -7880,19 +7880,40 @@ const createValidatedInsightsModel = (model) => ({
|
|
|
7880
7880
|
if (!isRecord$2(input) || !hasOnlyKeys(input, [
|
|
7881
7881
|
"releases",
|
|
7882
7882
|
"scope",
|
|
7883
|
-
"timeRange"
|
|
7883
|
+
"timeRange",
|
|
7884
|
+
"intervalMs"
|
|
7884
7885
|
])) invalidQuery();
|
|
7885
7886
|
const releases = input.releases;
|
|
7886
7887
|
const scope = input.scope;
|
|
7887
7888
|
const timeRange = input.timeRange;
|
|
7888
|
-
const
|
|
7889
|
-
const
|
|
7889
|
+
const intervalMs = input.intervalMs;
|
|
7890
|
+
const releaseQuery = Array.isArray(releases) && releases.length > 0 && releases.length <= 100 && releases.every(isReleaseReference) && scope === void 0 && (timeRange === void 0 ? intervalMs === void 0 : isTimeRange(timeRange) && (intervalMs === void 0 || isTimestamp(intervalMs) && intervalMs > 0 && intervalMs % 36e5 === 0 && (timeRange.end - timeRange.start) / intervalMs <= 8784));
|
|
7891
|
+
const scopeQuery = releases === void 0 && intervalMs === void 0 && isRecord$2(scope) && hasOnlyKeys(scope, ["platform", "channel"]) && hasScope(scope) && isTimeRange(timeRange);
|
|
7890
7892
|
if (!releaseQuery && !scopeQuery) invalidQuery();
|
|
7891
7893
|
const result = await model.getReleaseActivity(input);
|
|
7892
7894
|
validateAggregateResult(result);
|
|
7893
7895
|
if (!Array.isArray(result.data)) invalidResult();
|
|
7894
7896
|
return result;
|
|
7895
7897
|
},
|
|
7898
|
+
async getDistributionHistory(input) {
|
|
7899
|
+
if (!isRecord$2(input) || !hasOnlyKeys(input, [
|
|
7900
|
+
"channel",
|
|
7901
|
+
"platform",
|
|
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
|
+
}
|
|
7915
|
+
return result;
|
|
7916
|
+
},
|
|
7896
7917
|
async getAppUsage(input) {
|
|
7897
7918
|
if (!isRecord$2(input) || !hasOnlyKeys(input, [
|
|
7898
7919
|
"channel",
|
|
@@ -7924,7 +7945,8 @@ const createInsightsModel = (api) => createValidatedInsightsModel({
|
|
|
7924
7945
|
countLatestEvents: api.countLatestEvents,
|
|
7925
7946
|
countEvents: api.countEvents,
|
|
7926
7947
|
getReleaseActivity: api.getReleaseActivity,
|
|
7927
|
-
getAppUsage: api.getAppUsage
|
|
7948
|
+
getAppUsage: api.getAppUsage,
|
|
7949
|
+
getDistributionHistory: api.getDistributionHistory
|
|
7928
7950
|
});
|
|
7929
7951
|
//#endregion
|
|
7930
7952
|
//#region ../insights/dist/server/official.mjs
|
|
@@ -9006,6 +9028,71 @@ const insightsDistribution = (retention) => defineAggregate({
|
|
|
9006
9028
|
},
|
|
9007
9029
|
retention
|
|
9008
9030
|
});
|
|
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
|
+
});
|
|
9009
9096
|
/** Latest events by the bundle they came from or went to, bucketed like the distribution. */
|
|
9010
9097
|
const insightsLatestByBundle = (retention) => defineAggregate({
|
|
9011
9098
|
platform: {
|
|
@@ -9125,6 +9212,8 @@ const createInsightsSchema = ({ rawDays, dailyDays } = DEFAULT_INSIGHTS_RETENTIO
|
|
|
9125
9212
|
insights_overview_lifetime: counters(),
|
|
9126
9213
|
insights_sketches_lifetime: lifetimeSketches(),
|
|
9127
9214
|
insights_distribution: insightsDistribution(daily),
|
|
9215
|
+
bundle_daily_heads: bundleDailyHeads(daily),
|
|
9216
|
+
insights_distribution_history: insightsDistributionHistory(daily),
|
|
9128
9217
|
insights_latest_by_bundle: insightsLatestByBundle(daily),
|
|
9129
9218
|
insights_outcomes: insightsOutcomes(hourly),
|
|
9130
9219
|
insights_failures: insightsFailures(hourly)
|
|
@@ -9512,21 +9601,66 @@ const REPEATED_FIELDS = [
|
|
|
9512
9601
|
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]);
|
|
9513
9602
|
/**
|
|
9514
9603
|
* Records one event in one transaction: one batch read of the event and its
|
|
9515
|
-
* installation's
|
|
9604
|
+
* installation's latest and daily heads, then the gauge and sketch rows it
|
|
9605
|
+
* changes and one
|
|
9516
9606
|
* write. On a database that batches aggregates (DynamoDB, Firestore), the
|
|
9517
9607
|
* write holds the event's rows and one log row instead, and a compaction
|
|
9518
9608
|
* reads and writes the aggregate rows. A stored event's id changes nothing
|
|
9519
9609
|
* when repeated, whichever installation sends it, and neither does the id
|
|
9520
9610
|
* of the installation's head. An UNCHANGED report that repeats its head on
|
|
9521
|
-
* the same UTC day
|
|
9611
|
+
* the same UTC day only advances its daily observation. An older event counts in its
|
|
9522
9612
|
* own hour but never replaces the head. An update failure is stored and
|
|
9523
9613
|
* counted, but changes what no installation runs, so it moves no head.
|
|
9524
9614
|
*/
|
|
9525
9615
|
const recordEvent = (db, event) => {
|
|
9526
9616
|
assertBundleEventRow(event);
|
|
9527
9617
|
return db.transaction(async (tx) => {
|
|
9528
|
-
const
|
|
9529
|
-
|
|
9618
|
+
const dailyKey = {
|
|
9619
|
+
install_id: event.install_id,
|
|
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
|
+
}
|
|
9530
9664
|
if (previous !== null && repeatsHead(event, previous)) return;
|
|
9531
9665
|
if (event.type !== "UNCHANGED") {
|
|
9532
9666
|
tx.create("bundle_events", event);
|
|
@@ -9892,19 +10026,30 @@ const lifetimeMetrics = async (db, release) => {
|
|
|
9892
10026
|
failedLaunches: lifetime?.failed_launches ?? 0
|
|
9893
10027
|
};
|
|
9894
10028
|
};
|
|
9895
|
-
/**
|
|
9896
|
-
|
|
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) => {
|
|
9897
10035
|
const [counters, sketches] = await Promise.all([counterRows(db, parts, range, days, hour), sketchRows(db, users.parts, range, days, hour)]);
|
|
9898
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
|
+
});
|
|
9899
10042
|
for (const row of counters) {
|
|
9900
|
-
const
|
|
9901
|
-
const point = series.get(
|
|
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,
|
|
9902
10046
|
launches: 0,
|
|
9903
10047
|
failedLaunches: 0
|
|
9904
10048
|
};
|
|
10049
|
+
point.downloads += row.downloads;
|
|
9905
10050
|
point.launches += row.launches;
|
|
9906
10051
|
point.failedLaunches += row.failed_launches;
|
|
9907
|
-
series.set(
|
|
10052
|
+
series.set(start, point);
|
|
9908
10053
|
}
|
|
9909
10054
|
const total = (field) => counters.reduce((sum, row) => sum + row[field], 0);
|
|
9910
10055
|
return {
|
|
@@ -9950,7 +10095,7 @@ const getReleaseActivity = async (db, input, now, retention) => {
|
|
|
9950
10095
|
metrics: input.timeRange === void 0 ? await lifetimeMetrics(db, release) : await rangedMetrics(db, releaseParts(release), {
|
|
9951
10096
|
parts: releaseParts(release),
|
|
9952
10097
|
field: "launch_users"
|
|
9953
|
-
}, input.timeRange, false, kept.hour)
|
|
10098
|
+
}, input.timeRange, false, kept.hour, input.intervalMs)
|
|
9954
10099
|
})));
|
|
9955
10100
|
const oldest = kept[input.scope === void 0 ? "hour" : "day"];
|
|
9956
10101
|
return {
|
|
@@ -10216,6 +10361,43 @@ const getUpdateFailures = async (db, input, now, retention) => {
|
|
|
10216
10361
|
}
|
|
10217
10362
|
};
|
|
10218
10363
|
};
|
|
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
|
+
};
|
|
10219
10401
|
//#endregion
|
|
10220
10402
|
//#region ../insights/dist/server/queryInput.mjs
|
|
10221
10403
|
const MAX_PAGE_LIMIT = 100;
|
|
@@ -10407,6 +10589,7 @@ const createInsightsApi = (db, now, retention) => ({
|
|
|
10407
10589
|
countLatestEvents: (input) => countLatestEvents(db, input),
|
|
10408
10590
|
countEvents: (input) => countEvents(db, input),
|
|
10409
10591
|
getReleaseActivity: (input) => getReleaseActivity(db, input, now, retention),
|
|
10592
|
+
getDistributionHistory: (input) => getDistributionHistory(db, input, now, retention),
|
|
10410
10593
|
getAppUsage: (input) => getAppUsage(db, input, now, retention),
|
|
10411
10594
|
/** A release's or a channel's update failures, and their breakdown over a time range. */
|
|
10412
10595
|
getUpdateFailures: (input) => getUpdateFailures(db, input, now, retention)
|
|
@@ -10475,7 +10658,7 @@ const insights = (options = {}) => {
|
|
|
10475
10658
|
const plugin = definePlugin({
|
|
10476
10659
|
id: "insights",
|
|
10477
10660
|
namespace: false,
|
|
10478
|
-
schemaVersion: "1.
|
|
10661
|
+
schemaVersion: "1.3.0",
|
|
10479
10662
|
schema: createInsightsSchema(retention),
|
|
10480
10663
|
init: ({ db, now }) => {
|
|
10481
10664
|
const api = createInsightsApi(db, now, retention);
|
|
@@ -10490,6 +10673,32 @@ const insights = (options = {}) => {
|
|
|
10490
10673
|
handler: (request, params) => routes[handler](params, request)
|
|
10491
10674
|
})),
|
|
10492
10675
|
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
|
+
},
|
|
10493
10702
|
{
|
|
10494
10703
|
access: "admin",
|
|
10495
10704
|
method: "GET",
|
package/dist/index.d.cts
CHANGED
|
@@ -191,9 +191,13 @@ interface ReleaseActivityMetrics {
|
|
|
191
191
|
readonly failedLaunches: number;
|
|
192
192
|
/** Omitted for lifetime-only bundle-row reads. */
|
|
193
193
|
readonly uniqueUsers?: number;
|
|
194
|
-
/**
|
|
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
|
+
*/
|
|
195
198
|
readonly series?: readonly {
|
|
196
199
|
readonly startMs: number;
|
|
200
|
+
readonly downloads: number;
|
|
197
201
|
readonly launches: number;
|
|
198
202
|
readonly failedLaunches: number;
|
|
199
203
|
}[];
|
|
@@ -201,10 +205,17 @@ interface ReleaseActivityMetrics {
|
|
|
201
205
|
type InsightsGetReleaseActivityInput = {
|
|
202
206
|
readonly releases: readonly ReleaseReference[];
|
|
203
207
|
readonly timeRange?: InsightsTimeRange;
|
|
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;
|
|
204
214
|
readonly scope?: never;
|
|
205
215
|
} | {
|
|
206
216
|
readonly scope: InsightsScope;
|
|
207
217
|
readonly timeRange: InsightsTimeRange;
|
|
218
|
+
readonly intervalMs?: never;
|
|
208
219
|
readonly releases?: never;
|
|
209
220
|
};
|
|
210
221
|
interface InsightsGetReleaseActivityResult {
|
|
@@ -247,6 +258,23 @@ interface InsightsGetAppUsageResult {
|
|
|
247
258
|
}[];
|
|
248
259
|
readonly measuredAtMs: number;
|
|
249
260
|
}
|
|
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
|
+
}
|
|
250
278
|
//#endregion
|
|
251
279
|
//#region ../insights/dist/server/schema.d.mts
|
|
252
280
|
//#region src/server/schema.d.ts
|
|
@@ -588,6 +616,68 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
|
|
|
588
616
|
readonly sort: readonly ["bucket_start_ms"];
|
|
589
617
|
};
|
|
590
618
|
}, 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"]>;
|
|
591
681
|
readonly insights_latest_by_bundle: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
592
682
|
readonly platform: {
|
|
593
683
|
readonly type: "string";
|
|
@@ -684,6 +774,7 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
|
|
|
684
774
|
countLatestEvents: (input: InsightsCountLatestEventsInput) => Promise<number>;
|
|
685
775
|
countEvents: (input: InsightsCountEventsInput) => Promise<number>;
|
|
686
776
|
getReleaseActivity: (input: InsightsGetReleaseActivityInput) => Promise<InsightsGetReleaseActivityResult>;
|
|
777
|
+
getDistributionHistory: (input: InsightsGetDistributionHistoryInput) => Promise<InsightsGetDistributionHistoryResult>;
|
|
687
778
|
getAppUsage: (input: InsightsGetAppUsageInput) => Promise<InsightsGetAppUsageResult>;
|
|
688
779
|
getUpdateFailures: (input: InsightsUpdateFailuresInput) => Promise<InsightsUpdateFailures>;
|
|
689
780
|
};
|
package/dist/index.d.mts
CHANGED
|
@@ -191,9 +191,13 @@ interface ReleaseActivityMetrics {
|
|
|
191
191
|
readonly failedLaunches: number;
|
|
192
192
|
/** Omitted for lifetime-only bundle-row reads. */
|
|
193
193
|
readonly uniqueUsers?: number;
|
|
194
|
-
/**
|
|
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
|
+
*/
|
|
195
198
|
readonly series?: readonly {
|
|
196
199
|
readonly startMs: number;
|
|
200
|
+
readonly downloads: number;
|
|
197
201
|
readonly launches: number;
|
|
198
202
|
readonly failedLaunches: number;
|
|
199
203
|
}[];
|
|
@@ -201,10 +205,17 @@ interface ReleaseActivityMetrics {
|
|
|
201
205
|
type InsightsGetReleaseActivityInput = {
|
|
202
206
|
readonly releases: readonly ReleaseReference[];
|
|
203
207
|
readonly timeRange?: InsightsTimeRange;
|
|
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;
|
|
204
214
|
readonly scope?: never;
|
|
205
215
|
} | {
|
|
206
216
|
readonly scope: InsightsScope;
|
|
207
217
|
readonly timeRange: InsightsTimeRange;
|
|
218
|
+
readonly intervalMs?: never;
|
|
208
219
|
readonly releases?: never;
|
|
209
220
|
};
|
|
210
221
|
interface InsightsGetReleaseActivityResult {
|
|
@@ -247,6 +258,23 @@ interface InsightsGetAppUsageResult {
|
|
|
247
258
|
}[];
|
|
248
259
|
readonly measuredAtMs: number;
|
|
249
260
|
}
|
|
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
|
+
}
|
|
250
278
|
//#endregion
|
|
251
279
|
//#region ../insights/dist/server/schema.d.mts
|
|
252
280
|
//#region src/server/schema.d.ts
|
|
@@ -588,6 +616,68 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
|
|
|
588
616
|
readonly sort: readonly ["bucket_start_ms"];
|
|
589
617
|
};
|
|
590
618
|
}, 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"]>;
|
|
591
681
|
readonly insights_latest_by_bundle: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
592
682
|
readonly platform: {
|
|
593
683
|
readonly type: "string";
|
|
@@ -684,6 +774,7 @@ declare const plugins: readonly [import("@hot-updater/plugin-core").HotUpdaterPl
|
|
|
684
774
|
countLatestEvents: (input: InsightsCountLatestEventsInput) => Promise<number>;
|
|
685
775
|
countEvents: (input: InsightsCountEventsInput) => Promise<number>;
|
|
686
776
|
getReleaseActivity: (input: InsightsGetReleaseActivityInput) => Promise<InsightsGetReleaseActivityResult>;
|
|
777
|
+
getDistributionHistory: (input: InsightsGetDistributionHistoryInput) => Promise<InsightsGetDistributionHistoryResult>;
|
|
687
778
|
getAppUsage: (input: InsightsGetAppUsageInput) => Promise<InsightsGetAppUsageResult>;
|
|
688
779
|
getUpdateFailures: (input: InsightsUpdateFailuresInput) => Promise<InsightsUpdateFailures>;
|
|
689
780
|
};
|
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.30",
|
|
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/protocol": "1.0.0-rc.28",
|
|
40
|
+
"@hot-updater/cli-tools": "1.0.0-rc.30",
|
|
42
41
|
"@hot-updater/plugin-core": "1.0.0-rc.28",
|
|
43
|
-
"@hot-updater/server": "1.0.0-rc.
|
|
42
|
+
"@hot-updater/server": "1.0.0-rc.30",
|
|
43
|
+
"@hot-updater/protocol": "1.0.0-rc.28"
|
|
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/test-utils": "1.0.0-rc.29",
|
|
59
|
+
"@hot-updater/mock": "1.0.0-rc.28"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"firebase-admin": "*",
|