@hot-updater/console 1.0.0-rc.2 → 1.0.0-rc.4

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.
Files changed (126) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/InsightsPageHeader-C-Xc2MB9.js +1 -0
  3. package/.output/public/assets/api-keys-C1sgC6ko.js +1 -0
  4. package/.output/public/assets/{api-keys-api-BHcODB7q.js → api-keys-api-LchP65vQ.js} +1 -1
  5. package/.output/public/assets/badge-BuMDU0Rf.js +1 -0
  6. package/.output/public/assets/card-BEvx_QKh.js +1 -0
  7. package/.output/public/assets/{chart-no-axes-combined-Dy4WJv2U.js → chart-no-axes-combined-C0G0fQ3J.js} +1 -1
  8. package/.output/public/assets/{composite-Bp98A0Ld.js → composite-CHTH1Xhh.js} +1 -1
  9. package/.output/public/assets/createServerFn-DHc2erub.js +3 -0
  10. package/.output/public/assets/dialog-dyZ1SphZ.js +1 -0
  11. package/.output/public/assets/{dist-C_NydL0e.js → dist-Bh1gaDfB.js} +1 -1
  12. package/.output/public/assets/field-79SVH26s.js +1 -0
  13. package/.output/public/assets/index-BNkxg4jz.js +10 -0
  14. package/.output/public/assets/input-group-BbHfaBzQ.js +1 -0
  15. package/.output/public/assets/insights-D_7uSKfc.js +1 -0
  16. package/.output/public/assets/installations-BzoHWuXw.js +1 -0
  17. package/.output/public/assets/{installations-CE33wgbY.js → installations-DvjbUqd1.js} +2 -2
  18. package/.output/public/assets/{lazyRouteComponent-Ccp3bwBY.js → lazyRouteComponent-BN0MS05o.js} +1 -1
  19. package/.output/public/assets/{link-CI8idAUi.js → link-BG06whwB.js} +1 -1
  20. package/.output/public/assets/{loader-circle-BoxLrsOu.js → loader-circle-Bbp8LGVC.js} +1 -1
  21. package/.output/public/assets/{refresh-cw-Bqc72UZG.js → refresh-cw-DZgiAOMk.js} +1 -1
  22. package/.output/public/assets/routes-BFE-oRIT.js +1 -0
  23. package/.output/public/assets/{routes-DGVXdzpw.js → routes-DaPMY31P.js} +2 -2
  24. package/.output/public/assets/{shield-check-uK3MHWJZ.js → shield-check-DoIiaaXU.js} +1 -1
  25. package/.output/public/assets/signing-BTRhaF5D.js +1 -0
  26. package/.output/public/assets/styles-C7U0oVke.css +2 -0
  27. package/.output/public/assets/table-BBEzJ7Xl.js +1 -0
  28. package/.output/public/assets/useCompositeListItem-BkPDaqyj.js +42 -0
  29. package/.output/server/{__23tanstack-start-server-fn-resolver-B7ioJhtE.mjs → __23tanstack-start-server-fn-resolver-Dr2ixDgQ.mjs} +40 -32
  30. package/.output/server/_libs/@base-ui/react+[...].mjs +1 -1
  31. package/.output/server/_libs/@reduxjs/toolkit+[...].mjs +1554 -0
  32. package/.output/server/_libs/@tanstack/react-router+[...].mjs +1 -1
  33. package/.output/server/_libs/@tanstack/react-router-ssr-query+[...].mjs +1 -1
  34. package/.output/server/_libs/d3-array.mjs +231 -0
  35. package/.output/server/_libs/d3-color.mjs +330 -0
  36. package/.output/server/_libs/d3-format.mjs +252 -0
  37. package/.output/server/_libs/d3-interpolate.mjs +207 -0
  38. package/.output/server/_libs/d3-path.mjs +77 -0
  39. package/.output/server/_libs/d3-scale+[...].mjs +1907 -0
  40. package/.output/server/_libs/d3-shape.mjs +818 -0
  41. package/.output/server/_libs/decimal.js-light.mjs +884 -0
  42. package/.output/server/_libs/es-toolkit.mjs +1159 -0
  43. package/.output/server/_libs/eventemitter3.mjs +236 -0
  44. package/.output/server/_libs/immer.mjs +532 -0
  45. package/.output/server/_libs/lucide-react.mjs +9 -2
  46. package/.output/server/_libs/next-themes.mjs +1 -1
  47. package/.output/server/_libs/react-redux+[...].mjs +205 -0
  48. package/.output/server/_libs/recharts+[...].mjs +15054 -0
  49. package/.output/server/_libs/sonner.mjs +1 -1
  50. package/.output/server/_libs/tanstack__react-query.mjs +1 -1
  51. package/.output/server/_runtime.mjs +10 -1
  52. package/.output/server/_ssr/InsightsPageHeader-C5Has8Ry.mjs +60 -0
  53. package/.output/server/_ssr/{api-keys-CZeTPQys.mjs → api-keys-DJml_XoK.mjs} +10 -8
  54. package/.output/server/_ssr/{api-keys-api-BUPQVSpk.mjs → api-keys-api-CrCZyvnl.mjs} +2 -2
  55. package/.output/server/_ssr/{api-keys-rpc-Cs7tS5_j.mjs → api-keys-rpc-CFaNf-q0.mjs} +3 -3
  56. package/.output/server/_ssr/{api-rpc-tAupfGQm.mjs → api-rpc-B61KL3Eo.mjs} +25 -25
  57. package/.output/server/_ssr/{auth-rpc-DUaeOWKK.mjs → auth-rpc-D_hv52-J.mjs} +4 -4
  58. package/.output/server/_ssr/{auth.server-DW71WzsD.mjs → auth.server-DF_shCeq.mjs} +1 -1
  59. package/.output/server/_ssr/badge-BgsCP9Dp.mjs +28 -0
  60. package/.output/server/_ssr/{bundle-signing-rpc-BhnBw0-d.mjs → bundle-signing-rpc-BBipZnFs.mjs} +2 -2
  61. package/.output/server/_ssr/{bundle-signing.server-C4NW5mWx.mjs → bundle-signing.server-Co_zHdJx.mjs} +1 -1
  62. package/.output/server/_ssr/bundleActivity-D3_fIda2.mjs +27 -0
  63. package/.output/server/_ssr/{card-BRiPmi0C.mjs → card-DrbxCPtG.mjs} +3 -25
  64. package/.output/server/_ssr/chart-qxa-_30a.mjs +162 -0
  65. package/.output/server/_ssr/{config.server-afDuAwaG.mjs → config.server-CBr6PDj5.mjs} +5 -5
  66. package/.output/server/_ssr/{createSsrRpc-DjWNJ0uc.mjs → createSsrRpc-NHF2PxuK.mjs} +4 -4
  67. package/.output/server/_ssr/{deleteBundle-BTGwBgzx.mjs → deleteBundle-BAq3JuGr.mjs} +1 -1
  68. package/.output/server/_ssr/{dialog-DS-nw-pA.mjs → dialog-DUaIgpgd.mjs} +1 -1
  69. package/.output/server/_ssr/{downloadBundle-CNnYxYO2.mjs → downloadBundle-B3ert-pY.mjs} +1 -1
  70. package/.output/server/_ssr/{table-CnZmon_Z.mjs → field-CqNUlqUq.mjs} +4 -50
  71. package/.output/server/_ssr/{getBundleChildren-C0vh4ZvE.mjs → getBundleChildren-COMt16m_.mjs} +1 -1
  72. package/.output/server/_ssr/{input-group-BpsnmvZw.mjs → input-group-Bb8ueylQ.mjs} +51 -2
  73. package/.output/server/_ssr/insights-B-DUbksX.mjs +502 -0
  74. package/.output/server/_ssr/insights-recovery-DSFM-YTL.mjs +21 -0
  75. package/.output/server/_ssr/insights-recovery-rpc-P71FH66n.mjs +37 -0
  76. package/.output/server/_ssr/{insights-rpc-38DujK0G.mjs → insights-rpc-DrIQCjpD.mjs} +2 -2
  77. package/.output/server/_ssr/insightsRecovery-CqcosG7B.mjs +149 -0
  78. package/.output/server/_ssr/{installations-CtA59MQG.mjs → installations-BQrppti7.mjs} +2 -2
  79. package/.output/server/_ssr/{installations-DiVKEF5T.mjs → installations-YumqpNpa.mjs} +426 -11
  80. package/.output/server/_ssr/{router-B9wG_9f8.mjs → router-iP95Vqi2.mjs} +14 -14
  81. package/.output/server/_ssr/{routes-CKrrS2q2.mjs → routes-BDabsmc7.mjs} +2 -2
  82. package/.output/server/_ssr/{routes-D7Yh_jfj.mjs → routes-ivsV2TDu.mjs} +224 -13
  83. package/.output/server/_ssr/{runtime.server-DacU1TBV.mjs → runtime.server-jmZOKIQM.mjs} +1 -1
  84. package/.output/server/_ssr/{server-D_C2rMzf.mjs → server-p7yrVBfD.mjs} +3 -3
  85. package/.output/server/_ssr/{signing-Dz1WkZOf.mjs → signing-CxCbnFCt.mjs} +5 -4
  86. package/.output/server/_ssr/ssr.mjs +1 -1
  87. package/.output/server/_ssr/table-D1mL8fJu.mjs +53 -0
  88. package/.output/server/_tanstack-start-manifest_v-Cwpu0C9Q.mjs +100 -0
  89. package/.output/server/index.mjs +154 -154
  90. package/package.json +3 -3
  91. package/src/components/features/bundles/BundleActivityChart.tsx +95 -0
  92. package/src/components/features/bundles/BundleInsightsSummary.tsx +121 -0
  93. package/src/components/features/insights/InsightsControls.tsx +1 -15
  94. package/src/components/features/insights/InsightsOverview.tsx +368 -179
  95. package/src/components/features/releases/ReleaseEditorSheet.tsx +10 -0
  96. package/src/components/ui/empty.tsx +101 -0
  97. package/src/lib/bundle-activity.ts +21 -0
  98. package/src/lib/insights-recovery-rpc.ts +38 -0
  99. package/src/lib/insights-recovery.ts +59 -0
  100. package/src/lib/server/bundleActivity.ts +37 -0
  101. package/src/lib/server/insightsHistory.ts +52 -0
  102. package/src/lib/server/insightsRecovery.ts +186 -0
  103. package/src/routes/index.tsx +33 -3
  104. package/src/routes/insights.tsx +1 -42
  105. package/.output/public/assets/HashValueDisplay-CB0Eskit.js +0 -1
  106. package/.output/public/assets/InsightsPageHeader-Car5LNiZ.js +0 -1
  107. package/.output/public/assets/api-keys-BzKocqRy.js +0 -1
  108. package/.output/public/assets/card-Dhr_eFYj.js +0 -1
  109. package/.output/public/assets/createServerFn-CA8fs7QD.js +0 -3
  110. package/.output/public/assets/dialog-CUTrcPlB.js +0 -1
  111. package/.output/public/assets/index-DdAz-CXv.js +0 -10
  112. package/.output/public/assets/input-group-cSOG4wh_.js +0 -1
  113. package/.output/public/assets/insights-C0PBSZ-n.js +0 -1
  114. package/.output/public/assets/installations-CStZja8x.js +0 -1
  115. package/.output/public/assets/routes-DTlwOr8I.js +0 -1
  116. package/.output/public/assets/signing-CMf4aqDO.js +0 -1
  117. package/.output/public/assets/styles-BCvbtKsm.css +0 -2
  118. package/.output/public/assets/table-2vcV89QB.js +0 -1
  119. package/.output/public/assets/useCompositeListItem-CHLITE_4.js +0 -1
  120. package/.output/public/assets/x-DCd0j8c6.js +0 -1
  121. package/.output/server/_ssr/HashValueDisplay-DwpGLfrl.mjs +0 -55
  122. package/.output/server/_ssr/InsightsPageHeader-Df4Brj8u.mjs +0 -484
  123. package/.output/server/_ssr/insights-BdS364Xu.mjs +0 -460
  124. package/.output/server/_tanstack-start-manifest_v-2MjTyxil.mjs +0 -97
  125. package/src/components/features/insights/InsightsBundleReports.tsx +0 -51
  126. /package/.output/public/assets/{preload-helper-CQ36nxok.js → preload-helper-uTix4PVD.js} +0 -0
