@@ -100,6 +100,24 @@ describe('resolveRegistryMetadataBatch', () => {
100100 expect ( calledUrls [ 0 ] ) . toBe ( 'https://api.test/v0/facets/acme%2Fcowsay/latest' )
101101 } )
102102
103+ test ( 'scoped names use the two-segment scoped route with an unencoded slash' , async ( ) => {
104+ const calledUrls : string [ ] = [ ]
105+ globalThis . fetch = ( async ( input : string | URL | Request ) => {
106+ calledUrls . push ( captureUrl ( input ) )
107+ return new Response ( JSON . stringify ( fixtures . versionMetadata ( { name : '@julian/cowsay' , version : '1.2.3' } ) ) , {
108+ status : 200 ,
109+ } )
110+ } ) as unknown as typeof fetch
111+ await resolveRegistryMetadataBatch ( [
112+ { name : '@julian/cowsay' , version : { kind : 'exact' , major : 1 , minor : 2 , patch : 3 } } ,
113+ ] )
114+ // Scope and name are independent path segments: the scope marker is a
115+ // single segment (`%40julian`) and the slash between scope and name is a
116+ // real path separator — never collapsed into `%2F`.
117+ expect ( calledUrls [ 0 ] ) . toBe ( 'https://api.test/v0/facets/%40julian/cowsay/1.2.3' )
118+ expect ( calledUrls [ 0 ] ) . not . toContain ( '%2F' )
119+ } )
120+
103121 test ( '404 maps to NOT_FOUND with the spec verbatim' , async ( ) => {
104122 globalThis . fetch = ( async ( ) =>
105123 new Response ( JSON . stringify ( fixtures . apiError ( { error : 'gone' , docs_url : 'x' } ) ) , {
@@ -251,6 +269,24 @@ describe('downloadAndExtractFacet', () => {
251269 expect ( readFileSync ( join ( dest , 'commands/cowsay.md' ) , 'utf8' ) ) . toContain ( '# cowsay' )
252270 } )
253271
272+ test ( 'scoped name uses the two-segment scoped archive route (no %2F)' , async ( ) => {
273+ const facetJson = JSON . stringify (
274+ { name : '@julian/cowsay' , version : '0.1.0' , commands : { cowsay : { description : 'Say moo' } } } ,
275+ null ,
276+ 2 ,
277+ )
278+ const { bytes, integrity } = buildArchive ( [
279+ { path : 'facet.json' , content : facetJson } ,
280+ { path : 'commands/cowsay.md' , content : '# cowsay\n' } ,
281+ ] )
282+ const { urls } = stubArchiveDownload ( new Response ( bytes , { status : 200 } ) )
283+ const result = await downloadAndExtractFacet ( { ...META , name : '@julian/cowsay' , transportHash : integrity } , dest )
284+ expect ( result . ok ) . toBe ( true )
285+ expect ( urls [ 0 ] ) . toBe ( 'https://api.test/v0/facets/%40julian/cowsay/0.1.0/archive' )
286+ expect ( urls [ 0 ] ) . not . toContain ( '%2F' )
287+ expect ( urls [ 1 ] ) . toBe ( S3_URL )
288+ } )
289+
254290 test ( 'sha256 mismatch: returns NETWORK_ERROR with hash detail and writes nothing' , async ( ) => {
255291 const { bytes } = buildArchive ( [ { path : 'facet.json' , content : JSON . stringify ( { name : 'x' , version : '0.1.0' } ) } ] )
256292 stubArchiveDownload ( new Response ( bytes , { status : 200 } ) )
0 commit comments