11import assert from "node:assert/strict"
22import { execFile } from "node:child_process"
3- import { cp , lstat , mkdir , mkdtemp , readFile , rm } from "node:fs/promises"
3+ import { cp , lstat , mkdir , mkdtemp , readFile , rm , writeFile } from "node:fs/promises"
44import { tmpdir } from "node:os"
55import { join , resolve } from "node:path"
6+ import { pathToFileURL } from "node:url"
67import { promisify } from "node:util"
78
89const execFileAsync = promisify ( execFile )
@@ -18,15 +19,25 @@ assert.deepEqual(homeboy.release?.package_coverage, [{
1819 archive_root : "wp-codebox" ,
1920} ] )
2021
21- const { stdout } = await execFileAsync ( "npm" , [ "run" , "release:package" ] , {
22- cwd : repositoryRoot ,
23- env : {
24- ...process . env ,
25- WP_CODEBOX_RELEASE_PLATFORM : "linux" ,
26- WP_CODEBOX_RELEASE_ARCH : "x64" ,
27- } ,
28- maxBuffer : 1024 * 1024 * 20 ,
29- } )
22+ await execFileAsync ( "npm" , [ "run" , "build" ] , { cwd : repositoryRoot , maxBuffer : 1024 * 1024 * 10 } )
23+ const staleDistPath = resolve ( repositoryRoot , "packages/runtime-playground/dist/mount-materialization.js" )
24+ const currentDist = await readFile ( staleDistPath )
25+ await writeFile ( staleDistPath , "export const staleBuild = true\n" )
26+
27+ let stdout = ""
28+ try {
29+ ( { stdout } = await execFileAsync ( "npm" , [ "run" , "release:package" ] , {
30+ cwd : repositoryRoot ,
31+ env : {
32+ ...process . env ,
33+ WP_CODEBOX_RELEASE_PLATFORM : "linux" ,
34+ WP_CODEBOX_RELEASE_ARCH : "x64" ,
35+ } ,
36+ maxBuffer : 1024 * 1024 * 20 ,
37+ } ) )
38+ } finally {
39+ await writeFile ( staleDistPath , currentDist )
40+ }
3041const artifacts = JSON . parse ( stdout . trim ( ) . split ( "\n" ) . at ( - 1 ) ?? "[]" )
3142assert . equal ( artifacts . length , 2 , "release package emitted unexpected artifacts" )
3243assert . deepEqual ( artifacts . filter ( ( artifact : { type : string } ) => artifact . type === "wordpress-plugin-zip" ) , [
95106 env : { ...process . env , WP_CODEBOX_NODE_BIN : "" } ,
96107 } )
97108 assert . equal ( wrapperVersion , version , "wrapper must fall back to host Node when bundled Node is incompatible" )
109+
110+ await assertPackagedReadonlyMaterialization ( root )
98111 }
99112} finally {
100113 await rm ( extractionRoot , { recursive : true , force : true } )
@@ -139,3 +152,64 @@ try {
139152}
140153
141154console . log ( "release package coverage passed" )
155+
156+ async function assertPackagedReadonlyMaterialization ( root : string ) : Promise < void > {
157+ const modulePath = join ( root , "packages" , "runtime-playground" , "dist" , "mount-materialization.js" )
158+ const runtime = await import ( pathToFileURL ( modulePath ) . href ) as {
159+ stageReadonlyPlaygroundMounts ( mounts : Array < Record < string , unknown > > ) : Promise < {
160+ mounts : Array < { source : string } >
161+ [ Symbol . asyncDispose ] ( ) : Promise < void >
162+ } >
163+ materializePlaygroundStagedInputs ( server : unknown , mounts : Array < Record < string , unknown > > ) : Promise < { materialized : number } >
164+ }
165+ assert . equal ( typeof runtime . stageReadonlyPlaygroundMounts , "function" , "packaged dist must contain readonly mount isolation" )
166+ assert . equal ( typeof runtime . materializePlaygroundStagedInputs , "function" , "packaged dist must contain staged input materialization" )
167+
168+ const fixtureRoot = await mkdtemp ( join ( tmpdir ( ) , "wp-codebox-packaged-readonly-" ) )
169+ const readonlySource = join ( fixtureRoot , "font.woff2" )
170+ const binarySource = join ( fixtureRoot , "translation.mo" )
171+ const readonlyBytes = Buffer . from ( "774f4632000100000000108000120000" , "hex" )
172+ const binaryBytes = Buffer . from ( "de12049500000000c50000001c000000" , "hex" )
173+ try {
174+ await writeFile ( readonlySource , readonlyBytes )
175+ await writeFile ( binarySource , binaryBytes )
176+
177+ const staging = await runtime . stageReadonlyPlaygroundMounts ( [ {
178+ type : "file" ,
179+ source : readonlySource ,
180+ target : "/wordpress/wp-content/themes/example/font.woff2" ,
181+ mode : "readonly" ,
182+ } ] )
183+ try {
184+ await writeFile ( staging . mounts [ 0 ] . source , Buffer . from ( "sandbox overwrite" ) )
185+ assert . deepEqual ( await readFile ( readonlySource ) , readonlyBytes , "packaged readonly staging must isolate host bytes" )
186+ } finally {
187+ await staging [ Symbol . asyncDispose ] ( )
188+ }
189+
190+ let directWrites = 0
191+ let materializedBase64 = ""
192+ const result = await runtime . materializePlaygroundStagedInputs ( {
193+ playground : {
194+ async writeFile ( ) {
195+ directWrites ++
196+ } ,
197+ async run ( { code } : { code : string } ) {
198+ const match = code . match ( / \$ p a y l o a d = j s o n _ d e c o d e \( ( .* ) , t r u e \) ; / )
199+ assert . ok ( match , "packaged binary fallback includes its materialization payload" )
200+ const payload = JSON . parse ( JSON . parse ( match [ 1 ] ) ) as { directories ?: string [ ] ; files ?: Array < { contentsBase64 ?: string } > }
201+ if ( code . includes ( "wp-codebox/host-mount-directory-materialization/v1" ) ) {
202+ return { text : JSON . stringify ( { schema : "wp-codebox/host-mount-directory-materialization/v1" , created : payload . directories ?. length ?? 0 , skipped : 0 } ) }
203+ }
204+ materializedBase64 = payload . files ?. [ 0 ] ?. contentsBase64 ?? ""
205+ return { text : JSON . stringify ( { schema : "wp-codebox/host-mount-materialization/v1" , materialized : 1 , created : 0 , skipped : 0 } ) }
206+ } ,
207+ } ,
208+ } , [ { type : "file" , source : binarySource , target : "/wordpress/wp-content/languages/example.mo" , mode : "readonly" } ] )
209+ assert . equal ( result . materialized , 1 )
210+ assert . equal ( directWrites , 0 , "invalid UTF-8 bytes must bypass Playground's text writer" )
211+ assert . equal ( materializedBase64 , binaryBytes . toString ( "base64" ) , "packaged binary fallback must preserve exact bytes" )
212+ } finally {
213+ await rm ( fixtureRoot , { recursive : true , force : true } )
214+ }
215+ }
0 commit comments