@@ -0,0 +1,149 @@
1
+ import { n as recoveryWindows, t as readRecoveryInput } from "./insights-recovery-DSFM-YTL.mjs";
2
+ //#region node_modules/.nitro/vite/services/ssr/assets/insightsRecovery-CqcosG7B.js
3
+ async function readInsightsHistory(model, window, beforeReceivedAtMs = Date.now()) {
4
+ const { durationMs, intervalMs } = recoveryWindows[window];
5
+ const sinceMs = Math.max(0, beforeReceivedAtMs - durationMs);
6
+ let completeSinceMs = sinceMs;
7
+ let after;
8
+ let truncated = false;
9
+ const events = [];
10
+ while (events.length < 5e4) {
11
+ const limit = Math.min(100, 5e4 - events.length);
12
+ const rows = await model.listEvents({
13
+ filter: { kind: "all" },
14
+ sinceMs,
15
+ beforeReceivedAtMs,
16
+ after,
17
+ limit: limit + 1
18
+ });
19
+ const page = rows.slice(0, limit);
20
+ events.push(...page);
21
+ const last = page.at(-1);
22
+ if (!last || rows.length <= limit) break;
23
+ after = {
24
+ id: last.id,
25
+ receivedAtMs: last.received_at_ms
26
+ };
27
+ if (events.length === 5e4) {
28
+ truncated = true;
29
+ completeSinceMs = Math.floor(last.received_at_ms / intervalMs) * intervalMs + intervalMs;
30
+ }
31
+ }
32
+ return {
33
+ events: events.filter((row) => row.received_at_ms >= completeSinceMs).reverse(),
34
+ sinceMs: completeSinceMs,
35
+ beforeReceivedAtMs,
36
+ intervalMs,
37
+ truncated
38
+ };
39
+ }
40
+ async function getRecoveryReport(model, input, beforeReceivedAtMs = Date.now()) {
41
+ readRecoveryInput(input);
42
+ return buildRecoveryReport(await readInsightsHistory(model, input.window, beforeReceivedAtMs), input);
43
+ }
44
+ function buildRecoveryReport(history, input) {
45
+ const { events, sinceMs, beforeReceivedAtMs, intervalMs, truncated } = history;
46
+ const bucketStart = (time) => Math.floor(time / intervalMs) * intervalMs;
47
+ const releases = /* @__PURE__ */ new Map();
48
+ const ensureRelease = (id) => {
49
+ let release = releases.get(id);
50
+ if (!release) {
51
+ release = {
52
+ firstAdoptedAtMs: null,
53
+ recovered: /* @__PURE__ */ new Set(),
54
+ buckets: /* @__PURE__ */ new Map()
55
+ };
56
+ releases.set(id, release);
57
+ }
58
+ return release;
59
+ };
60
+ const latest = /* @__PURE__ */ new Map();
61
+ const active = /* @__PURE__ */ new Map();
62
+ const snapshots = /* @__PURE__ */ new Map();
63
+ let unattributedInstallations = 0;
64
+ let observed = false;
65
+ let eventIndex = 0;
66
+ for (let startMs = bucketStart(sinceMs); startMs < beforeReceivedAtMs; startMs += intervalMs) {
67
+ while (eventIndex < events.length && events[eventIndex].received_at_ms < startMs + intervalMs) {
68
+ const row = events[eventIndex++];
69
+ const inScope = row.platform === input.platform && row.channel === input.channel;
70
+ const previous = latest.get(row.install_id);
71
+ const releaseId = row.to_release_id ?? (row.type === "UNCHANGED" && previous?.bundleId === row.to_bundle_id && previous.inScope === inScope ? previous.releaseId : null);
72
+ if (previous?.inScope) if (previous.releaseId) active.set(previous.releaseId, (active.get(previous.releaseId) ?? 0) - 1);
73
+ else unattributedInstallations -= 1;
74
+ latest.set(row.install_id, {
75
+ releaseId,
76
+ bundleId: row.to_bundle_id,
77
+ inScope
78
+ });
79
+ if (!inScope) continue;
80
+ observed = true;
81
+ if (releaseId) {
82
+ ensureRelease(releaseId);
83
+ active.set(releaseId, (active.get(releaseId) ?? 0) + 1);
84
+ } else unattributedInstallations += 1;
85
+ if (row.from_release_id) ensureRelease(row.from_release_id);
86
+ const outcomeId = row.type === "RECOVERED" ? row.from_release_id : row.to_release_id;
87
+ if (!outcomeId || row.type !== "RECOVERED" && row.type !== "RELEASE_ADOPTED" && row.type !== "UPDATE_APPLIED") continue;
88
+ const release = ensureRelease(outcomeId);
89
+ const bucket = release.buckets.get(startMs) ?? {
90
+ adopted: 0,
91
+ recovered: 0,
92
+ installations: /* @__PURE__ */ new Set(),
93
+ lastRecoveredAtMs: -1
94
+ };
95
+ if (row.type === "RECOVERED") {
96
+ bucket.recovered += 1;
97
+ bucket.installations.add(row.install_id);
98
+ bucket.lastRecoveredAtMs = row.received_at_ms;
99
+ release.recovered.add(row.install_id);
100
+ } else {
101
+ bucket.adopted += 1;
102
+ release.firstAdoptedAtMs ??= row.received_at_ms;
103
+ }
104
+ release.buckets.set(startMs, bucket);
105
+ }
106
+ snapshots.set(startMs, observed ? new Map(active) : null);
107
+ }
108
+ const series = [];
109
+ for (const [releaseId, release] of releases) {
110
+ if (input.releaseId && input.releaseId !== releaseId) continue;
111
+ let previousRate = null;
112
+ const points = Array.from(snapshots, ([startMs, snapshot]) => {
113
+ const bucket = release.buckets.get(startMs);
114
+ const adopted = bucket?.adopted ?? 0;
115
+ const recovered = bucket?.recovered ?? 0;
116
+ const total = adopted + recovered;
117
+ const rate = total === 0 ? null : recovered / total * 100;
118
+ const spike = release.firstAdoptedAtMs !== null && (bucket?.lastRecoveredAtMs ?? -1) >= release.firstAdoptedAtMs && (!truncated || previousRate !== null) && rate !== null && total >= 10 && recovered >= 3 && rate >= 10 && rate - (previousRate ?? 0) >= 10;
119
+ if (rate !== null) previousRate = rate;
120
+ return {
121
+ startMs,
122
+ active: snapshot ? snapshot.get(releaseId) ?? 0 : null,
123
+ recoveredInstallations: bucket?.installations.size ?? 0,
124
+ adopted,
125
+ recovered,
126
+ rate,
127
+ spike
128
+ };
129
+ });
130
+ series.push({
131
+ releaseId,
132
+ firstAdoptedAtMs: release.firstAdoptedAtMs,
133
+ activeInstallations: active.get(releaseId) ?? 0,
134
+ recoveredInstallations: release.recovered.size,
135
+ points
136
+ });
137
+ }
138
+ series.sort((a, b) => b.releaseId.localeCompare(a.releaseId));
139
+ return {
140
+ sinceMs,
141
+ beforeReceivedAtMs,
142
+ intervalMs,
143
+ truncated,
144
+ unattributedInstallations,
145
+ series
146
+ };
147
+ }
148
+ //#endregion
149
+ export { buildRecoveryReport, getRecoveryReport, readInsightsHistory as t };
@@ -1,5 +1,5 @@
1
1
  import { d as lazyRouteComponent, f as createFileRoute } from "../_libs/@tanstack/react-router+[...].mjs";
