191191
192192 const runtimeError = ( phase , code , message , data = null ) => {
193193 const error = new Error ( message ) ;
194+ error . schema = 'wp-codebox/browser-runtime-error/v1' ;
194195 error . phase = phase ;
195196 error . code = code ;
196197 error . data = data ;
739740 } ;
740741 } ;
741742
742- const browserSdkMethodContract = Object . freeze ( [
743- 'activateTheme' ,
744- 'browserSessionRecipe' ,
745- 'createBrowserConnectorRequest' ,
746- 'executeBrowserConnectorRequest' ,
747- 'executeBrowserProviderProxyRequest' ,
748- 'consumeContainedSiteSync' ,
749- 'openOrCreateBrowserContainedSite' ,
750- 'startBrowserPreview' ,
751- 'bootExecutableBrowserSession' ,
752- 'createParentToolRequest' ,
753- 'dispatchParentTool' ,
754- 'ensureDirectory' ,
755- 'readFile' ,
756- 'listDirectory' ,
757- 'grep' ,
758- 'editFile' ,
759- 'applyPatch' ,
760- 'executableBrowserSession' ,
761- 'installTheme' ,
762- 'parentToolBridge' ,
763- 'validateBrowserRuntimeMaterialization' ,
764- 'aggregateFanoutOutputs' ,
765- 'preparedBrowserRuntimeContract' ,
766- 'preparedBrowserRuntimeStatus' ,
767- 'runBrowserRuntimeContractProbe' ,
768- 'runBrowserSessionRecipe' ,
769- 'runPhpRequest' ,
770- 'runRecipe' ,
771- 'runWordPressOperation' ,
772- 'selectPreparedBrowserBlueprint' ,
773- 'setFrontendAdminBarVisible' ,
774- 'writeFile' ,
775- 'writeReviewFile' ,
743+ const browserSdkContract = Object . freeze ( [
744+ { name : 'activateTheme' } ,
745+ { name : 'browserSessionRecipe' } ,
746+ { name : 'createBrowserConnectorRequest' , topLevelOrder : 16 , topLevel : ( api ) => api . createBrowserConnectorRequest } ,
747+ { name : 'executeBrowserConnectorRequest' , topLevelOrder : 17 , topLevel : ( api ) => api . executeBrowserConnectorRequest } ,
748+ { name : 'executeBrowserProviderProxyRequest' } ,
749+ { name : 'consumeContainedSiteSync' , topLevelOrder : 1 , topLevel : ( api ) => ( client , delegation , options = { } ) => api . consumeContainedSiteSync ( client , delegation , options ) } ,
750+ { name : 'openOrCreateBrowserContainedSite' , topLevelOrder : 2 , topLevel : ( api ) => ( input = { } , options = { } ) => api . openOrCreateBrowserContainedSite ( input , options ) } ,
751+ { name : 'startBrowserPreview' , topLevelOrder : 3 , topLevel : ( api ) => ( input , options = { } ) => api . startBrowserPreview ( input , options ) } ,
752+ { name : 'bootExecutableBrowserSession' , topLevelOrder : 14 , topLevel : ( api ) => async ( client , session , options = { } ) => normalizeBrowserRunResult ( await api . bootExecutableBrowserSession ( client , session , options ) , 'browser-executable-session' ) } ,
753+ { name : 'createParentToolRequest' , topLevelOrder : 18 , topLevel : ( api ) => api . createParentToolRequest } ,
754+ { name : 'dispatchParentTool' , topLevelOrder : 19 , topLevel : ( api ) => api . dispatchParentTool } ,
755+ { name : 'ensureDirectory' , topLevelOrder : 6 , topLevel : ( api ) => ( client , args = { } , options = { } ) => api . ensureDirectory ( client , args , options ) } ,
756+ { name : 'readFile' , topLevelOrder : 8 , topLevel : ( api ) => ( client , args = { } , options = { } ) => api . readFile ( client , args , options ) } ,
757+ { name : 'listDirectory' , topLevelOrder : 9 , topLevel : ( api ) => ( client , args = { } , options = { } ) => api . listDirectory ( client , args , options ) } ,
758+ { name : 'grep' , topLevelOrder : 10 , topLevel : ( api ) => ( client , args = { } , options = { } ) => api . grep ( client , args , options ) } ,
759+ { name : 'editFile' , topLevelOrder : 11 , topLevel : ( api ) => ( client , args = { } , options = { } ) => api . editFile ( client , args , options ) } ,
760+ { name : 'applyPatch' , topLevelOrder : 12 , topLevel : ( api ) => ( client , args = { } , options = { } ) => api . applyPatch ( client , args , options ) } ,
761+ { name : 'executableBrowserSession' , topLevelOrder : 13 , topLevel : ( api ) => api . executableBrowserSession } ,
762+ { name : 'installTheme' } ,
763+ { name : 'parentToolBridge' , topLevelOrder : 15 , topLevel : ( api ) => api . parentToolBridge } ,
764+ { name : 'validateBrowserRuntimeMaterialization' , topLevelOrder : 5 , topLevel : ( api ) => ( client , session , options = { } ) => api . validateBrowserRuntimeMaterialization ( client , session , options ) } ,
765+ { name : 'aggregateFanoutOutputs' , topLevelOrder : 4 , topLevel : ( api ) => ( input ) => api . aggregateFanoutOutputs ( input ) } ,
766+ { name : 'preparedBrowserRuntimeContract' } ,
767+ { name : 'preparedBrowserRuntimeStatus' } ,
768+ { name : 'runBrowserRuntimeContractProbe' } ,
769+ { name : 'runBrowserSessionRecipe' , topLevelOrder : 20 , topLevel : ( api ) => async ( client , session , taskPayload , options = { } ) => normalizeBrowserRunResult ( await api . runBrowserSessionRecipe ( client , session , taskPayload , options ) , 'browser-session-recipe' ) } ,
770+ { name : 'runPhpRequest' } ,
771+ { name : 'runRecipe' , topLevelOrder : 12.5 , topLevel : ( api ) => ( client , recipe , taskPayload , options = { } ) => api . runRecipe ( client , recipe , taskPayload , options ) } ,
772+ { name : 'runWordPressOperation' } ,
773+ { name : 'selectPreparedBrowserBlueprint' } ,
774+ { name : 'setFrontendAdminBarVisible' , topLevelOrder : 22 , topLevel : ( api ) => ( client , args = { } , options = { } ) => api . setFrontendAdminBarVisible ( client , args , options ) } ,
775+ { name : 'writeFile' , topLevelOrder : 7 , topLevel : ( api ) => ( client , args = { } , options = { } ) => api . writeFile ( client , args , options ) } ,
776+ { name : 'writeReviewFile' } ,
776777 ] ) ;
777778
778- const browserSdkTopLevelContract = Object . freeze ( {
779- capabilities : ( ) => ( ) => browserSdkInfo ( ) . capabilities ,
780- getCapabilities : ( ) => ( ) => browserSdkInfo ( ) ,
781- info : ( ) => browserSdkInfo ,
782- normalizeError : ( ) => normalizeBrowserSdkError ,
783- normalizeBrowserRunResult : ( ) => normalizeBrowserRunResult ,
784- browserArtifactPersistenceRef : ( ) => browserArtifactPersistenceRef ,
785- createRuntimeTaskRequest : ( ) => createRuntimeTaskRequest ,
786- runRuntimeTask : ( ) => runRuntimeTask ,
787- consumeContainedSiteSync : ( api ) => ( client , delegation , options = { } ) => api . consumeContainedSiteSync ( client , delegation , options ) ,
788- openOrCreateBrowserContainedSite : ( api ) => ( input = { } , options = { } ) => api . openOrCreateBrowserContainedSite ( input , options ) ,
789- startBrowserPreview : ( api ) => ( input , options = { } ) => api . startBrowserPreview ( input , options ) ,
790- aggregateFanoutOutputs : ( api ) => ( input ) => api . aggregateFanoutOutputs ( input ) ,
791- validateBrowserRuntimeMaterialization : ( api ) => ( client , session , options = { } ) => api . validateBrowserRuntimeMaterialization ( client , session , options ) ,
792- ensureDirectory : ( api ) => ( client , args = { } , options = { } ) => api . ensureDirectory ( client , args , options ) ,
793- writeFile : ( api ) => ( client , args = { } , options = { } ) => api . writeFile ( client , args , options ) ,
794- readFile : ( api ) => ( client , args = { } , options = { } ) => api . readFile ( client , args , options ) ,
795- listDirectory : ( api ) => ( client , args = { } , options = { } ) => api . listDirectory ( client , args , options ) ,
796- grep : ( api ) => ( client , args = { } , options = { } ) => api . grep ( client , args , options ) ,
797- editFile : ( api ) => ( client , args = { } , options = { } ) => api . editFile ( client , args , options ) ,
798- applyPatch : ( api ) => ( client , args = { } , options = { } ) => api . applyPatch ( client , args , options ) ,
799- runRecipe : ( api ) => ( client , recipe , taskPayload , options = { } ) => api . runRecipe ( client , recipe , taskPayload , options ) ,
800- normalizeResult : ( ) => normalizeOperationResult ,
801- result : ( ) => browserSdkResult ,
802- executableBrowserSession : ( api ) => api . executableBrowserSession ,
803- bootExecutableBrowserSession : ( api ) => async ( client , session , options = { } ) => normalizeBrowserRunResult ( await api . bootExecutableBrowserSession ( client , session , options ) , 'browser-executable-session' ) ,
804- parentToolBridge : ( api ) => api . parentToolBridge ,
805- createBrowserConnectorRequest : ( api ) => api . createBrowserConnectorRequest ,
806- executeBrowserConnectorRequest : ( api ) => api . executeBrowserConnectorRequest ,
807- createParentToolRequest : ( api ) => api . createParentToolRequest ,
808- dispatchParentTool : ( api ) => api . dispatchParentTool ,
809- runBrowserSessionRecipe : ( api ) => async ( client , session , taskPayload , options = { } ) => normalizeBrowserRunResult ( await api . runBrowserSessionRecipe ( client , session , taskPayload , options ) , 'browser-session-recipe' ) ,
810- setFrontendAdminBarVisible : ( api ) => ( client , args = { } , options = { } ) => api . setFrontendAdminBarVisible ( client , args , options ) ,
811- } ) ;
779+ const browserSdkUtilityContract = Object . freeze ( [
780+ { name : 'capabilities' , topLevelOrder : - 8 , topLevel : ( ) => ( ) => browserSdkInfo ( ) . capabilities } ,
781+ { name : 'getCapabilities' , topLevelOrder : - 7 , topLevel : ( ) => ( ) => browserSdkInfo ( ) } ,
782+ { name : 'info' , topLevelOrder : - 6 , topLevel : ( ) => browserSdkInfo } ,
783+ { name : 'normalizeError' , topLevelOrder : - 5 , topLevel : ( ) => normalizeBrowserSdkError } ,
784+ { name : 'normalizeBrowserRunResult' , topLevelOrder : - 4 , topLevel : ( ) => normalizeBrowserRunResult } ,
785+ { name : 'browserArtifactPersistenceRef' , topLevelOrder : - 3 , topLevel : ( ) => browserArtifactPersistenceRef } ,
786+ { name : 'createRuntimeTaskRequest' , topLevelOrder : - 2 , topLevel : ( ) => createRuntimeTaskRequest } ,
787+ { name : 'runRuntimeTask' , topLevelOrder : - 1 , topLevel : ( ) => runRuntimeTask } ,
788+ { name : 'normalizeResult' , topLevelOrder : 12.6 , topLevel : ( ) => normalizeOperationResult } ,
789+ { name : 'result' , topLevelOrder : 12.7 , topLevel : ( ) => browserSdkResult } ,
790+ ] ) ;
791+
792+ const browserRuntimeInternalContract = Object . freeze ( [
793+ 'normalizeOperationResult' ,
794+ 'parseJsonResponse' ,
795+ ] ) ;
796+
797+ const browserSdkMethodContract = Object . freeze ( browserSdkContract . map ( ( entry ) => entry . name ) ) ;
798+ const browserSdkTopLevelContract = Object . freeze ( [ ...browserSdkUtilityContract , ...browserSdkContract . filter ( ( entry ) => typeof entry . topLevel === 'function' ) ]
799+ . sort ( ( a , b ) => a . topLevelOrder - b . topLevelOrder ) ) ;
800+
801+ const assertBrowserSdkContractComplete = ( api ) => {
802+ const sdkMethods = new Set ( browserSdkMethodContract ) ;
803+ const internalMethods = new Set ( browserRuntimeInternalContract ) ;
804+ const missing = browserSdkMethodContract . filter ( ( name ) => typeof api [ name ] !== 'function' ) ;
805+ const uncovered = Object . keys ( api ) . filter ( ( name ) => ! sdkMethods . has ( name ) && ! internalMethods . has ( name ) ) ;
806+ if ( missing . length || uncovered . length ) {
807+ throw runtimeError ( 'contract' , 'browser_sdk_contract_incomplete' , 'Browser SDK contract must cover every public runtime API method.' , { missing, uncovered } ) ;
808+ }
809+ } ;
812810
813811 const materializeBrowserSdkMethods = ( api ) => Object . freeze ( Object . fromEntries (
814812 browserSdkMethodContract . map ( ( name ) => [ name , api [ name ] ] )
818816 schema : browserSdkSchema ,
819817 apiVersion : 'v1' ,
820818 version : browserSdkVersion ,
821- ...Object . fromEntries ( Object . entries ( browserSdkTopLevelContract ) . map ( ( [ name , materialize ] ) => [ name , materialize ( api ) ] ) ) ,
819+ ...Object . fromEntries ( browserSdkTopLevelContract . map ( ( { name, topLevel } ) => [ name , topLevel ( api ) ] ) ) ,
822820 methods : materializeBrowserSdkMethods ( api ) ,
823821 } ) ;
824822
868866 ) ;
869867 } ;
870868
871- const isPlaygroundStructuredCloneError = ( error ) => {
872- const message = String ( error ?. message || '' ) ;
873- if ( error ?. code === 25 || message . includes ( 'could not be cloned' ) || message . includes ( 'DataCloneError' ) ) {
874- return true ;
875- }
876-
877- const details = error ?. data ;
878- if ( details ?. last_error && isPlaygroundStructuredCloneError ( details . last_error ) ) {
879- return true ;
880- }
881-
882- return Array . isArray ( details ?. attempts ) && details . attempts . some ( ( attempt ) => isPlaygroundStructuredCloneError ( attempt ?. error ) ) ;
883- } ;
884-
885869 const runPhpDirect = async ( client , code , options = { } ) => {
886870 if ( typeof client ?. run !== 'function' ) {
887871 throw runtimeError ( 'run_php' , 'playground_run_unavailable' , 'Playground run is unavailable.' ) ;
@@ -1924,9 +1908,6 @@ try {
19241908 if ( ! options . forceRequest && typeof client ?. run === 'function' ) {
19251909 return await runPhpDirect ( client , code , options ) ;
19261910 }
1927- if ( options . forceRequest && typeof client ?. writeFile !== 'function' && typeof client ?. run === 'function' ) {
1928- return await runPhpDirect ( client , code , options ) ;
1929- }
19301911
19311912 const runnerDir = String ( options . runnerDir || defaultRunnerDir ) ;
19321913 const runnerUrlBase = String ( options . runnerUrlBase || defaultRunnerUrlBase ) ;
@@ -1940,16 +1921,7 @@ try {
19401921 method : 'GET' ,
19411922 url : requestUrl ,
19421923 } ;
1943- let response ;
1944- try {
1945- response = await playgroundRequest ( client , request ) ;
1946- } catch ( error ) {
1947- if ( options . forceRequest && typeof client ?. run === 'function' && isPlaygroundStructuredCloneError ( error ) ) {
1948- return await runPhpDirect ( client , code , options ) ;
1949- }
1950-
1951- throw error ;
1952- }
1924+ const response = await playgroundRequest ( client , request ) ;
19531925
19541926 return options . expectJson ? await parseJsonResponse ( response ) : response ;
19551927 } ;
@@ -3171,6 +3143,7 @@ echo wp_json_encode( array(
31713143 writeReviewFile,
31723144 } ) ;
31733145 const wpCodeboxBrowserApi = { ...browserRuntimeApiContract } ;
3146+ assertBrowserSdkContractComplete ( wpCodeboxBrowserApi ) ;
31743147 wpCodeboxBrowserApi . v1 = browserSdkFacade ( wpCodeboxBrowserApi ) ;
31753148 window . wpCodeboxBrowser = wpCodeboxBrowserApi ;
31763149 window . wpCodebox = Object . freeze ( {
0 commit comments