Skip to content

Commit 0386578

Browse files
committed
Clarify browser contained site lifecycle
1 parent 3e287c4 commit 0386578

6 files changed

Lines changed: 210 additions & 18 deletions

packages/runtime-core/src/runtime-boundary-contracts.ts

Lines changed: 141 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export const RUNTIME_PROFILE_SCHEMA = "wp-codebox/runtime-profile/v1" as const
22
export const PREVIEW_LEASE_SCHEMA = "wp-codebox/preview-lease/v1" as const
33
export const BROWSER_CONTAINED_SITE_STATUS_SCHEMA = "wp-codebox/browser-contained-site-status/v1" as const
4+
export const BROWSER_CONTAINED_SITE_OPEN_SCHEMA = "wp-codebox/browser-contained-site-open/v1" as const
45
export const BROWSER_SESSION_PRODUCT_DTO_SCHEMA = "wp-codebox/browser-session-product-dto/v1" as const
56
export const BROWSER_PREVIEW_BOOT_CONFIG_SCHEMA = "wp-codebox/browser-preview-boot-config/v1" as const
67

@@ -87,16 +88,38 @@ export interface PreviewLease {
8788
}
8889

8990
export type PreviewLeaseLifecycleStatus = "active" | "expired" | "released" | "unknown"
91+
export type BrowserContainedSiteLifecycleStatus = "recoverable_prepared_runtime" | "current" | "live" | "materialized" | "miss" | "expired" | "blocked" | "disabled" | "incompatible" | "unknown" | (string & {})
92+
93+
export interface BrowserContainedSiteIdentity {
94+
schema: "wp-codebox/browser-contained-site/v1"
95+
site_id: string
96+
preview_id?: string
97+
session_id?: string
98+
status?: BrowserContainedSiteLifecycleStatus
99+
source_digest?: {
100+
algorithm: "sha256" | (string & {})
101+
value: string
102+
}
103+
resolution?: Record<string, unknown>
104+
prepared_runtime?: Record<string, unknown>
105+
blueprint_ref?: Record<string, unknown>
106+
preview_boot?: BrowserPreviewBootConfig
107+
preview_lease?: PreviewLease
108+
session?: Record<string, unknown>
109+
recovery?: Record<string, unknown>
110+
metadata?: Record<string, unknown>
111+
}
90112

91113
export interface BrowserContainedSiteStatus {
92114
schema: typeof BROWSER_CONTAINED_SITE_STATUS_SCHEMA
93115
success: boolean
94116
site_id: string
95-
status: "recoverable" | "miss" | "expired" | "blocked" | "unknown" | (string & {})
117+
status: BrowserContainedSiteLifecycleStatus
96118
source_digest: {
97119
algorithm: "sha256" | (string & {})
98120
value: string
99121
}
122+
resolution?: Record<string, unknown>
100123
prepared_runtime?: Record<string, unknown>
101124
blueprint_ref?: Record<string, unknown>
102125
metadata?: Record<string, unknown>
@@ -138,6 +161,26 @@ export interface BrowserSessionProductDto {
138161
error?: Record<string, unknown>
139162
}
140163

164+
export interface BrowserContainedSiteOpenEnvelope {
165+
schema: typeof BROWSER_CONTAINED_SITE_OPEN_SCHEMA
166+
success: boolean
167+
site_id: string
168+
status: BrowserContainedSiteLifecycleStatus
169+
resolution?: Record<string, unknown>
170+
contained_site?: BrowserContainedSiteIdentity
171+
source_digest?: {
172+
algorithm: "sha256" | (string & {})
173+
value: string
174+
}
175+
prepared_runtime?: Record<string, unknown>
176+
blueprint_ref?: Record<string, unknown>
177+
preview_boot?: BrowserPreviewBootConfig
178+
preview_lease?: PreviewLease
179+
preview_session?: BrowserSessionProductDto
180+
session?: Record<string, unknown>
181+
recovery?: Record<string, unknown>
182+
}
183+
141184
export function runtimeProfile(input: unknown): RuntimeProfile {
142185
const value = requireObject(input, "Runtime profile") as Partial<RuntimeProfile>
143186
if (value.schema !== RUNTIME_PROFILE_SCHEMA) throw new Error(`Runtime profile schema must be ${RUNTIME_PROFILE_SCHEMA}.`)
@@ -218,12 +261,109 @@ export function browserContainedSiteStatus(input: unknown): BrowserContainedSite
218261
algorithm: optionalString(digest.algorithm, "source_digest.algorithm") ?? "sha256",
219262
value: digestValue,
220263
},
264+
resolution: normalizeOptionalObject(value.resolution, "resolution"),
221265
prepared_runtime: normalizeOptionalObject(value.prepared_runtime, "prepared_runtime"),
222266
blueprint_ref: normalizeOptionalObject(value.blueprint_ref, "blueprint_ref"),
223267
metadata: normalizeOptionalObject(value.metadata, "metadata"),
224268
}
225269
}
226270