2
- //#region node_modules/.nitro/vite/services/ssr/assets/installations-CtA59MQG.js
2
+ //#region node_modules/.nitro/vite/services/ssr/assets/installations-BQrppti7.js
3
3
  var readCursor = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
4
4
  var readTimestamp = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
5
5
  function getInsightsScrollRestorationKey(location) {
@@ -20,7 +20,7 @@ function validateInstallationsSearch(search) {
20
20
  historyBefore: readTimestamp(search.historyBefore)
21
21
  };
22
22
  }
23
- var $$splitComponentImporter = () => import("./installations-DiVKEF5T.mjs");
23
+ var $$splitComponentImporter = () => import("./installations-YumqpNpa.mjs");
24
24
  var Route = createFileRoute("/installations")({
25
25
  component: lazyRouteComponent($$splitComponentImporter, "component"),
26
26
  validateSearch: validateInstallationsSearch
@@ -1,17 +1,368 @@
1
- import { r as __toESM } from "../_runtime.mjs";
1
+ import { i as __toESM } from "../_runtime.mjs";
2
+ import { c as createServerFn } from "./createServerFn-B2hcXoPM.mjs";
2
3
  import { J as require_react, q as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
3
- import { N as ChevronDown, P as Check, R as ArrowLeft, d as RefreshCw, l as Search, t as X } from "../_libs/lucide-react.mjs";
4
- import { C as cn, S as buttonVariants, _ as Skeleton, r as Separator$1, t as Button$1 } from "./createSsrRpc-DjWNJ0uc.mjs";
5
- import { s as useElementScrollRestoration } from "../_libs/@tanstack/react-router+[...].mjs";
6
- import { d as CardTitle, o as Card, s as CardContent, u as CardHeader } from "./card-BRiPmi0C.mjs";
7
- import { a as FieldLabel, c as TableBody, d as TableHeader, f as TableRow, l as TableCell, s as Table, t as Field, u as TableHead } from "./table-CnZmon_Z.mjs";
8
- import { n as shortenIdentifier, t as HashValueDisplay } from "./HashValueDisplay-DwpGLfrl.mjs";
9
- import { a as EventTypeBadge, c as InsightsPagination, d as useInsightsInstallationQuery, f as useInsightsInstallationsQuery, i as EventTimestamp, l as useInsightsEventsQuery, n as EventHistoryCard, o as InsightsErrorAlert, p as useInsightsTimeFormat, r as EventHistoryList, s as InsightsPageHeader, t as EventBundleTransition, u as useInsightsInstallationEventsQuery } from "./InsightsPageHeader-Df4Brj8u.mjs";
10
- import { n as getInsightsScrollRestorationKey, t as Route } from "./installations-CtA59MQG.mjs";
11
- import { i as InputGroupInput, n as InputGroupAddon, r as InputGroupButton, t as InputGroup } from "./input-group-BpsnmvZw.mjs";
12
- //#region node_modules/.nitro/vite/services/ssr/assets/installations-DiVKEF5T.js
4
+ import { B as Activity, F as Check, L as ArrowUpRight, P as ChevronDown, d as RotateCcw, f as RefreshCw, g as PackageCheck, l as Search, t as X, z as ArrowLeft } from "../_libs/lucide-react.mjs";
5
+ import { C as cn, S as buttonVariants, _ as Skeleton, r as Separator$1, t as Button$1, w as createSsrRpc } from "./createSsrRpc-NHF2PxuK.mjs";
6
+ import { m as Link, s as useElementScrollRestoration } from "../_libs/@tanstack/react-router+[...].mjs";
7
+ import { n as useQuery } from "../_libs/tanstack__react-query.mjs";
8
+ import { a as Card, l as CardHeader, o as CardContent, u as CardTitle } from "./card-DrbxCPtG.mjs";
9
+ import { t as Badge } from "./badge-BgsCP9Dp.mjs";
10
+ import { a as FieldLabel, t as Field } from "./field-CqNUlqUq.mjs";
11
+ import { a as TableHeader, i as TableHead, n as TableBody, o as TableRow, r as TableCell, t as Table } from "./table-D1mL8fJu.mjs";
12
+ import { n as InsightsPageHeader, t as InsightsErrorAlert } from "./InsightsPageHeader-C5Has8Ry.mjs";
13
+ import { n as getInsightsScrollRestorationKey, t as Route } from "./installations-BQrppti7.mjs";
14
+ import { a as InputGroupInput, c as shortenIdentifier, i as InputGroupButton, n as InputGroup, r as InputGroupAddon, t as HashValueDisplay } from "./input-group-Bb8ueylQ.mjs";
15
+ //#region node_modules/.nitro/vite/services/ssr/assets/installations-YumqpNpa.js
13
16
  var import_react = /* @__PURE__ */ __toESM(require_react());
14
17
  var import_jsx_runtime = require_jsx_runtime();
18
+ var eventTypes = {
19
+ UPDATE_APPLIED: {
20
+ label: "Bundle applied",
21
+ variant: "success",
22
+ icon: Check
23
+ },
24
+ RECOVERED: {
25
+ label: "Recovered",
26
+ variant: "warning",
27
+ icon: RotateCcw
28
+ },
29
+ RELEASE_ADOPTED: {
30
+ label: "Bundle adopted",
31
+ variant: "success",
32
+ icon: PackageCheck
33
+ },
34
+ UNCHANGED: {
35
+ label: "Activity reported",
36
+ variant: "secondary",
37
+ icon: Activity
38
+ }
39
+ };
40
+ function useInsightsTimeFormat() {
41
+ const [timeZone, setTimeZone] = (0, import_react.useState)("UTC");
42
+ (0, import_react.useEffect)(() => {
43
+ setTimeZone(Intl.DateTimeFormat().resolvedOptions().timeZone);
44
+ }, []);
45
+ return new Intl.DateTimeFormat("en", {
46
+ timeZone,
47
+ timeZoneName: "shortOffset",
48
+ year: "numeric",
49
+ month: "2-digit",
50
+ day: "2-digit",
51
+ hour: "2-digit",
52
+ minute: "2-digit",
53
+ second: "2-digit",
54
+ hourCycle: "h23"
55
+ });
56
+ }
57
+ function formatInsightsTimestamp(value, formatter) {
58
+ const parts = Object.fromEntries(formatter.formatToParts(new Date(value)).map(({ type, value }) => [type, value]));
59
+ const offset = parts.timeZoneName === "GMT" ? "GMT+0" : parts.timeZoneName;
60
+ return `${parts.year}/${parts.month}/${parts.day} ${parts.hour}:${parts.minute}:${parts.second} ${offset}`;
61
+ }
62
+ function EventTimestamp({ value, formatter, touch = false }) {
63
+ const date = new Date(value);
64
+ const localTime = formatInsightsTimestamp(value, formatter);
65
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", {
66
+ className: "group/time",
67
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", {
68
+ className: `cursor-pointer list-none rounded-md outline-none focus-visible:ring-2 focus-visible:ring-ring/30 [&::-webkit-details-marker]:hidden ${touch ? "flex min-h-11 items-center" : ""}`,
69
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
70
+ className: "flex items-center gap-1",
71
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("time", {
72
+ dateTime: date.toISOString(),
73
+ className: "whitespace-nowrap",
74
+ children: localTime
75
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, {
76
+ "aria-hidden": "true",
77
+ className: "size-3 text-muted-foreground group-open/time:rotate-180"
78
+ })]
79
+ })
80
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
81
+ className: "mt-2 text-muted-foreground",
82
+ children: date.toISOString().replace("T", " ").replace("Z", " UTC")
83
+ })]
84
+ });
85
+ }
86
+ function EventTypeBadge({ type }) {
87
+ const eventType = eventTypes[type];
88
+ const Icon = eventType.icon;
89
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Badge, {
90
+ variant: eventType.variant,
91
+ className: "gap-1 whitespace-nowrap [&_svg]:size-3",
92
+ title: type === "UNCHANGED" ? "App activity reported on the current bundle without a bundle transition." : type === "RELEASE_ADOPTED" ? "A different release was adopted without changing the bundle." : void 0,
93
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { "aria-hidden": "true" }), eventType.label]
94
+ });
95
+ }
96
+ function EventBundleTransition({ event, touch = false }) {
97
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dl", {
98
+ className: "grid grid-cols-[auto_minmax(0,1fr)] items-center gap-x-2 gap-y-2",
99
+ children: [
100
+ event.fromBundleId ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
101
+ className: "text-muted-foreground",
102
+ children: "From"
103
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HashValueDisplay, {
104
+ value: event.fromBundleId,
105
+ buttonClassName: touch ? "min-h-11 px-3" : void 0
106
+ }) })] }) : null,
107
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
108
+ className: "text-muted-foreground",
109
+ children: event.type === "UNCHANGED" ? "Current" : "To"
110
+ }),
111
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HashValueDisplay, {
112
+ value: event.toBundleId,
113
+ buttonClassName: touch ? "min-h-11 px-3" : void 0
114
+ }) })
115
+ ]
116
+ });
117
+ }
118
+ function EventHistoryList({ events, formatter, renderIdentity }) {
119
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
120
+ className: "px-4 pb-2 text-xs text-muted-foreground sm:px-6",
121
+ children: "Time in your browser zone"
122
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", {
123
+ "aria-label": "Events",
124
+ className: "divide-y border-y",
125
+ children: events.map((event) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", {
126
+ className: "flex min-w-0 flex-col gap-3 px-4 py-4 sm:px-6",
127
+ children: [
128
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
129
+ className: "flex flex-wrap items-center justify-between gap-x-4 gap-y-1",
130
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventTypeBadge, { type: event.type }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
131
+ className: "text-xs tabular-nums",
132
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventTimestamp, {
133
+ value: event.receivedAtMs,
134
+ formatter,
135
+ touch: true
136
+ })
137
+ })]
138
+ }),
139
+ renderIdentity?.(event),
140
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dl", {
141
+ className: "flex flex-wrap gap-x-2 gap-y-1 text-sm",
142
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
143
+ className: "sr-only",
144
+ children: "App"
145
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dd", { children: [
146
+ event.platform === "ios" ? "iOS" : "Android",
147
+ " ",
148
+ event.appVersion
149
+ ] })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
150
+ className: "min-w-0 text-muted-foreground",
151
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
152
+ className: "sr-only",
153
+ children: "Channel"
154
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", {
155
+ className: "wrap-anywhere",
156
+ children: event.channel
157
+ })]
158
+ })]
159
+ }),
160
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
161
+ className: "text-xs",
162
+ "aria-label": "Bundle",
163
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventBundleTransition, {
164
+ event,
165
+ touch: true
166
+ })
167
+ })
168
+ ]
169
+ }, event.id))
170
+ })] });
171
+ }
172
+ function InsightsPagination({ hasPrevious, label, nextCursor, onNext, onPrevious, pageLength, pageNumber }) {
173
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("nav", {
174
+ "aria-label": `${label} pagination`,
175
+ className: "flex flex-wrap items-center justify-between gap-3 border-t px-4 py-3 sm:px-6",
176
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
177
+ className: "text-xs text-muted-foreground tabular-nums",
178
+ children: [
179
+ "Page ",
180
+ pageNumber,
181
+ " · ",
182
+ pageLength.toLocaleString(),
183
+ " rows"
184
+ ]
185
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
186
+ className: "flex gap-2",
187
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
188
+ className: "h-11 min-w-11 px-3 lg:h-8 lg:min-w-0 lg:px-2",
189
+ disabled: !hasPrevious,
190
+ onClick: onPrevious,
191
+ size: "sm",
192
+ type: "button",
193
+ variant: "outline",
194
+ children: "Previous"
195
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
196
+ className: "h-11 min-w-11 px-3 lg:h-8 lg:min-w-0 lg:px-2",
197
+ disabled: nextCursor === null,
198
+ onClick: onNext,
199
+ size: "sm",
200
+ type: "button",
201
+ variant: "outline",
202
+ children: "Next"
203
+ })]
204
+ })]
205
+ });
206
+ }
207
+ function EventIdentity({ event, eventsLocation, touch = false }) {
208
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
209
+ className: touch ? "flex min-w-0 flex-wrap items-center justify-between gap-2" : "flex min-w-0 flex-col items-start gap-2",
210
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Link, {
211
+ className: buttonVariants({
212
+ className: touch ? "-ml-2 h-auto min-h-11 min-w-0 flex-1 justify-start gap-2 whitespace-normal text-sm" : "-ml-2 max-w-full justify-start",
213
+ size: "sm",
214
+ variant: "ghost"
215
+ }),
216
+ to: "/installations",
217
+ search: {
218
+ ...eventsLocation,
219
+ query: event.installId || void 0,
220
+ installId: event.installId
221
+ },
222
+ "aria-label": `View history for ${event.userId ?? event.username ?? "anonymous installation"} (${event.installId})`,
223
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
224
+ className: touch ? "min-w-0 text-left wrap-anywhere" : "truncate",
225
+ children: event.userId ?? event.username ?? "Anonymous installation"
226
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArrowUpRight, {
227
+ "aria-hidden": "true",
228
+ "data-icon": "inline-end"
229
+ })]
230
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HashValueDisplay, {
231
+ value: event.installId,
232
+ maxLength: 13,
233
+ buttonClassName: touch ? "min-h-11 px-3" : void 0
234
+ })]
235
+ });
236
+ }
237
+ function EventHistoryCard({ children, error, eventsLocation, history, isFetching, isLoading, onNext, onPrevious, onRefresh, pageNumber, title = "All events" }) {
238
+ const dateTimeFormat = useInsightsTimeFormat();
239
+ const hasPrevious = pageNumber > 1;
240
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Card, {
241
+ className: "@container min-w-0 shadow-sm",
242
+ "aria-busy": isFetching,
243
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CardHeader, {
244
+ className: "gap-4 p-4 sm:p-6",
245
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
246
+ className: "flex flex-wrap items-center justify-between gap-3",
247
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardTitle, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: title }) }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
248
+ className: "h-11 lg:h-8",
249
+ disabled: isFetching,
250
+ onClick: onRefresh,
251
+ size: "lg",
252
+ variant: "outline",
253
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RefreshCw, {
254
+ "aria-hidden": "true",
255
+ "data-icon": "inline-start"
256
+ }), "Refresh"]
257
+ })]
258
+ }), children]
259
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardContent, {
260
+ className: "min-w-0 p-0",
261
+ children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
262
+ className: "flex flex-col gap-4 p-6",
263
+ role: "status",
264
+ "aria-label": "Loading events",
265
+ children: [
266
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-12 w-full" }),
267
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-12 w-full" }),
268
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-12 w-full" })
269
+ ]
270
+ }) : error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
271
+ className: "px-6 pb-6",
272
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsErrorAlert, {
273
+ error,
274
+ fallbackTitle: "Event history unavailable"
275
+ })
276
+ }) : history ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [history.data.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
277
+ className: "@[58rem]:hidden",
278
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventHistoryList, {
279
+ events: history.data,
280
+ formatter: dateTimeFormat,
281
+ renderIdentity: (event) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventIdentity, {
282
+ event,
283
+ eventsLocation,
284
+ touch: true
285
+ })
286
+ })
287
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
288
+ className: "hidden @[58rem]:block",
289
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Table, {
290
+ className: "min-w-4xl table-fixed",
291
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TableRow, {
292
+ className: "[&>th]:px-4 sm:[&>th]:px-6",
293
+ children: [
294
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHead, {
295
+ className: "w-64",
296
+ children: "Time"
297
+ }),
298
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHead, {
299
+ className: "w-48",
300
+ children: "Event"
301
+ }),
302
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHead, {
303
+ className: "w-48",
304
+ children: "User / installation"
305
+ }),
306
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHead, {
307
+ className: "w-32",
308
+ children: "App"
309
+ }),
310
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableHead, {
311
+ className: "w-52",
312
+ children: "Bundle"
313
+ })
314
+ ]
315
+ }) }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableBody, { children: history.data.map((event) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TableRow, {
316
+ className: "[&>td]:px-4 [&>td]:py-4 [&>td]:align-top sm:[&>td]:px-6",
317
+ children: [
318
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
319
+ className: "whitespace-normal text-xs tabular-nums",
320
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventTimestamp, {
321
+ value: event.receivedAtMs,
322
+ formatter: dateTimeFormat
323
+ })
324
+ }),
325
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventTypeBadge, { type: event.type }) }),
326
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventIdentity, {
327
+ event,
328
+ eventsLocation
329
+ }) }),
330
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
331
+ className: "whitespace-normal text-xs",
332
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
333
+ className: "flex flex-col gap-2",
334
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
335
+ event.platform === "ios" ? "iOS" : "Android",
336
+ " ",
337
+ event.appVersion
338
+ ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
339
+ className: "text-muted-foreground",
340
+ children: event.channel
341
+ })]
342
+ })
343
+ }),
344
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TableCell, {
345
+ className: "whitespace-normal text-xs",
346
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventBundleTransition, { event })
347
+ })
348
+ ]
349
+ }, event.id)) })]
350
+ })
351
+ })] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
352
+ className: "px-6 pb-6 text-sm text-muted-foreground",
353
+ children: hasPrevious || history.nextCursor ? "No events on this page" : title === "All events" ? "No events recorded yet" : "No matching reports in this period"
354
+ }), history.data.length > 0 || hasPrevious || history.nextCursor ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsPagination, {
355
+ hasPrevious,
356
+ label: title,
357
+ nextCursor: history.nextCursor,
358
+ onNext,
359
+ onPrevious,
360
+ pageLength: history.data.length,
361
+ pageNumber
362
+ }) : null] }) : null
363
+ })]
364
+ });
365
+ }
15
366
  var getUserLabel$1 = (event) => event.userId ?? event.username ?? "—";
