In my perspective, for truly "self-contained", "portable" and "self-updating" and even "un-hosted" web apps, the only option nowadays are prehistoric data-URIs, that are slowly losing abilities anyway (basically can live only as bookmarks or direct URL pastes, and their only persistence option is location #hash that needs re-bookmarking):

    data:text/html;charset=utf-8,<body id=b onload=b.innerHTML=decodeURIComponent((l=location).hash.slice(1)) onkeyup=document.title=b.innerText.split('\n')[0]||'.' onblur=try{history.pushState({},document.title,'\u0023'+b.innerHTML)}catch(e){l.hash=b.innerHTML} contenteditable bgcolor=darkslategray text=snow link=aqua vlink=lime style=text-align:center>#Hello, HN!<br><br>Do you like this %E2%9D%9Dun-hosted%E2%9D%9E&nbsp;app?<br>With persistence<a href="https://news.ycombinator.com/reply?id=44944112">%E2%80%A6?</a>