@sentry/junior-dashboard 0.279.0 → 0.280.0
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/app.js +100 -31
- package/dist/client-manifest.json +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +100 -31
- package/dist/public/chunks/AdminPage-YEKZOKH6.js +1 -0
- package/dist/public/chunks/AutomationPage-5GS3IUAK.js +3 -0
- package/dist/public/chunks/AutomationRunsPage-IPYEZU4L.js +1 -0
- package/dist/public/chunks/AutomationsPage-HSAYPC3C.js +1 -0
- package/dist/public/chunks/CodePage-IPFKXWFB.js +1 -0
- package/dist/public/chunks/ConnectionsPage-5XUEK6FS.js +1 -0
- package/dist/public/chunks/DrawerContent-RYYS57ZH.js +1 -0
- package/dist/public/chunks/LocationDetailPage-27ETUGR6.js +1 -0
- package/dist/public/chunks/LocationsPage-WOKMQAGM.js +1 -0
- package/dist/public/chunks/McpSettingsPage-3RT7JPEK.js +1 -0
- package/dist/public/chunks/PeoplePage-6YQLDOUL.js +1 -0
- package/dist/public/chunks/PersonProfilePage-GYMAZFIK.js +1 -0
- package/dist/public/chunks/PersonalTokensPage-4BCR744B.js +1 -0
- package/dist/public/chunks/PluginUserPage-IP6WL7MD.js +1 -0
- package/dist/public/chunks/RepositoryPage-TRD44LQ3.js +1 -0
- package/dist/public/chunks/SettingsPage-4E3SLQ36.js +1 -0
- package/dist/public/chunks/SystemPage-C3XBKXKW.js +1 -0
- package/dist/public/chunks/WorkspaceFormPage-2LS2X67Q.js +2 -0
- package/dist/public/chunks/WorkspacesPage-5WB3CUTX.js +1 -0
- package/dist/public/chunks/chunk-2BCS5AQL.js +1 -0
- package/dist/public/chunks/chunk-3Y3ICT6K.js +1 -0
- package/dist/public/chunks/chunk-4AKUZW6R.js +1 -0
- package/dist/public/chunks/chunk-4UGZNVJM.js +1 -0
- package/dist/public/chunks/chunk-564V7YHG.js +1 -0
- package/dist/public/chunks/chunk-5GZDBGAR.js +1 -0
- package/dist/public/chunks/chunk-5ISD3QSC.js +1 -0
- package/dist/public/chunks/chunk-5LNGNJ3P.js +1 -0
- package/dist/public/chunks/chunk-5ZA674OU.js +1 -0
- package/dist/public/chunks/chunk-6FSJ3W2W.js +1 -0
- package/dist/public/chunks/chunk-6GQL3EVJ.js +1 -0
- package/dist/public/chunks/chunk-6PHJZPWD.js +19 -0
- package/dist/public/chunks/chunk-A2QWWA7O.js +41 -0
- package/dist/public/chunks/chunk-AYXSBMWA.js +1 -0
- package/dist/public/chunks/chunk-B5ZN3SWQ.js +14 -0
- package/dist/public/chunks/chunk-B6HGRBBX.js +1 -0
- package/dist/public/chunks/chunk-BKOACADP.js +1 -0
- package/dist/public/chunks/chunk-CQY6BYUB.js +1 -0
- package/dist/public/chunks/chunk-CR2ICLSH.js +1 -0
- package/dist/public/chunks/chunk-DZB4LPI6.js +1 -0
- package/dist/public/chunks/chunk-EFEREKX4.js +1 -0
- package/dist/public/chunks/chunk-FE522UEA.js +1 -0
- package/dist/public/chunks/chunk-FVX42XAD.js +1 -0
- package/dist/public/chunks/chunk-G5NWYJTV.js +1 -0
- package/dist/public/chunks/chunk-GMZIELFU.js +1 -0
- package/dist/public/chunks/chunk-GO45RQDL.js +1 -0
- package/dist/public/chunks/chunk-GOTQ4D6W.js +1 -0
- package/dist/public/chunks/chunk-GRMX2HX6.js +1 -0
- package/dist/public/chunks/chunk-GYRM5ZIV.js +1 -0
- package/dist/public/chunks/chunk-H2ZU34CP.js +1 -0
- package/dist/public/chunks/chunk-H76B43OQ.js +1 -0
- package/dist/public/chunks/chunk-HCLRG2EC.js +1 -0
- package/dist/public/chunks/chunk-IA6BJDKN.js +1 -0
- package/dist/public/chunks/chunk-IZWTFLCC.js +1 -0
- package/dist/public/chunks/chunk-J37MXUED.js +1 -0
- package/dist/public/chunks/chunk-JAZABOBA.js +0 -0
- package/dist/public/chunks/chunk-L2H3ZWZN.js +1 -0
- package/dist/public/chunks/chunk-LBELRO5P.js +1 -0
- package/dist/public/chunks/chunk-LKW7MULF.js +1 -0
- package/dist/public/chunks/chunk-MEHLQZDG.js +3 -0
- package/dist/public/chunks/chunk-MTBUV2GJ.js +1 -0
- package/dist/public/chunks/chunk-N4AONKD3.js +1 -0
- package/dist/public/chunks/chunk-NQMDED2R.js +2 -0
- package/dist/public/chunks/chunk-NQOJAZFQ.js +1 -0
- package/dist/public/chunks/chunk-PEQNT5MO.js +1 -0
- package/dist/public/chunks/chunk-PQPAPPEI.js +1 -0
- package/dist/public/chunks/chunk-PXHLJIOT.js +1 -0
- package/dist/public/chunks/chunk-QNA7F7QB.js +1 -0
- package/dist/public/chunks/chunk-QOX5RMLM.js +1 -0
- package/dist/public/chunks/chunk-QPBLZSNX.js +1 -0
- package/dist/public/chunks/chunk-R4ORRE77.js +1 -0
- package/dist/public/chunks/chunk-RHIXHBS5.js +1 -0
- package/dist/public/chunks/chunk-RHMZNAJX.js +1 -0
- package/dist/public/chunks/chunk-SDKV3OID.js +1 -0
- package/dist/public/chunks/chunk-SIOW7OOW.js +1 -0
- package/dist/public/chunks/chunk-SUX4QBVF.js +1 -0
- package/dist/public/chunks/chunk-T4M53QQT.js +1 -0
- package/dist/public/chunks/chunk-TFJOEF3Q.js +1 -0
- package/dist/public/chunks/chunk-UAH6DWNS.js +1 -0
- package/dist/public/chunks/chunk-UJD3MBMB.js +1 -0
- package/dist/public/chunks/chunk-UK2V4UBT.js +1 -0
- package/dist/public/chunks/chunk-UT6HZPGL.js +1 -0
- package/dist/public/chunks/chunk-UZ44J2LH.js +1 -0
- package/dist/public/chunks/chunk-V5UDSIRA.js +63 -0
- package/dist/public/chunks/chunk-VC6IS564.js +1 -0
- package/dist/public/chunks/chunk-VFEBNOPX.js +1 -0
- package/dist/public/chunks/chunk-VXT3FIVL.js +55 -0
- package/dist/public/chunks/chunk-WJ3OXMVX.js +1 -0
- package/dist/public/chunks/chunk-WRXMQB7X.js +25 -0
- package/dist/public/chunks/chunk-WZVO3HG4.js +1 -0
- package/dist/public/chunks/chunk-XNR4RZEC.js +1 -0
- package/dist/public/chunks/chunk-YPMUHXYT.js +147 -0
- package/dist/public/chunks/chunk-YYNMZ4WC.js +1 -0
- package/dist/public/chunks/chunk-Z2JPRGFT.js +1 -0
- package/dist/public/chunks/chunk-ZIJ6SJDN.js +1 -0
- package/dist/public/chunks/code-highlighter-Q7WC7AEF.js +12 -0
- package/dist/public/client-TYC7JKRE.js +91 -0
- package/dist/tailwind.css +1 -1
- package/package.json +5 -4
- package/src/api/schema.ts +1 -1
- package/src/assets.ts +4 -1
- package/src/client/App.tsx +100 -23
- package/src/client/TracedRoutes.tsx +8 -4
- package/src/client/code-highlighter.ts +26 -0
- package/src/client/code.tsx +2 -22
- package/src/client/components/Drawer.tsx +12 -71
- package/src/client/components/DrawerContent.tsx +76 -0
- package/src/client/components/ProfileMenu.tsx +12 -3
- package/src/client/components/UserInputForm.tsx +28 -5
- package/src/client/conversations/ConversationCatchUp.tsx +107 -0
- package/src/client/conversations/ConversationContextTags.tsx +110 -0
- package/src/client/conversations/ConversationEventLog.tsx +213 -71
- package/src/client/conversations/ConversationHeader.tsx +35 -41
- package/src/client/conversations/ConversationHeaderActions.tsx +34 -38
- package/src/client/conversations/ConversationHomeList.tsx +41 -3
- package/src/client/conversations/ConversationMeta.tsx +6 -12
- package/src/client/conversations/ConversationOverflowMenu.tsx +15 -4
- package/src/client/conversations/ConversationPage.tsx +188 -150
- package/src/client/conversations/ShareConversationButton.tsx +5 -50
- package/src/client/conversations/SlackMark.tsx +16 -4
- package/src/client/conversations/TranscriptRows.tsx +24 -11
- package/src/client/conversations/TranscriptView.tsx +21 -13
- package/src/client/conversations/conversationArchive.ts +22 -0
- package/src/client/conversations/conversationCatchUpState.ts +96 -0
- package/src/client/conversations/conversationMemory.ts +8 -1
- package/src/client/conversations/queries.ts +11 -3
- package/src/client/conversations/{transcriptUnreadHistory.ts → transcriptAroundHistory.ts} +48 -42
- package/src/client/conversations/transcriptBottomPinning.ts +27 -3
- package/src/client/conversations/transcriptReadPosition.ts +24 -2
- package/src/client/conversations/turnContext.ts +1 -1
- package/src/client/pages/admin/AdminPage.tsx +1 -1
- package/src/client/pages/admin/ServiceAccountsSection.tsx +1 -1
- package/src/client/pages/memory/memoryDashboard.ts +1 -1
- package/src/client/pages/memory/memoryRecord.ts +1 -1
- package/src/client/pages/settings/ConnectionsPage.tsx +1 -1
- package/src/client/pages/user/PluginUserPage.tsx +0 -6
- package/src/client/pages/user/pluginUserPageRoutes.ts +5 -0
- package/src/client/primaryNav.ts +1 -1
- package/src/client/review-desk/reviewItemFormat.ts +1 -1
- package/src/mock-reporting/fixtures.ts +43 -0
- package/src/mock-reporting/routes.ts +40 -3
- package/src/public-assets.ts +28 -20
- package/src/shell.ts +12 -2
- package/dist/public/client-ZCZXZBTL.js +0 -460
|
@@ -27,12 +27,6 @@ import {
|
|
|
27
27
|
type PluginUserPageRecordAction,
|
|
28
28
|
} from "./pluginUserPageData";
|
|
29
29
|
|
|
30
|
-
/** Build the canonical dashboard path for a plugin-owned page. */
|
|
31
|
-
export function pluginUserPagePath(pluginName: string, pageId: string): string {
|
|
32
|
-
if (pluginName === "memory" && pageId === "memories") return "/memories";
|
|
33
|
-
return `/plugins/${encodeURIComponent(pluginName)}/${encodeURIComponent(pageId)}`;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
30
|
/** Render the memory library for its first-class permalink route. */
|
|
37
31
|
export function MemoryPermalinkRoute(props: { pages: PluginUserPageLink[] }) {
|
|
38
32
|
const page = props.pages.find(
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Build the canonical dashboard path for a plugin-owned page. */
|
|
2
|
+
export function pluginUserPagePath(pluginName: string, pageId: string): string {
|
|
3
|
+
if (pluginName === "memory" && pageId === "memories") return "/memories";
|
|
4
|
+
return `/plugins/${encodeURIComponent(pluginName)}/${encodeURIComponent(pageId)}`;
|
|
5
|
+
}
|
package/src/client/primaryNav.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PluginUserPageLink } from "@sentry/junior-plugin-api";
|
|
2
2
|
|
|
3
3
|
import type { DashboardHeaderNavItem } from "./components/layout/DashboardHeader";
|
|
4
|
-
import { pluginUserPagePath } from "./pages/user/
|
|
4
|
+
import { pluginUserPagePath } from "./pages/user/pluginUserPageRoutes";
|
|
5
5
|
|
|
6
6
|
const AUTH_PRIMARY_NAV_PREFIXES = [
|
|
7
7
|
"/admin",
|
|
@@ -393,6 +393,7 @@ function activeConversation(nowMs: number): ConversationDetailReport {
|
|
|
393
393
|
kind: "resource_link",
|
|
394
394
|
key: "getsentry/payments#42",
|
|
395
395
|
label: "getsentry/payments#42",
|
|
396
|
+
objectType: "code_change",
|
|
396
397
|
plugin: "github",
|
|
397
398
|
status: "open",
|
|
398
399
|
url: "https://github.com/getsentry/payments/pull/42",
|
|
@@ -685,6 +686,7 @@ function dashboardQaConversation(nowMs: number): ConversationDetailReport {
|
|
|
685
686
|
kind: "resource_link",
|
|
686
687
|
key: "getsentry/getsentry#21571",
|
|
687
688
|
label: "getsentry/getsentry#21571",
|
|
689
|
+
objectType: "code_change",
|
|
688
690
|
plugin: "github",
|
|
689
691
|
status: "merged",
|
|
690
692
|
url: "https://github.com/getsentry/getsentry/pull/21571",
|
|
@@ -695,6 +697,7 @@ function dashboardQaConversation(nowMs: number): ConversationDetailReport {
|
|
|
695
697
|
kind: "resource_link",
|
|
696
698
|
key: "getsentry/getsentry#21572",
|
|
697
699
|
label: "getsentry/getsentry#21572",
|
|
700
|
+
objectType: "code_change",
|
|
698
701
|
plugin: "github",
|
|
699
702
|
status: "open",
|
|
700
703
|
url: "https://github.com/getsentry/getsentry/pull/21572",
|
|
@@ -705,6 +708,7 @@ function dashboardQaConversation(nowMs: number): ConversationDetailReport {
|
|
|
705
708
|
kind: "resource_link",
|
|
706
709
|
key: "getsentry/getsentry#21569",
|
|
707
710
|
label: "getsentry/getsentry#21569",
|
|
711
|
+
objectType: "code_change",
|
|
708
712
|
plugin: "github",
|
|
709
713
|
status: "draft",
|
|
710
714
|
url: "https://github.com/getsentry/getsentry/pull/21569",
|
|
@@ -715,6 +719,7 @@ function dashboardQaConversation(nowMs: number): ConversationDetailReport {
|
|
|
715
719
|
kind: "resource_link",
|
|
716
720
|
key: "getsentry/sentry#121727",
|
|
717
721
|
label: "getsentry/sentry#121727",
|
|
722
|
+
objectType: "code_change",
|
|
718
723
|
plugin: "github",
|
|
719
724
|
status: "merged",
|
|
720
725
|
url: "https://github.com/getsentry/sentry/pull/121727",
|
|
@@ -1180,6 +1185,7 @@ function longConversation(nowMs: number): ConversationDetailReport {
|
|
|
1180
1185
|
kind: "resource_link",
|
|
1181
1186
|
key: "getsentry/junior#2201",
|
|
1182
1187
|
label: "getsentry/junior#2201",
|
|
1188
|
+
objectType: "code_change",
|
|
1183
1189
|
plugin: "github",
|
|
1184
1190
|
status: "draft",
|
|
1185
1191
|
url: "https://github.com/getsentry/junior/pull/2201",
|
|
@@ -1190,6 +1196,7 @@ function longConversation(nowMs: number): ConversationDetailReport {
|
|
|
1190
1196
|
kind: "resource_link",
|
|
1191
1197
|
key: "getsentry/payments#91",
|
|
1192
1198
|
label: "getsentry/payments#91",
|
|
1199
|
+
objectType: "code_change",
|
|
1193
1200
|
plugin: "github",
|
|
1194
1201
|
status: "open",
|
|
1195
1202
|
url: "https://github.com/getsentry/payments/pull/91",
|
|
@@ -1200,6 +1207,7 @@ function longConversation(nowMs: number): ConversationDetailReport {
|
|
|
1200
1207
|
kind: "resource_link",
|
|
1201
1208
|
key: "getsentry/relay#44",
|
|
1202
1209
|
label: "getsentry/relay#44",
|
|
1210
|
+
objectType: "code_change",
|
|
1203
1211
|
plugin: "github",
|
|
1204
1212
|
status: "merged",
|
|
1205
1213
|
url: "https://github.com/getsentry/relay/pull/44",
|
|
@@ -1268,6 +1276,7 @@ function incidentConversation(nowMs: number): ConversationDetailReport {
|
|
|
1268
1276
|
kind: "resource_link",
|
|
1269
1277
|
key: "getsentry/payments#77",
|
|
1270
1278
|
label: "getsentry/payments#77",
|
|
1279
|
+
objectType: "code_change",
|
|
1271
1280
|
plugin: "github",
|
|
1272
1281
|
status: "merged",
|
|
1273
1282
|
url: "https://github.com/getsentry/payments/pull/77",
|
|
@@ -1278,6 +1287,7 @@ function incidentConversation(nowMs: number): ConversationDetailReport {
|
|
|
1278
1287
|
kind: "resource_link",
|
|
1279
1288
|
key: "getsentry/payments#61",
|
|
1280
1289
|
label: "getsentry/payments#61",
|
|
1290
|
+
objectType: "task",
|
|
1281
1291
|
plugin: "github",
|
|
1282
1292
|
status: "closed",
|
|
1283
1293
|
url: "https://github.com/getsentry/payments/issues/61",
|
|
@@ -2281,6 +2291,39 @@ export function readMockConversationEvents(
|
|
|
2281
2291
|
};
|
|
2282
2292
|
}
|
|
2283
2293
|
|
|
2294
|
+
/**
|
|
2295
|
+
* Return the page around a linked event or Turn. Mock transcripts fit one
|
|
2296
|
+
* page, so it holds every event of the Conversation.
|
|
2297
|
+
*/
|
|
2298
|
+
export function readMockConversationEventsAround(
|
|
2299
|
+
conversationId: string,
|
|
2300
|
+
link: { event: number } | { turn: string },
|
|
2301
|
+
): ConversationEventPage | "turn-not-found" | undefined {
|
|
2302
|
+
const conversation = mockConversations(NOW_MS).find(
|
|
2303
|
+
(candidate) => candidate.conversationId === conversationId,
|
|
2304
|
+
);
|
|
2305
|
+
if (!conversation) return undefined;
|
|
2306
|
+
if (
|
|
2307
|
+
"turn" in link &&
|
|
2308
|
+
!conversation.events.some(
|
|
2309
|
+
({ data }) =>
|
|
2310
|
+
data.type === "turn_lifecycle" &&
|
|
2311
|
+
data.state === "started" &&
|
|
2312
|
+
data.turnId === link.turn,
|
|
2313
|
+
)
|
|
2314
|
+
) {
|
|
2315
|
+
return "turn-not-found";
|
|
2316
|
+
}
|
|
2317
|
+
return {
|
|
2318
|
+
events: conversation.events,
|
|
2319
|
+
eventHistory: conversation.eventHistory,
|
|
2320
|
+
generatedAt: NOW,
|
|
2321
|
+
...(conversation.previousCursor
|
|
2322
|
+
? { previousCursor: conversation.previousCursor }
|
|
2323
|
+
: undefined),
|
|
2324
|
+
};
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2284
2327
|
function mockBeforeCursor(conversationId: string, seq: number): string {
|
|
2285
2328
|
return `mock:before:${encodeURIComponent(conversationId)}:${seq}`;
|
|
2286
2329
|
}
|
|
@@ -42,6 +42,8 @@ import {
|
|
|
42
42
|
automationParamsSchema,
|
|
43
43
|
automationSummarySchema,
|
|
44
44
|
automationRunListSchema,
|
|
45
|
+
conversationCatchUpBodySchema,
|
|
46
|
+
conversationCatchUpSchema,
|
|
45
47
|
} from "@sentry/junior/api/schema";
|
|
46
48
|
import { mockChartPng } from "./chart-png";
|
|
47
49
|
import {
|
|
@@ -56,6 +58,7 @@ import {
|
|
|
56
58
|
readMockConversationChildAgents,
|
|
57
59
|
readMockConversationDetail,
|
|
58
60
|
readMockConversationEvents,
|
|
61
|
+
readMockConversationEventsAround,
|
|
59
62
|
readMockConversationFeed,
|
|
60
63
|
readMockConversationPendingMessages,
|
|
61
64
|
readMockConversationStats,
|
|
@@ -198,14 +201,30 @@ export function createMockReportingApi(): Hono<{
|
|
|
198
201
|
if (!params.success) {
|
|
199
202
|
return errorResponse("Invalid route parameters.", 400);
|
|
200
203
|
}
|
|
201
|
-
|
|
202
|
-
// only pages backward.
|
|
203
|
-
if (!query.success || !("before" in query.data)) {
|
|
204
|
+
if (!query.success) {
|
|
204
205
|
return errorResponse("Invalid query parameters.", 400);
|
|
205
206
|
}
|
|
206
207
|
if (!readMockConversationDetail(params.data.conversationId)) {
|
|
207
208
|
return errorResponse("Conversation not found.", 404);
|
|
208
209
|
}
|
|
210
|
+
if ("event" in query.data || "turn" in query.data) {
|
|
211
|
+
const around = readMockConversationEventsAround(
|
|
212
|
+
params.data.conversationId,
|
|
213
|
+
"event" in query.data
|
|
214
|
+
? { event: query.data.event }
|
|
215
|
+
: { turn: query.data.turn },
|
|
216
|
+
);
|
|
217
|
+
return around === "turn-not-found"
|
|
218
|
+
? errorResponse("Turn not found.", 404)
|
|
219
|
+
: around
|
|
220
|
+
? jsonResponse(conversationEventPageSchema, around)
|
|
221
|
+
: errorResponse("Conversation not found.", 404);
|
|
222
|
+
}
|
|
223
|
+
// Mock details fit one page and have no unread cursor, so mock history
|
|
224
|
+
// pages only backward from cursors.
|
|
225
|
+
if (!("before" in query.data)) {
|
|
226
|
+
return errorResponse("Invalid query parameters.", 400);
|
|
227
|
+
}
|
|
209
228
|
const report = readMockConversationEvents(
|
|
210
229
|
params.data.conversationId,
|
|
211
230
|
query.data.before,
|
|
@@ -287,6 +306,24 @@ export function createMockReportingApi(): Hono<{
|
|
|
287
306
|
? jsonResponse(conversationReadPositionSchema, result)
|
|
288
307
|
: errorResponse("Conversation not found.", 404);
|
|
289
308
|
});
|
|
309
|
+
app.post("/conversations/:conversationId/catch-up", async (c) => {
|
|
310
|
+
const params = conversationParamsSchema.safeParse(c.req.param());
|
|
311
|
+
if (!params.success) {
|
|
312
|
+
return errorResponse("Invalid route parameters.", 400);
|
|
313
|
+
}
|
|
314
|
+
const body = conversationCatchUpBodySchema.safeParse(await c.req.json());
|
|
315
|
+
if (!body.success) {
|
|
316
|
+
return errorResponse("Invalid request body.", 400);
|
|
317
|
+
}
|
|
318
|
+
if (!readMockConversationDetail(params.data.conversationId)) {
|
|
319
|
+
return errorResponse("Conversation not found.", 404);
|
|
320
|
+
}
|
|
321
|
+
return jsonResponse(conversationCatchUpSchema, {
|
|
322
|
+
needsYou: "Review the Safari fix on PR #12.",
|
|
323
|
+
summary:
|
|
324
|
+
"CI failed on Safari after you left. Junior pushed a fix to PR #12, and the checks pass now.",
|
|
325
|
+
});
|
|
326
|
+
});
|
|
290
327
|
app.patch("/conversations/:conversationId/archive", async (c) => {
|
|
291
328
|
const params = conversationParamsSchema.safeParse(c.req.param());
|
|
292
329
|
if (!params.success) {
|
package/src/public-assets.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { existsSync,
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import
|
|
4
|
+
import * as z from "zod";
|
|
5
|
+
import { dashboardClientManifest, dashboardPublicAssets } from "./assets";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* URL prefix for the content-hashed dashboard browser files. `juniorNitro()`
|
|
@@ -33,28 +34,35 @@ function publicDir(): string | undefined {
|
|
|
33
34
|
].find((dir) => existsSync(dir));
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
/** File under `dist` where the client build names its entry and imports. */
|
|
38
|
+
export const DASHBOARD_CLIENT_MANIFEST_FILE = "client-manifest.json";
|
|
39
|
+
|
|
40
|
+
/** The client entry and every file it imports statically. */
|
|
41
|
+
export const dashboardClientManifestSchema = z.object({
|
|
42
|
+
entry: z.string().regex(DASHBOARD_CLIENT_ENTRY_NAME),
|
|
43
|
+
imports: z.array(z.string().regex(DASHBOARD_PUBLIC_ASSET_NAME)),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
/** The dashboard client entry and every file it imports statically. */
|
|
47
|
+
export type DashboardClientManifest = z.infer<
|
|
48
|
+
typeof dashboardClientManifestSchema
|
|
49
|
+
>;
|
|
50
|
+
|
|
36
51
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* file.
|
|
52
|
+
* Read the client manifest. Without embedded assets, read the build output on
|
|
53
|
+
* each call, so a client rebuild never leaves the page on a deleted file.
|
|
40
54
|
*/
|
|
41
|
-
export function
|
|
42
|
-
if (
|
|
55
|
+
export function readDashboardClientManifest(): DashboardClientManifest {
|
|
56
|
+
if (dashboardClientManifest) return dashboardClientManifest;
|
|
43
57
|
const dir = publicDir();
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
.filter((file) => DASHBOARD_CLIENT_ENTRY_NAME.test(file))
|
|
48
|
-
.map((file) => ({
|
|
49
|
-
file,
|
|
50
|
-
mtimeMs: statSync(path.join(dir, file)).mtimeMs,
|
|
51
|
-
}))
|
|
52
|
-
.sort((a, b) => b.mtimeMs - a.mtimeMs)[0]
|
|
53
|
-
: undefined;
|
|
54
|
-
if (!newest) {
|
|
58
|
+
const manifestPath =
|
|
59
|
+
dir && path.join(dir, "..", DASHBOARD_CLIENT_MANIFEST_FILE);
|
|
60
|
+
if (!manifestPath || !existsSync(manifestPath)) {
|
|
55
61
|
throw new Error("Junior dashboard client bundle was not found");
|
|
56
62
|
}
|
|
57
|
-
return
|
|
63
|
+
return dashboardClientManifestSchema.parse(
|
|
64
|
+
JSON.parse(readFileSync(manifestPath, "utf8")),
|
|
65
|
+
);
|
|
58
66
|
}
|
|
59
67
|
|
|
60
68
|
function readPublicAsset(file: string): string | undefined {
|
|
@@ -63,7 +71,7 @@ function readPublicAsset(file: string): string | undefined {
|
|
|
63
71
|
return dashboardPublicAssets[file];
|
|
64
72
|
}
|
|
65
73
|
// An embedded build has every file it can serve.
|
|
66
|
-
if (
|
|
74
|
+
if (dashboardClientManifest) return undefined;
|
|
67
75
|
const dir = publicDir();
|
|
68
76
|
const assetPath = dir ? path.join(dir, file) : undefined;
|
|
69
77
|
return assetPath && existsSync(assetPath)
|
package/src/shell.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
import { dashboardRainbowProgressClass } from "./dashboardLoader";
|
|
12
12
|
import {
|
|
13
13
|
DASHBOARD_ASSETS_PATH,
|
|
14
|
-
|
|
14
|
+
readDashboardClientManifest,
|
|
15
15
|
} from "./public-assets";
|
|
16
16
|
import { DASHBOARD_THEME_COLORS, DASHBOARD_THEME_SCRIPT } from "./theme";
|
|
17
17
|
|
|
@@ -217,6 +217,15 @@ export function renderDashboard(
|
|
|
217
217
|
const bootstrapScript = bootstrap
|
|
218
218
|
? `window.__JUNIOR_DASHBOARD_BOOTSTRAP__ = ${scriptJson(bootstrap)};`
|
|
219
219
|
: "";
|
|
220
|
+
const client = readDashboardClientManifest();
|
|
221
|
+
// Without these hints, the browser finds the chunks only after the entry
|
|
222
|
+
// has downloaded and parsed, one round trip later.
|
|
223
|
+
const clientPreloads = client.imports
|
|
224
|
+
.map(
|
|
225
|
+
(file) =>
|
|
226
|
+
`<link rel="modulepreload" href="${DASHBOARD_ASSETS_PATH}/${file}" />`,
|
|
227
|
+
)
|
|
228
|
+
.join("\n ");
|
|
220
229
|
return new Response(
|
|
221
230
|
`<!DOCTYPE html>
|
|
222
231
|
<html lang="en">
|
|
@@ -231,6 +240,7 @@ export function renderDashboard(
|
|
|
231
240
|
<meta name="apple-mobile-web-app-title" content="${escapeHtml(agentName)}" />
|
|
232
241
|
<link rel="manifest" href="${DASHBOARD_MANIFEST_PATH}" />
|
|
233
242
|
<link rel="apple-touch-icon" href="${DASHBOARD_INSTALL_ICON_PATH}" />
|
|
243
|
+
${clientPreloads}
|
|
234
244
|
<title>${escapeHtml(agentName)}</title>
|
|
235
245
|
${renderSentryHead(sentry)}
|
|
236
246
|
<style>
|
|
@@ -297,7 +307,7 @@ export function renderDashboard(
|
|
|
297
307
|
});
|
|
298
308
|
})();
|
|
299
309
|
</script>
|
|
300
|
-
<script type="module" src="${DASHBOARD_ASSETS_PATH}/${
|
|
310
|
+
<script type="module" src="${DASHBOARD_ASSETS_PATH}/${client.entry}"></script>
|
|
301
311
|
</body>
|
|
302
312
|
</html>`,
|
|
303
313
|
{
|