16
367
  var getLastKnownBundleId$1 = (event) => "lastKnownBundleId" in event ? event.lastKnownBundleId : event.toBundleId ?? event.fromBundleId;
17
368
  function InstallationHistoryCard({ error, history, isLoading, onNext, onPrevious, onRefresh, pageNumber, selectedEvent, selectedInstallId }) {
@@ -386,6 +737,70 @@ function InstallationResultsSkeleton() {
386
737
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-64 w-full lg:h-full" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { className: "h-64 w-full lg:h-full" })]
387
738
  });
388
739
  }
740
+ var readOverview = (input) => input;
741
+ var readEventsPage = (input) => input;
742
+ var readInstallationEventsPage = (input) => input;
743
+ var readInstallationPage = (input) => input;
744
+ var readInstallId = (input) => input;
745
+ createServerFn({ method: "GET" }).validator(readOverview).handler(createSsrRpc("ff00311d412c8cdfa8ad5b0ba1eb6aa7471d0cc12170e6cc645deb91603d42dd"));
746
+ var listInsightsEventsRpc = createServerFn({ method: "GET" }).validator(readEventsPage).handler(createSsrRpc("d4c50f40701055145f24873dc90f3c55a5494457a6a03a743339b2ac4545eb19"));
747
+ var listInsightsInstallationEventsRpc = createServerFn({ method: "GET" }).validator(readInstallationEventsPage).handler(createSsrRpc("3c04bc6624be6dfdd80cf96819f9c6d0455e59a7bb4c0374b6afb0162ffd15b0"));
748
+ var getInsightsInstallationRpc = createServerFn({ method: "GET" }).validator(readInstallId).handler(createSsrRpc("6726f1ed9eda1f0cb4c1f1ad1023faee435ddcfd0b7c55ef08ca7f2f7f577370"));
749
+ var findInsightsInstallationsRpc = createServerFn({ method: "GET" }).validator(readInstallationPage).handler(createSsrRpc("735c501f6d5ea81a445ff2b6b2ea6a1b589e3c6f63a465a881822c80ae71e7d1"));
750
+ var STALE_TIME_MS = 3e4;
751
+ var queryKeys = {
752
+ reportingInstallations: (input) => [
753
+ "insights",
754
+ "reporting-installations",
755
+ input
756
+ ],
757
+ events: (input) => [
758
+ "insights",
759
+ "events",
760
+ input
761
+ ],
762
+ installations: (input) => [
763
+ "insights",
764
+ "installations",
765
+ input
766
+ ],
767
+ installation: (installId) => [
768
+ "insights",
769
+ "installation",
770
+ installId
771
+ ],
772
+ installationEvents: (input) => [
773
+ "insights",
774
+ "installation-events",
775
+ input
776
+ ]
777
+ };
778
+ var useInsightsEventsQuery = (input, enabled) => useQuery({
779
+ queryKey: queryKeys.events(input),
780
+ queryFn: () => listInsightsEventsRpc({ data: input }),
781
+ enabled,
782
+ refetchOnWindowFocus: true,
783
+ staleTime: STALE_TIME_MS
784
+ });
785
+ var useInsightsInstallationsQuery = (input, enabled) => useQuery({
786
+ queryKey: queryKeys.installations(input),
787
+ queryFn: () => findInsightsInstallationsRpc({ data: input }),
788
+ enabled: enabled && input.identity.length > 0,
789
+ staleTime: STALE_TIME_MS
790
+ });
791
+ var useInsightsInstallationQuery = (installId, enabled) => useQuery({
792
+ queryKey: queryKeys.installation(installId),
793
+ queryFn: () => getInsightsInstallationRpc({ data: { installId } }),
794
+ enabled: enabled && installId.length > 0,
795
+ staleTime: STALE_TIME_MS
796
+ });
797
+ var useInsightsInstallationEventsQuery = (input, enabled) => useQuery({
798
+ queryKey: queryKeys.installationEvents(input),
799
+ queryFn: () => listInsightsInstallationEventsRpc({ data: input }),
800
+ enabled: enabled && input.installId.length > 0,
801
+ refetchOnWindowFocus: true,
802
+ staleTime: STALE_TIME_MS
803
+ });
389
804
  var SEARCH_LIMIT = 20;
390
805
  var EVENT_LIMIT = 50;
391
806
  var freshBefore = () => Date.now();