@@ -188,15 +188,22 @@ public static function open_browser_contained_site( array $input ): array|WP_Err
188188 $ resolution = is_array ( $ status ['resolution ' ] ?? null ) ? $ status ['resolution ' ] : array ();
189189 $ open_status = (string ) ( $ status ['status ' ] ?? 'miss ' );
190190 $ open_success = true === ( $ status ['success ' ] ?? false );
191+ $ lifecycle = self ::browser_contained_site_lifecycle ( $ open_status , $ resolution );
191192 if ( $ open_success && true !== ( $ boot_contract ['valid ' ] ?? false ) ) {
192193 $ open_success = false ;
193194 $ open_status = 'unusable ' ;
194195 $ resolution = self ::browser_contained_site_resolution ( $ open_status , array ( 'invalidation ' => array ( 'reason ' => (string ) ( $ boot_contract ['reason ' ] ?? 'preview-boot-contract-unusable ' ) ) ) );
196+ $ lifecycle = self ::browser_contained_site_lifecycle ( $ open_status , $ resolution );
195197 }
198+ $ recovery = self ::browser_contained_site_open_recovery ( $ site_id , (string ) ( $ status ['source_digest ' ]['value ' ] ?? '' ) );
199+ $ recovery_handle = self ::browser_contained_site_recovery_handle ( $ site_id , (string ) ( $ status ['source_digest ' ]['value ' ] ?? '' ) );
200+ $ digest_refs = self ::browser_contained_site_digest_refs ( $ status );
196201
197202 $ opened_site = array_filter (
198203 array_merge (
199204 $ contained_site ,
205+ $ lifecycle ,
206+ $ digest_refs ,
200207 array (
201208 'schema ' => 'wp-codebox/browser-contained-site/v1 ' ,
202209 'site_id ' => $ site_id ,
@@ -211,7 +218,8 @@ public static function open_browser_contained_site( array $input ): array|WP_Err
211218 'preview_boot ' => $ preview_boot ,
212219 'preview_lease ' => $ preview_lease ,
213220 'session ' => self ::browser_contained_site_session_identity ( $ session_id , $ preview_id , $ scope ),
214- 'recovery ' => self ::browser_contained_site_open_recovery ( $ site_id , (string ) ( $ status ['source_digest ' ]['value ' ] ?? '' ) ),
221+ 'recovery ' => $ recovery ,
222+ 'recovery_handle ' => $ recovery_handle ,
215223 )
216224 ),
217225 static fn ( mixed $ value ): bool => array () !== $ value && '' !== $ value
@@ -220,21 +228,26 @@ public static function open_browser_contained_site( array $input ): array|WP_Err
220228 $ preview_session = WP_Codebox_Browser_Task_Builder::product_browser_session_dto ( $ session );
221229
222230 return array_filter (
223- array (
224- 'success ' => $ open_success ,
225- 'schema ' => 'wp-codebox/browser-contained-site-open/v1 ' ,
226- 'site_id ' => $ site_id ,
227- 'status ' => $ open_status ,
228- 'resolution ' => $ resolution ,
229- 'contained_site ' => $ opened_site ,
230- 'source_digest ' => is_array ( $ status ['source_digest ' ] ?? null ) ? $ status ['source_digest ' ] : array (),
231- 'prepared_runtime ' => is_array ( $ status ['prepared_runtime ' ] ?? null ) ? $ status ['prepared_runtime ' ] : array (),
232- 'blueprint_ref ' => $ blueprint_ref ,
233- 'preview_boot ' => $ preview_boot ,
234- 'preview_lease ' => $ preview_lease ,
235- 'preview_session ' => $ preview_session ,
236- 'session ' => self ::browser_contained_site_session_identity ( $ session_id , $ preview_id , $ scope ),
237- 'recovery ' => self ::browser_contained_site_open_recovery ( $ site_id , (string ) ( $ status ['source_digest ' ]['value ' ] ?? '' ) ),
231+ array_merge (
232+ $ lifecycle ,
233+ $ digest_refs ,
234+ array (
235+ 'success ' => $ open_success ,
236+ 'schema ' => 'wp-codebox/browser-contained-site-open/v1 ' ,
237+ 'site_id ' => $ site_id ,
238+ 'status ' => $ open_status ,
239+ 'resolution ' => $ resolution ,
240+ 'contained_site ' => $ opened_site ,
241+ 'source_digest ' => is_array ( $ status ['source_digest ' ] ?? null ) ? $ status ['source_digest ' ] : array (),
242+ 'prepared_runtime ' => is_array ( $ status ['prepared_runtime ' ] ?? null ) ? $ status ['prepared_runtime ' ] : array (),
243+ 'blueprint_ref ' => $ blueprint_ref ,
244+ 'preview_boot ' => $ preview_boot ,
245+ 'preview_lease ' => $ preview_lease ,
246+ 'preview_session ' => $ preview_session ,
247+ 'session ' => self ::browser_contained_site_session_identity ( $ session_id , $ preview_id , $ scope ),
248+ 'recovery ' => $ recovery ,
249+ 'recovery_handle ' => $ recovery_handle ,
250+ )
238251 ),
239252 static fn ( mixed $ value ): bool => array () !== $ value && '' !== $ value
240253 );
@@ -1065,36 +1078,114 @@ private static function browser_contained_site_artifact_meta( array $input ): ar
10651078/** @return array<string,mixed> */
10661079private static function browser_contained_site_status_envelope ( string $ cache_key , string $ input_hash , array $ lookup ): array {
10671080 $ artifact = is_array ( $ lookup ['artifact ' ] ?? null ) ? $ lookup ['artifact ' ] : array ();
1068- $ status = self ::browser_contained_site_status_from_lookup ( $ lookup );
1081+ $ status = self ::browser_contained_site_status_from_lookup ( $ lookup );
10691082 $ resolution = self ::browser_contained_site_resolution ( $ status , $ lookup );
1083+ $ lifecycle = self ::browser_contained_site_lifecycle ( $ status , $ resolution );
1084+ $ digest_refs = self ::browser_contained_site_digest_refs ( array_merge ( $ lookup , array ( 'source_digest ' => array ( 'algorithm ' => 'sha256 ' , 'value ' => $ input_hash ) ) ) );
10701085
10711086 return array_filter (
1072- array (
1073- 'success ' => 'recoverable_prepared_runtime ' === $ status ,
1074- 'schema ' => 'wp-codebox/browser-contained-site-status/v1 ' ,
1075- 'site_id ' => $ cache_key ,
1076- 'status ' => $ status ,
1077- 'resolution ' => $ resolution ,
1078- 'source_digest ' => array (
1079- 'algorithm ' => 'sha256 ' ,
1080- 'value ' => $ input_hash ,
1081- ),
1082- 'prepared_runtime ' => array_filter (
1083- array (
1084- 'cache_key ' => $ cache_key ,
1085- 'input_hash ' => $ input_hash ,
1086- 'status ' => (string ) ( $ lookup ['status ' ] ?? '' ),
1087- 'reason ' => (string ) ( $ resolution ['reason ' ] ?? '' ),
1088- 'created_at ' => (string ) ( $ artifact ['created_at ' ] ?? '' ),
1087+ array_merge (
1088+ $ lifecycle ,
1089+ $ digest_refs ,
1090+ array (
1091+ 'success ' => 'recoverable_prepared_runtime ' === $ status ,
1092+ 'schema ' => 'wp-codebox/browser-contained-site-status/v1 ' ,
1093+ 'site_id ' => $ cache_key ,
1094+ 'status ' => $ status ,
1095+ 'resolution ' => $ resolution ,
1096+ 'source_digest ' => array (
1097+ 'algorithm ' => 'sha256 ' ,
1098+ 'value ' => $ input_hash ,
10891099 ),
1090- static fn ( string $ value ): bool => '' !== $ value
1091- ),
1092- 'blueprint_ref ' => 'recoverable_prepared_runtime ' === $ status ? WP_Codebox_Browser_Task_Builder::browser_blueprint_ref ( array ( 'cache_key ' => $ cache_key , 'input_hash ' => $ input_hash , 'status ' => 'recoverable_prepared_runtime ' ) ) : array (),
1100+ 'prepared_runtime ' => array_filter (
1101+ array (
1102+ 'cache_key ' => $ cache_key ,
1103+ 'input_hash ' => $ input_hash ,
1104+ 'status ' => (string ) ( $ lookup ['status ' ] ?? '' ),
1105+ 'reason ' => (string ) ( $ resolution ['reason ' ] ?? '' ),
1106+ 'created_at ' => (string ) ( $ artifact ['created_at ' ] ?? '' ),
1107+ ),
1108+ static fn ( string $ value ): bool => '' !== $ value
1109+ ),
1110+ 'blueprint_ref ' => 'recoverable_prepared_runtime ' === $ status ? WP_Codebox_Browser_Task_Builder::browser_blueprint_ref ( array ( 'cache_key ' => $ cache_key , 'input_hash ' => $ input_hash , 'status ' => 'recoverable_prepared_runtime ' ) ) : array (),
1111+ 'recovery ' => self ::browser_contained_site_open_recovery ( $ cache_key , $ input_hash ),
1112+ 'recovery_handle ' => self ::browser_contained_site_recovery_handle ( $ cache_key , $ input_hash ),
1113+ )
10931114 ),
10941115 static fn ( mixed $ value ): bool => array () !== $ value && '' !== $ value
10951116 );
10961117}
10971118
1119+ /** @return array<string,mixed> */
1120+ private static function browser_contained_site_lifecycle ( string $ status , array $ resolution ): array {
1121+ $ prepared_runtime_recoverable = true === ( $ resolution ['prepared_runtime_recoverable ' ] ?? false );
1122+ $ live = true === ( $ resolution ['live ' ] ?? false );
1123+ $ current = true === ( $ resolution ['current ' ] ?? false );
1124+ $ materialized = true === ( $ resolution ['materialized ' ] ?? false );
1125+ $ open_mode = 'materialize ' ;
1126+ $ reuse_level = 'none ' ;
1127+
1128+ if ( $ current ) {
1129+ $ open_mode = 'reuse_current ' ;
1130+ $ reuse_level = 'current ' ;
1131+ } elseif ( $ live ) {
1132+ $ open_mode = 'reuse_live ' ;
1133+ $ reuse_level = 'live ' ;
1134+ } elseif ( $ prepared_runtime_recoverable ) {
1135+ $ open_mode = 'reuse_prepared_runtime ' ;
1136+ $ reuse_level = 'prepared_runtime ' ;
1137+ } elseif ( $ materialized ) {
1138+ $ open_mode = 'reuse_materialized ' ;
1139+ $ reuse_level = 'materialized ' ;
1140+ } elseif ( in_array ( $ status , array ( 'disabled ' , 'incompatible ' , 'unusable ' ), true ) ) {
1141+ $ open_mode = 'unavailable ' ;
1142+ }
1143+
1144+ return array (
1145+ 'open_mode ' => $ open_mode ,
1146+ 'reuse_level ' => $ reuse_level ,
1147+ 'requires_materialization ' => ! ( $ prepared_runtime_recoverable || $ live || $ current || $ materialized ),
1148+ 'prepared_runtime_recoverable ' => $ prepared_runtime_recoverable ,
1149+ 'live ' => $ live ,
1150+ 'current ' => $ current ,
1151+ 'materialized ' => $ materialized ,
1152+ );
1153+ }
1154+
1155+ /** @return array<string,mixed> */
1156+ private static function browser_contained_site_digest_refs ( array $ input ): array {
1157+ $ artifact = is_array ( $ input ['artifact ' ] ?? null ) ? $ input ['artifact ' ] : array ();
1158+
1159+ return array_filter (
1160+ array (
1161+ 'source_digest ' => self ::browser_contained_site_digest_ref ( $ input ['source_digest ' ] ?? $ artifact ['source_digest ' ] ?? $ input ['input_hash ' ] ?? '' ),
1162+ 'artifact_digest ' => self ::browser_contained_site_digest_ref ( $ artifact ['artifact_digest ' ] ?? $ input ['artifact_digest ' ] ?? $ artifact ['digest ' ] ?? $ artifact ['sha256 ' ] ?? '' ),
1163+ 'materialization_digest ' => self ::browser_contained_site_digest_ref ( $ artifact ['materialization_digest ' ] ?? $ input ['materialization_digest ' ] ?? '' ),
1164+ ),
1165+ static fn ( mixed $ value ): bool => array () !== $ value
1166+ );
1167+ }
1168+
1169+ /** @return array{algorithm:string,value:string}|array{} */
1170+ private static function browser_contained_site_digest_ref ( mixed $ value ): array {
1171+ if ( is_array ( $ value ) ) {
1172+ $ digest = strtolower ( trim ( (string ) ( $ value ['value ' ] ?? $ value ['sha256 ' ] ?? $ value ['hash ' ] ?? '' ) ) );
1173+ $ algorithm = (string ) ( $ value ['algorithm ' ] ?? 'sha256 ' );
1174+ } else {
1175+ $ digest = strtolower ( trim ( (string ) $ value ) );
1176+ $ algorithm = 'sha256 ' ;
1177+ }
1178+
1179+ if ( ! preg_match ( '/^[a-f0-9]{64}$/ ' , $ digest ) ) {
1180+ return array ();
1181+ }
1182+
1183+ return array (
1184+ 'algorithm ' => $ algorithm ,
1185+ 'value ' => $ digest ,
1186+ );
1187+ }
1188+
10981189/** @return string */
10991190private static function browser_contained_site_status_from_lookup ( array $ lookup ): string {
11001191 $ lookup_status = (string ) ( $ lookup ['status ' ] ?? 'miss ' );
@@ -1247,6 +1338,10 @@ private static function browser_contained_site_open_recovery( string $site_id, s
12471338 );
12481339}
12491340
1341+ private static function browser_contained_site_recovery_handle ( string $ site_id , string $ source_digest ): string {
1342+ return '' !== $ site_id && preg_match ( '/^[a-f0-9]{64}$/ ' , $ source_digest ) ? 'browser-contained-site: ' . $ site_id . ': ' . $ source_digest : '' ;
1343+ }
1344+
12501345private static function browser_contained_site_source_digest ( array $ input , array $ playground , array $ runtime , array $ prepared_runtime ): string {
12511346 $ input_hash = strtolower ( trim ( (string ) ( $ prepared_runtime ['input_hash ' ] ?? '' ) ) );
12521347 if ( preg_match ( '/^[a-f0-9]{64}$/ ' , $ input_hash ) ) {
0 commit comments