@@ -18,13 +18,41 @@ type DashboardArtifact = {
1818function html ( strings : TemplateStringsArray , ...values : unknown [ ] ) : string {
1919 return strings . reduce < string > ( ( result , str , i ) => result + str + ( values [ i ] ?? '' ) , '' ) ;
2020}
21-
2221const isDev = process . env . NODE_ENV === "development" ;
2322
23+ function getEsmFallbackVersion ( version : string ) : string {
24+ const parts = version . split ( "." ) ;
25+ const [ major , minor , patchString ] = parts ;
26+ const patch = Number ( patchString ) ;
27+ return `${ major } .${ minor } .${ patch - 1 } ` ;
28+ }
29+
2430function getDependencyScripts ( esmVersion : string , esmFallbackVersion : string , dashboardUrl : string ) : string {
2531 if ( isDev ) {
2632 return html `
2733 < script type ="module ">
34+ function formatDependencyError ( error ) {
35+ return error instanceof Error ? error . message : String ( error ) ;
36+ }
37+
38+ function reportDependencyError ( message , error ) {
39+ window . parent . postMessage ( {
40+ type : 'dashboard-sandbox-dependency-error' ,
41+ message,
42+ stack : error instanceof Error ? error . stack : undefined ,
43+ } , '*' ) ;
44+ }
45+
46+ function failDependencyLoad ( message , error ) {
47+ reportDependencyError ( message , error ) ;
48+ window . __depsError = {
49+ message,
50+ stack : error instanceof Error ? error . stack : undefined ,
51+ } ;
52+ window . __depsReady = true ;
53+ window . dispatchEvent ( new Event ( 'deps-ready' ) ) ;
54+ }
55+
2856 import React from 'https://esm.sh/react@19.2.3' ;
2957 import * as ReactDOM from 'https://esm.sh/react-dom@19.2.3?deps=react@19.2.3' ;
3058 import * as ReactDOMClient from 'https://esm.sh/react-dom@19.2.3/client?deps=react@19.2.3' ;
@@ -41,14 +69,14 @@ function getDependencyScripts(esmVersion: string, esmFallbackVersion: string, da
4169 window . StackServerApp = StackSDK . StackServerApp ;
4270 window . StackSDK = StackSDK ;
4371 } catch ( e ) {
44- window . parent . postMessage ( { type : 'dashboard-error-boundary' , message : ' [sandbox] Stack SDK failed at version ${ esmVersion } , trying fallback ${ esmFallbackVersion } : ' + e ?. message } , '*' ) ;
72+ reportDependencyError ( ' [sandbox] @stackframe/js failed at version ${ esmVersion } ; trying fallback ${ esmFallbackVersion } : ' + formatDependencyError ( e ) , e ) ;
4573 try {
4674 const StackSDK = await import ( 'https://esm.sh/@stackframe/js@${ esmFallbackVersion } ' ) ;
4775 window . StackAdminApp = StackSDK . StackAdminApp ;
4876 window . StackServerApp = StackSDK . StackServerApp ;
4977 window . StackSDK = StackSDK ;
5078 } catch ( e2 ) {
51- window . parent . postMessage ( { type : 'dashboard-error-boundary' , message : ' [sandbox] Stack SDK fallback also failed: ' + e2 ?. message } , '*' ) ;
79+ failDependencyLoad ( ' [sandbox] @stackframe/js fallback failed at version ${ esmFallbackVersion } : ' + formatDependencyError ( e2 ) , e2 ) ;
5280 }
5381 }
5482 window . generateUuid = ( ) => crypto . randomUUID ( ) ;
@@ -61,17 +89,39 @@ function getDependencyScripts(esmVersion: string, esmFallbackVersion: string, da
6189 window . dispatchEvent ( new Event ( 'deps-ready' ) ) ;
6290 } ;
6391 script . onerror = ( e ) => {
64- window . parent . postMessage ( {
65- type : 'dashboard-error-boundary' ,
66- message : 'Failed to load dashboard-ui-components IIFE bundle' ,
67- } , '*' ) ;
92+ const message = '[sandbox] Failed to load local dashboard-ui-components IIFE bundle. Run pnpm --filter @stackframe/dashboard-ui-components dev or pnpm --filter @stackframe/dashboard-ui-components build so apps/dashboard/public/dashboard-ui-components.iife.js exists.' ;
93+ failDependencyLoad ( message , e instanceof Error ? e : new Error ( message ) ) ;
6894 } ;
6995 document . head . appendChild ( script ) ;
7096 </ script > ` ;
7197 }
7298
7399 return html `
74100 < script type ="module ">
101+ const CUSTOM_DASHBOARD_LOAD_ERROR_MESSAGE = 'There was a problem loading custom dashboards. Please refresh the page and try again.' ;
102+
103+ function formatDependencyError ( error ) {
104+ return error instanceof Error ? error . message : String ( error ) ;
105+ }
106+
107+ function reportDependencyError ( message , error ) {
108+ window . parent . postMessage ( {
109+ type : 'dashboard-sandbox-dependency-error' ,
110+ message,
111+ stack : error instanceof Error ? error . stack : undefined ,
112+ } , '*' ) ;
113+ }
114+
115+ function failDependencyLoad ( message , error ) {
116+ reportDependencyError ( message , error ) ;
117+ window . __depsError = {
118+ message : CUSTOM_DASHBOARD_LOAD_ERROR_MESSAGE ,
119+ stack : error instanceof Error ? error . stack : undefined ,
120+ } ;
121+ window . __depsReady = true ;
122+ window . dispatchEvent ( new Event ( 'deps-ready' ) ) ;
123+ }
124+
75125 import React from 'https://esm.sh/react@19.2.3' ;
76126 import * as ReactDOM from 'https://esm.sh/react-dom@19.2.3?deps=react@19.2.3' ;
77127 import * as ReactDOMClient from 'https://esm.sh/react-dom@19.2.3/client?deps=react@19.2.3' ;
@@ -89,21 +139,27 @@ function getDependencyScripts(esmVersion: string, esmFallbackVersion: string, da
89139 import ( 'https://esm.sh/@stackframe/js@${ esmVersion } ' ) ,
90140 ] ) ;
91141 } catch ( e ) {
92- window . parent . postMessage ( { type : 'dashboard-error-boundary' , message : '[sandbox] Failed to load at version ${ esmVersion } , trying fallback ${ esmFallbackVersion } : ' + e ?. message } , '*' ) ;
93- [ DashboardUIComponents , StackSDK ] = await Promise . all ( [
94- import ( 'https://esm.sh/@stackframe/dashboard-ui-components@${ esmFallbackVersion } ?deps=react@19.2.3,react-dom@19.2.3' ) ,
95- import ( 'https://esm.sh/@stackframe/js@${ esmFallbackVersion } ' ) ,
96- ] ) ;
142+ reportDependencyError ( '[sandbox] Custom dashboard packages failed at version ${ esmVersion } ; trying fallback ${ esmFallbackVersion } : ' + formatDependencyError ( e ) , e ) ;
143+ try {
144+ [ DashboardUIComponents , StackSDK ] = await Promise . all ( [
145+ import ( 'https://esm.sh/@stackframe/dashboard-ui-components@${ esmFallbackVersion } ?deps=react@19.2.3,react-dom@19.2.3' ) ,
146+ import ( 'https://esm.sh/@stackframe/js@${ esmFallbackVersion } ' ) ,
147+ ] ) ;
148+ } catch ( e2 ) {
149+ failDependencyLoad ( '[sandbox] Custom dashboard package fallback failed at version ${ esmFallbackVersion } : ' + formatDependencyError ( e2 ) , e2 ) ;
150+ }
97151 }
98152
99- window . DashboardUI = DashboardUIComponents ;
100- window . StackAdminApp = StackSDK . StackAdminApp ;
101- window . StackServerApp = StackSDK . StackServerApp ;
102- window . StackSDK = StackSDK ;
103- window . generateUuid = ( ) => crypto . randomUUID ( ) ;
153+ if ( ! window . __depsError ) {
154+ window . DashboardUI = DashboardUIComponents ;
155+ window . StackAdminApp = StackSDK . StackAdminApp ;
156+ window . StackServerApp = StackSDK . StackServerApp ;
157+ window . StackSDK = StackSDK ;
158+ window . generateUuid = ( ) => crypto . randomUUID ( ) ;
104159
105- window . __depsReady = true ;
106- window . dispatchEvent ( new Event ( 'deps-ready' ) ) ;
160+ window . __depsReady = true ;
161+ window . dispatchEvent ( new Event ( 'deps-ready' ) ) ;
162+ }
107163 </ script > ` ;
108164}
109165
@@ -118,7 +174,7 @@ function getSandboxDocument(artifact: DashboardArtifact, baseUrl: string, dashbo
118174 const sourceCode = escapeScriptContent ( artifact . runtimeCodegen . uiRuntimeSourceCode ) ;
119175 const darkClass = initialTheme === "dark" ? "dark" : "" ;
120176 const esmVersion = packageJson . version ;
121- const esmFallbackVersion = "2.8.71" ;
177+ const esmFallbackVersion = getEsmFallbackVersion ( esmVersion ) ;
122178 const devScriptSrc = isDev ? ` ${ dashboardUrl } ` : '' ;
123179 const devConnectSrc = isDev ? ` ${ dashboardUrl } ` : '' ;
124180
@@ -307,10 +363,18 @@ function getSandboxDocument(artifact: DashboardArtifact, baseUrl: string, dashbo
307363 } ;
308364
309365 async function waitForDeps ( ) {
310- if ( window . __depsReady ) return ;
311- await new Promise ( resolve => {
312- window . addEventListener ( 'deps-ready' , resolve , { once : true } ) ;
313- } ) ;
366+ if ( ! window . __depsReady ) {
367+ await new Promise ( resolve => {
368+ window . addEventListener ( 'deps-ready' , resolve , { once : true } ) ;
369+ } ) ;
370+ }
371+ if ( window . __depsError ) {
372+ const error = new Error ( window . __depsError . message || 'There was a problem loading custom dashboards. Please refresh the page and try again.' ) ;
373+ if ( window . __depsError . stack ) {
374+ error . stack = window . __depsError . stack ;
375+ }
376+ throw error ;
377+ }
314378 }
315379
316380 async function requestAccessToken ( ) {
@@ -735,6 +799,13 @@ export const DashboardSandboxHost = memo(function DashboardSandboxHost({
735799 return ;
736800 }
737801
802+ if ( type === "dashboard-sandbox-dependency-error" ) {
803+ const err = new Error ( event . data . message ?? 'Unknown custom dashboard dependency error' ) ;
804+ if ( event . data . stack ) err . stack = event . data . stack ;
805+ captureError ( 'dashboard-sandbox-dependency-error' , err ) ;
806+ return ;
807+ }
808+
738809 if ( type === "dashboard-error-boundary" ) {
739810 const err = new Error ( event . data . message ?? 'Unknown dashboard error' ) ;
740811 if ( event . data . stack ) err . stack = event . data . stack ;
0 commit comments