271+
export function browserContainedSiteOpenEnvelope(input: unknown): BrowserContainedSiteOpenEnvelope {
272+
const value = requireObject(input, "Browser contained site open envelope") as Partial<BrowserContainedSiteOpenEnvelope>
273+
if (value.schema !== BROWSER_CONTAINED_SITE_OPEN_SCHEMA) throw new Error(`Browser contained site open schema must be ${BROWSER_CONTAINED_SITE_OPEN_SCHEMA}.`)
274+
return {
275+
schema: BROWSER_CONTAINED_SITE_OPEN_SCHEMA,
276+
success: value.success === true,
277+
site_id: requiredIdentifier(value.site_id, "site_id"),
278+
status: requiredIdentifier(value.status, "status") as BrowserContainedSiteLifecycleStatus,
279+
resolution: normalizeOptionalObject(value.resolution, "resolution"),
280+
contained_site: value.contained_site === undefined ? undefined : normalizeContainedSiteIdentity(value.contained_site),
281+
source_digest: value.source_digest === undefined ? undefined : normalizeSourceDigest(value.source_digest),
282+
prepared_runtime: normalizeOptionalObject(value.prepared_runtime, "prepared_runtime"),
283+
blueprint_ref: normalizeOptionalObject(value.blueprint_ref, "blueprint_ref"),
284+
preview_boot: value.preview_boot === undefined ? undefined : normalizePreviewBootConfig(value.preview_boot),
285+
preview_lease: value.preview_lease === undefined ? undefined : previewLease(value.preview_lease),
286+
preview_session: value.preview_session === undefined ? undefined : normalizeBrowserSessionProductDto(value.preview_session),
287+
session: normalizeOptionalObject(value.session, "session"),
288+
recovery: normalizeOptionalObject(value.recovery, "recovery"),
289+
}
290+
}
291+
292+
function normalizeContainedSiteIdentity(input: unknown): BrowserContainedSiteIdentity {
293+
const value = requireObject(input, "Browser contained site identity") as Partial<BrowserContainedSiteIdentity>
294+
if (value.schema !== "wp-codebox/browser-contained-site/v1") throw new Error("Browser contained site identity schema must be wp-codebox/browser-contained-site/v1.")
295+
return {
296+
schema: "wp-codebox/browser-contained-site/v1",
297+
site_id: requiredIdentifier(value.site_id, "contained_site.site_id"),
298+
preview_id: optionalString(value.preview_id, "contained_site.preview_id"),
299+
session_id: optionalString(value.session_id, "contained_site.session_id"),
300+
status: optionalString(value.status, "contained_site.status") as BrowserContainedSiteLifecycleStatus | undefined,
301+
source_digest: value.source_digest === undefined ? undefined : normalizeSourceDigest(value.source_digest),
302+
resolution: normalizeOptionalObject(value.resolution, "contained_site.resolution"),
303+
prepared_runtime: normalizeOptionalObject(value.prepared_runtime, "contained_site.prepared_runtime"),
304+
blueprint_ref: normalizeOptionalObject(value.blueprint_ref, "contained_site.blueprint_ref"),
305+
preview_boot: value.preview_boot === undefined ? undefined : normalizePreviewBootConfig(value.preview_boot),
306+
preview_lease: value.preview_lease === undefined ? undefined : previewLease(value.preview_lease),
307+
session: normalizeOptionalObject(value.session, "contained_site.session"),
308+
recovery: normalizeOptionalObject(value.recovery, "contained_site.recovery"),
309+
metadata: normalizeOptionalObject(value.metadata, "contained_site.metadata"),
310+
}
311+
}
312+
313+
function normalizeSourceDigest(input: unknown): { algorithm: "sha256" | (string & {}); value: string } {
314+
const digest = requireObject(input, "source_digest") as { algorithm?: unknown; value?: unknown }
315+
const digestValue = optionalString(digest.value, "source_digest.value")
316+
if (!digestValue || !/^[a-f0-9]{64}$/.test(digestValue)) throw new Error("source_digest.value must be a 64-character sha256 digest.")
317+
return {
318+
algorithm: optionalString(digest.algorithm, "source_digest.algorithm") ?? "sha256",
319+
value: digestValue,
320+
}
321+
}
322+
323+
function normalizePreviewBootConfig(input: unknown): BrowserPreviewBootConfig {
324+
const value = requireObject(input, "Browser preview boot config") as Partial<BrowserPreviewBootConfig>
325+
if (value.schema !== BROWSER_PREVIEW_BOOT_CONFIG_SCHEMA) throw new Error(`Browser preview boot config schema must be ${BROWSER_PREVIEW_BOOT_CONFIG_SCHEMA}.`)
326+
return {
327+
schema: BROWSER_PREVIEW_BOOT_CONFIG_SCHEMA,
328+
session_id: optionalString(value.session_id, "preview_boot.session_id"),
329+
scope: optionalString(value.scope, "preview_boot.scope"),
330+
client_module_url: optionalString(value.client_module_url, "preview_boot.client_module_url"),
331+
remote_url: optionalString(value.remote_url, "preview_boot.remote_url"),
332+
cors_proxy_url: optionalString(value.cors_proxy_url, "preview_boot.cors_proxy_url"),
333+
blueprint_ref: optionalString(value.blueprint_ref, "preview_boot.blueprint_ref"),
334+
blueprint_ref_dto: normalizeOptionalObject(value.blueprint_ref_dto, "preview_boot.blueprint_ref_dto"),
335+
preview: value.preview === undefined ? undefined : previewLease(value.preview),
336+
contained_site: normalizeOptionalObject(value.contained_site, "preview_boot.contained_site"),
337+
artifacts: normalizeOptionalObject(value.artifacts, "preview_boot.artifacts"),
338+
provenance: normalizeOptionalObject(value.provenance, "preview_boot.provenance"),
339+
}
340+
}
341+
342+
function normalizeBrowserSessionProductDto(input: unknown): BrowserSessionProductDto {
343+
const value = requireObject(input, "Browser session product DTO") as Partial<BrowserSessionProductDto>
344+
if (value.schema !== BROWSER_SESSION_PRODUCT_DTO_SCHEMA) throw new Error(`Browser session product DTO schema must be ${BROWSER_SESSION_PRODUCT_DTO_SCHEMA}.`)
345+
return {
346+
schema: BROWSER_SESSION_PRODUCT_DTO_SCHEMA,
347+
source_schema: optionalString(value.source_schema, "preview_session.source_schema"),
348+
success: value.success === true,
349+
status: optionalString(value.status, "preview_session.status"),
350+
execution: optionalString(value.execution, "preview_session.execution"),
351+
execution_scope: optionalString(value.execution_scope, "preview_session.execution_scope"),
352+
permission_model: optionalString(value.permission_model, "preview_session.permission_model"),
353+
session_id: optionalString(value.session_id, "preview_session.session_id"),
354+
contained_site: normalizeOptionalObject(value.contained_site, "preview_session.contained_site"),
355+
task: optionalString(value.task, "preview_session.task"),
356+
target: normalizeOptionalObject(value.target, "preview_session.target"),
357+
agent: optionalString(value.agent, "preview_session.agent"),
358+
provider: optionalString(value.provider, "preview_session.provider"),
359+
model: optionalString(value.model, "preview_session.model"),
360+
preview_boot: value.preview_boot === undefined ? undefined : normalizePreviewBootConfig(value.preview_boot),
361+
signals: normalizeOptionalObject(value.signals, "preview_session.signals"),
362+
artifacts: normalizeOptionalObject(value.artifacts, "preview_session.artifacts"),
363+
error: normalizeOptionalObject(value.error, "preview_session.error"),
364+
}
365+
}
366+
227367
function normalizeDependencies(value: unknown, label: string, optional = false): RuntimeProfileDependency[] | undefined {
228368
if (value === undefined && optional) return undefined
229369
if (!Array.isArray(value)) throw new Error(`Runtime profile ${label} must be an array.`)

packages/wordpress-plugin/src/class-wp-codebox-browser-ability-descriptors.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public static function descriptors( array $context ): array {
122122
),
123123
'wp-codebox/get-browser-contained-site-status' => array(
124124
'label' => 'Get Browser Contained Site Status',
125-
'description' => 'Resolve a durable browser-contained site handle into recoverable prepared-runtime status without creating a new Playground session.',
125+
'description' => 'Resolve a durable browser-contained site handle into explicit prepared-runtime recoverability or live/current/materialized availability without creating a new Playground session.',
126126
'category' => 'wp-codebox',
127127
'input_schema' => array(
128128
'type' => 'object',
@@ -184,6 +184,7 @@ public static function descriptors( array $context ): array {
184184
'blueprint_ref' => array( 'type' => 'object' ),
185185
'preview_boot' => array( 'type' => 'object' ),
186186
'preview_lease' => array( 'type' => 'object' ),
187+
'preview_session' => array( 'type' => 'object' ),
187188
'session' => array( 'type' => 'object' ),
188189
'recovery' => array( 'type' => 'object' ),
189190
),

packages/wordpress-plugin/src/trait-wp-codebox-abilities-execution.php

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ public static function open_browser_contained_site( array $input ): array|WP_Err
208208
),
209209
static fn( mixed $value ): bool => array() !== $value && '' !== $value
210210
);
211+
$session['contained_site'] = $opened_site;
212+
$preview_session = WP_Codebox_Browser_Task_Builder::product_browser_session_dto( $session );
211213

212214
return array_filter(
213215
array(
@@ -222,6 +224,7 @@ public static function open_browser_contained_site( array $input ): array|WP_Err
222224
'blueprint_ref' => $blueprint_ref,
223225
'preview_boot' => $preview_boot,
224226
'preview_lease' => $preview_lease,
227+
'preview_session' => $preview_session,
225228
'session' => self::browser_contained_site_session_identity( $session_id, $preview_id, $scope ),
226229
'recovery' => self::browser_contained_site_open_recovery( $site_id, (string) ( $status['source_digest']['value'] ?? '' ) ),
227230
),
@@ -1059,7 +1062,7 @@ private static function browser_contained_site_status_envelope( string $cache_ke
10591062

10601063
return array_filter(
10611064
array(
1062-
'success' => 'recoverable' === $status,
1065+
'success' => 'recoverable_prepared_runtime' === $status,
10631066
'schema' => 'wp-codebox/browser-contained-site-status/v1',
10641067
'site_id' => $cache_key,
10651068
'status' => $status,
@@ -1078,7 +1081,7 @@ private static function browser_contained_site_status_envelope( string $cache_ke
10781081
),
10791082
static fn( string $value ): bool => '' !== $value
10801083
),
1081-
'blueprint_ref' => 'recoverable' === $status ? WP_Codebox_Browser_Task_Builder::browser_blueprint_ref( array( 'cache_key' => $cache_key, 'input_hash' => $input_hash, 'status' => 'recoverable' ) ) : array(),
1084+
'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(),
10821085
),
10831086
static fn( mixed $value ): bool => array() !== $value && '' !== $value
10841087
);
@@ -1088,7 +1091,7 @@ private static function browser_contained_site_status_envelope( string $cache_ke
10881091
private static function browser_contained_site_status_from_lookup( array $lookup ): string {
10891092
$lookup_status = (string) ( $lookup['status'] ?? 'miss' );
10901093
if ( 'hit' === $lookup_status ) {
1091-
return 'recoverable';
1094+
return 'recoverable_prepared_runtime';
10921095
}
10931096

10941097
if ( ! empty( $lookup['invalidation'] ) ) {
@@ -1104,7 +1107,7 @@ private static function browser_contained_site_resolution( string $status, array
11041107
$reason = (string) ( $invalidation['reason'] ?? '' );
11051108
if ( '' === $reason ) {
11061109
$reason = match ( $status ) {
1107-
'recoverable' => 'prepared-runtime-cache-hit',
1110+
'recoverable_prepared_runtime' => 'prepared-runtime-cache-hit',
11081111
'incompatible' => 'prepared-runtime-incompatible',
11091112
'disabled' => 'prepared-runtime-cache-disabled',
11101113
default => 'prepared-runtime-not-found-or-expired',
@@ -1116,7 +1119,12 @@ private static function browser_contained_site_resolution( string $status, array
11161119
'schema' => 'wp-codebox/browser-contained-site-resolution/v1',
11171120
'outcome' => $status,
11181121
'reason' => $reason,
1119-
'reused' => 'recoverable' === $status,
1122+
'recoverable' => 'recoverable_prepared_runtime' === $status,
1123+
'prepared_runtime_recoverable' => 'recoverable_prepared_runtime' === $status,
1124+
'live' => in_array( $status, array( 'current', 'live' ), true ),
1125+
'current' => 'current' === $status,
1126+
'materialized' => in_array( $status, array( 'current', 'live', 'materialized' ), true ),
1127+
'reused' => false,
11201128
'created' => false,
11211129
'expired' => 'miss' === $status ? null : false,
11221130
'miss' => 'miss' === $status,
@@ -1167,6 +1175,9 @@ private static function browser_contained_site_open_session( array $input, array
11671175
return array(
11681176
'success' => true === ( $status['success'] ?? false ),
11691177
'schema' => 'wp-codebox/browser-contained-site-open-session/v1',
1178+
'status' => (string) ( $status['status'] ?? '' ),
1179+
'execution' => 'browser-contained-site-open',
1180+
'execution_scope' => 'browser-contained-site',
11701181
'session' => array_filter( array( 'id' => $session_id ), static fn( string $value ): bool => '' !== $value ),
11711182
'session_id' => $session_id,
11721183
'playground' => $playground,

packages/wordpress-plugin/src/trait-wp-codebox-abilities-schemas.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ private static function browser_contained_site_schema(): array {
446446
'preview_id' => array( 'type' => 'string' ),
447447
'session_id' => array( 'type' => 'string' ),
448448
'caller_id' => array( 'type' => 'string' ),
449-
'status' => array( 'type' => 'string', 'enum' => array( 'ready', 'blocked', 'recoverable', 'miss', 'disabled', 'incompatible' ) ),
449+
'status' => array( 'type' => 'string', 'enum' => array( 'ready', 'blocked', 'recoverable_prepared_runtime', 'current', 'live', 'materialized', 'miss', 'disabled', 'incompatible' ) ),
450450
'resolution' => array( 'type' => 'object' ),
451451
'persistence' => array( 'type' => 'string', 'enum' => array( 'browser-contained' ) ),
452452
'artifact_seed' => array( 'type' => 'string' ),

0 commit comments

Comments
 (0)