amoosedweb-cli 0.21.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/amw.mjs +2 -0
- package/package.json +8 -5
- package/reader.mjs +1 -2
- package/amw.cjs +0 -424
- package/runtime/amoosed.com/boot.css +0 -1
- package/runtime/amoosed.com/boot.html +0 -1
- package/runtime/amoosed.com/boot.js +0 -59
- package/runtime/amoosed.com/download.js +0 -1
- package/runtime/amoosed.com/sw.js +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
:root{color-scheme:dark;--screen-background:#0a0908;--screen-foreground:#f6eddf;--screen-accent:#ff9a3d}*{box-sizing:border-box}body{position:relative;min-height:100dvh;margin:0;overflow-x:hidden;background:var(--screen-background);color:var(--screen-foreground);font:17px/1.55 system-ui,sans-serif}body.has-boot-scene{background:#050403}#boot-scene{position:fixed;inset:0;width:100%;height:100%;pointer-events:none;opacity:0;transition:opacity 1.5s ease}body.has-boot-scene #boot-scene{opacity:1}body.has-boot-scene:before,body.has-boot-scene:after{opacity:0}body.has-boot-scene main{max-width:100%;padding-left:clamp(28px,9vw,150px);padding-right:clamp(28px,9vw,150px);background:linear-gradient(90deg,#050403e8 0%,#050403a8 34%,transparent 62%)}body.has-boot-scene #intro{max-width:610px}body:before,body:after{content:"";position:fixed;z-index:0;pointer-events:none;border-radius:50%;filter:blur(70px);opacity:.28}body:before{width:52vw;height:52vw;min-width:350px;min-height:350px;top:-24vw;right:-12vw;background:var(--screen-accent);animation:drift-one 13s ease-in-out infinite alternate}body:after{width:38vw;height:38vw;min-width:280px;min-height:280px;bottom:-20vw;left:-10vw;background:var(--screen-accent);opacity:.12;animation:drift-two 16s ease-in-out infinite alternate}main{position:relative;z-index:1;display:flex;min-height:100dvh;max-width:820px;flex-direction:column;justify-content:center;margin:auto;padding:clamp(28px,7vw,72px)}.departing main{animation:boot-depart 2.4s ease-in both}.shutting-down main{animation:shutdown-depart 2.4s ease-in-out both}.shutting-down .orbit{animation:shutdown-orbit 2.4s ease-in both}.shutting-down .core{animation:shutdown-core 2.4s ease-in both}.shutting-down #progress-fill{transform-origin:left;animation:shutdown-drain 2.4s linear both}.loading-visual{position:relative;width:88px;height:88px;margin-bottom:32px}.loading-visual[hidden]{display:none}.orbit{position:absolute;inset:0;border:1px solid color-mix(in srgb,var(--screen-accent) 38%,transparent);border-radius:50%;animation:orbit 8s linear infinite}.orbit:before{content:"";position:absolute;top:-3px;left:50%;width:6px;height:6px;border-radius:50%;background:var(--screen-accent);box-shadow:0 0 14px var(--screen-accent)}.orbit-two{inset:12px;animation-duration:5s;animation-direction:reverse;opacity:.8}.orbit-three{inset:24px;animation-duration:3s;opacity:.65}.core{position:absolute;inset:37px;border-radius:50%;background:var(--screen-accent);box-shadow:0 0 18px var(--screen-accent);animation:pulse 2.4s ease-in-out infinite}.mode-label{margin:0 0 13px;color:var(--screen-accent);font-size:10px;font-weight:750;letter-spacing:.26em}#intro[hidden],#unlock[hidden],#index-import[hidden]{display:none}#intro{max-width:690px;background:none!important;white-space:pre-wrap}#intro:not([hidden]){animation:appear .9s ease both}#intro h2{margin:0 0 24px;font:600 clamp(40px,7vw,72px)/1.04 Georgia,serif;letter-spacing:-.035em}#intro p{max-width:55ch;margin:0 0 24px;color:color-mix(in srgb,var(--screen-foreground) 78%,transparent)}#intro a{color:var(--screen-accent)}.loading-status{max-width:520px;margin-top:24px}#status{min-height:1.5em;margin:0 0 14px;color:color-mix(in srgb,var(--screen-foreground) 78%,transparent);font-size:13px;letter-spacing:.03em}#progress{height:5px;overflow:hidden;border-radius:10px;background:color-mix(in srgb,var(--screen-foreground) 16%,transparent)}#progress-fill{display:block;width:0;height:100%;border-radius:inherit;background:var(--screen-accent);box-shadow:0 0 15px var(--screen-accent);transition:width .22s ease}.ready-hold #progress-fill{animation:ready-glow 1s ease-in-out infinite alternate}#unlock,#index-import{max-width:520px;margin-top:20px;padding:24px;border:1px solid color-mix(in srgb,var(--screen-accent) 35%,transparent);border-radius:12px;background:color-mix(in srgb,var(--screen-accent) 8%,transparent)}#content-key{width:100%;padding:10px;border:1px solid var(--screen-accent);border-radius:6px;background:#211710;color:#f6eddf;font:inherit}#index-file,#member-file{max-width:100%;font:inherit}.key-option{margin:8px 0;color:color-mix(in srgb,var(--screen-foreground) 75%,transparent);font-size:13px}#unlock button,#index-import button{padding:12px 22px;border:0;border-radius:8px;background:var(--screen-accent);color:#17100b;font:600 16px system-ui,sans-serif;cursor:pointer}#unlock button:hover,#unlock button:focus-visible,#index-import button:hover,#index-import button:focus-visible{filter:brightness(1.1)}body.awaiting-key .mode-label,body.awaiting-key .loading-status,body.awaiting-index .mode-label,body.awaiting-index .loading-status{display:none}body.awaiting-key main,body.awaiting-index main{align-items:center}body.awaiting-key #unlock,body.awaiting-index #index-import{width:min(100%,520px);margin-top:0}#key-feedback,#index-feedback{margin:16px 0 0;color:var(--screen-foreground);font-size:13px}#key-feedback[hidden],#index-feedback[hidden]{display:none}@keyframes orbit{to{transform:rotate(360deg)}}@keyframes appear{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}@keyframes boot-depart{0%,45%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-10px)}}@keyframes shutdown-depart{0%,55%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.96)}}@keyframes shutdown-orbit{0%{opacity:1;transform:rotate(0) scale(1)}to{opacity:0;transform:rotate(-180deg) scale(.15)}}@keyframes shutdown-core{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.1)}}@keyframes shutdown-drain{0%{transform:scaleX(1)}to{transform:scaleX(0)}}@keyframes ready-glow{0%{filter:brightness(.65);box-shadow:0 0 8px var(--screen-accent)}to{filter:brightness(1.65);box-shadow:0 0 28px var(--screen-accent)}}@keyframes pulse{50%{transform:scale(.55);opacity:.55}}@keyframes drift-one{to{transform:translate(-7vw,7vw)}}@keyframes drift-two{to{transform:translate(8vw,-5vw)}}.load-failed .orbit,.load-failed .core{animation-play-state:paused;opacity:.4}@media(prefers-reduced-motion:reduce){body:before,body:after,.orbit,.core,#intro:not([hidden]),.departing main,.ready-hold #progress-fill,.shutting-down main,.shutting-down #progress-fill{animation:none}#progress-fill{transition:none}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self'; img-src 'self'; connect-src 'self'; form-action 'none'; base-uri 'none'; object-src 'none'"><title>Opening Amoosed mode</title><link rel="icon" href="/favicon.ico"><link rel="stylesheet" href="/amoosed.com/boot.css"><canvas id="boot-scene" aria-hidden="true"></canvas><main><div id="loading-visual" class="loading-visual" aria-hidden="true"><span class="orbit orbit-one"></span><span class="orbit orbit-two"></span><span class="orbit orbit-three"></span><span class="core"></span></div><p class="mode-label">AMOOSED MODE</p><article id="intro" hidden></article><section class="loading-status" aria-label="Loading application"><p id="status" role="status">Opening application…</p><div id="progress" role="progressbar" aria-label="Verified app chunks" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"><span id="progress-fill"></span></div></section><form id="unlock" hidden><label for="content-key">Content key for <strong id="key-id"></strong></label><p><input id="content-key" type="password" inputmode="text" autocomplete="off" spellcheck="false" placeholder="64 hexadecimal characters"></p><p class="key-option">or choose the member key file shared by the site owner</p><p><input id="member-file" type="file" accept=".amwmember,text/plain"></p><button type="submit">Unlock content</button><p id="key-feedback" role="status" aria-live="polite" hidden></p></form><form id="index-import" hidden><label for="index-file">Open the Amoosed key file shared by this site's owner</label><p><input id="index-file" type="file" accept=".amwkey,.ico,.amw,application/octet-stream" required></p><button type="submit">Open Amoosed mode</button><p id="index-feedback" role="status" aria-live="polite" hidden></p></form></main><script type="module" src="/amoosed.com/boot.js"></script></html>
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
async function U(e,t){if(!Number.isSafeInteger(t)||t<0)throw new Error("Invalid response limit");let r=e.headers.get("Content-Length");if(r!==null&&(!/^[0-9]+$/.test(r)||!Number.isSafeInteger(Number(r))||Number(r)>t))throw e.body&&await e.body.cancel().catch(()=>{}),new Error("Carrier response exceeds byte limit");if(!e.body)return new Uint8Array(0);let o=e.body.getReader(),i=[],c=0,n=!1;try{for(;;){let{done:s,value:d}=await o.read();if(s){n=!0;break}if(c+=d.byteLength,c>t)throw new Error("Carrier response exceeds byte limit");i.push(d)}}finally{n||await o.cancel().catch(()=>{}),o.releaseLock()}let a=new Uint8Array(c),l=0;for(let s of i)a.set(s,l),l+=s.byteLength;return a}var L=4096,g=128,ie=1024*1024*1024,Q=16*1024*1024,Te=new TextDecoder("utf-8",{fatal:!0}),$=(e,t)=>e.length===t.length&&e.every((r,o)=>r===t[o]),O=async e=>new Uint8Array(await crypto.subtle.digest("SHA-256",e)),x=(e,t)=>{let r=Number(e.getBigUint64(t,!0));if(!Number.isSafeInteger(r))throw new Error("Unsafe offset");return r},se=(e,t)=>$(e.slice(0,8),new TextEncoder().encode(t)),Z=e=>JSON.parse(Te.decode(e));function Ne(e){if(e.length!==L||!se(e,"AMW1DOC\0"))throw new Error("Invalid AMW header");let t=new DataView(e.buffer,e.byteOffset,e.byteLength);if(t.getUint16(8,!0)!==1||t.getUint16(10,!0)!==L||e.slice(12,16).some(Boolean)||e.slice(120).some(Boolean))throw new Error("Unsupported AMW header");let r={total:x(t,16),manifestOffset:x(t,24),manifestLength:x(t,32),bootstrapOffset:x(t,40),bootstrapLength:x(t,48),manifestHash:e.slice(56,88),bootstrapHash:e.slice(88,120)};if(r.total>ie||r.total<L||r.manifestOffset!==L||r.manifestLength>1024*1024)throw new Error("Invalid AMW bounds");return r}function Oe(e,t){if(e.length!==g||!se(e,"AMW1TAIL"))throw new Error("No AMW footer");let r=new DataView(e.buffer,e.byteOffset,e.byteLength),o=r.getUint16(8,!0);if(![1,2].includes(o)||r.getUint16(10,!0)!==g||e.slice(12,16).some(Boolean)||o===1&&e.slice(104).some(Boolean))throw new Error("Unsupported footer");let i={version:o,carrierLength:x(r,16),payloadOffset:x(r,24),payloadLength:x(r,32),payloadHash:e.slice(40,72),bootstrapPrefixHash:o===2?e.slice(104,128):null};if(i.carrierLength!==i.payloadOffset||i.payloadLength>ie||i.payloadOffset+i.payloadLength+g!==t)throw new Error("Invalid carrier layout");return i}async function ce(e,{onBootstrap:t=()=>{},fetcher:r=fetch}={}){let o=new URL(e,globalThis.location?.href);if(o.protocol!=="https:"&&!(o.protocol==="http:"&&["localhost","127.0.0.1"].includes(o.hostname)))throw new Error("HTTPS required");let i,c,n;return Y(async(l,s,d=!1)=>{if(n)return d?n.slice(-g):n.slice(l,s+1);let f=await r(o,{headers:{Range:d?`bytes=-${g}`:`bytes=${l}-${s}`,...c?{"If-Range":c}:{}},cache:"no-store",credentials:"omit",redirect:"error"});if(f.status===404||f.status===410)throw new Error("No AMW footer");if(f.status===200){let E=f.headers.get("Content-Length");if(E!==null&&(!/^[0-9]+$/.test(E)||!Number.isSafeInteger(Number(E))))throw new Error("Invalid full response length");if(n=await U(f,Q),n.length<g&&d)throw new Error("No AMW footer");if(E!==null&&n.length!==Number(E)||i!==void 0&&i!==n.length)throw new Error("Full response length changed");return i=n.length,d?n.slice(-g):n.slice(l,s+1)}if(f.status!==206)throw new Error("Host must support HTTP byte ranges");let u=/^bytes (\d+)-(\d+)\/(\d+)$/.exec(f.headers.get("Content-Range")||"");if(!u)throw new Error("Invalid Content-Range");let[w,m,y]=u.slice(1).map(Number);if(Number.isSafeInteger(y)&&y<g)throw new Error("No AMW footer");if(!Number.isSafeInteger(y)||i!==void 0&&i!==y||(d?m!==y-1||m-w+1!==g:w!==l||m!==s))throw new Error("Range changed or mismatched");let h=f.headers.get("ETag");if(c&&h&&h!==c)throw new Error("ETag changed during load");!c&&h&&!h.startsWith("W/")&&(c=h),i=y;let A=await U(f,m-w+1);if(A.length!==m-w+1)throw new Error("Short range response");return A},()=>i,!o.pathname.endsWith(".amw"),t)}async function le(e,{onBootstrap:t=()=>{}}={}){let r=e instanceof Uint8Array?e:new Uint8Array(e);if(r.length<g||r.length>Q)throw new Error("Invalid local index size");return Y(async(i,c,n=!1)=>n?r.slice(-g):r.slice(i,c+1),()=>r.length,!0,t)}async function de(e){let t=e instanceof Uint8Array?e:new Uint8Array(e);if(t.length<L||t.length>Q)throw new Error("Invalid private index size");return Y(async(o,i)=>t.slice(o,i+1),()=>t.length,!1,()=>{})}async function Y(e,t,r,o){let i=r?Oe(await e(0,0,!0),t()):null,c=i?.payloadOffset||0,n=await e(c,c+L-1),a=Ne(n);if(i&&a.total!==i.payloadLength)throw new Error("Payload length mismatch");if(!i&&t()!==a.total)throw new Error("AMW length mismatch");let l=await e(c+a.manifestOffset,c+a.manifestOffset+a.manifestLength-1);if(!$(await O(l),a.manifestHash))throw new Error("Manifest integrity failure");let s=Z(l);if(s.format!=="amw.sections.v1"||!Array.isArray(s.sections)||s.sections.length!==2)throw new Error("Invalid manifest");let d=L+a.manifestLength;for(let h of s.sections){if(h.offset!==d||!Number.isSafeInteger(h.length)||h.length<1||!/^[0-9a-f]{64}$/.test(h.sha256))throw new Error("Invalid section layout");d+=h.length}if(d!==a.total||s.sections[0].id!=="bootstrap"||s.sections[1].id!=="document"||s.sections.some(h=>h.type!=="amw.document.v1")||s.sections[0].offset!==a.bootstrapOffset||s.sections[0].length!==a.bootstrapLength)throw new Error("Invalid section layout");let f=new Uint8Array(a.total);f.set(n,0),f.set(l,a.manifestOffset);let u=async h=>{let A=await e(c+h.offset,c+h.offset+h.length-1);if([...await O(A)].map(F=>F.toString(16).padStart(2,"0")).join("")!==h.sha256)throw new Error("Section integrity failure");return f.set(A,h.offset),A},w=await u(s.sections[0]);if(!$(await O(w),a.bootstrapHash))throw new Error("Bootstrap integrity failure");let m=Z(w);if(i?.version===2){let h=await O(f.subarray(0,a.bootstrapOffset+a.bootstrapLength));if(!$(h.subarray(0,24),i.bootstrapPrefixHash))throw new Error("Bootstrap prefix integrity failure");o(m)}let y=Z(await u(s.sections[1]));if(i&&!$(await O(f),i.payloadHash))throw new Error("Payload integrity failure");return(!i||i.version===1)&&o(m),y}var fe=/^[0-9a-f]{64}$/,$e=/^[0-9a-f]{24}$/,Pe=16*1024*1024+16,We=e=>[...e].map(t=>t.toString(16).padStart(2,"0")).join("");function R(e){if(!e||typeof e!="object"||Array.isArray(e)||Object.keys(e).some(t=>!["algorithm","url","offset","length","sha256","nonce","key"].includes(t))||e.algorithm!=="AES-256-GCM"||typeof e.url!="string"||!/^\/(?!\/)[A-Za-z0-9._/-]+$/.test(e.url)||e.url==="/favicon.ico"||e.url.startsWith("/amoosed.com/")||e.url.slice(1).split("/").some(t=>!t||t==="."||t==="..")||!Number.isSafeInteger(e.offset)||e.offset<0||!Number.isSafeInteger(e.length)||e.length<17||e.length>Pe||!fe.test(e.sha256)||!$e.test(e.nonce)||!fe.test(e.key))throw new Error("Invalid private index locator");return e}async function ue(e,t=fetch,r=location.origin){if(!e?.privateIndex)return e;if(e.webApp)throw new Error("Ambiguous index file");let o=R(e.privateIndex),i=new URL(o.url,r);if(i.origin!==r)throw new Error("Private index changed origin");let c=await t(i.href,{headers:{Range:`bytes=${o.offset}-${o.offset+o.length-1}`},cache:"no-store",credentials:"omit",redirect:"error"});if(!c.ok||new URL(c.url,r).origin!==r)throw new Error("Private index carrier unavailable");let n;if(c.status===206){let u=/^bytes (\d+)-(\d+)\/(\d+)$/.exec(c.headers.get("Content-Range")||"");if(!u||Number(u[1])!==o.offset||Number(u[2])!==o.offset+o.length-1||!Number.isSafeInteger(Number(u[3])))throw new Error("Private index range mismatch");n=await U(c,o.length)}else if(c.status===200)n=(await U(c,16777216)).slice(o.offset,o.offset+o.length);else throw new Error("Private index carrier needs byte ranges");if(n.length!==o.length||We(new Uint8Array(await crypto.subtle.digest("SHA-256",n)))!==o.sha256)throw new Error("Private index integrity failure");let a=await crypto.subtle.importKey("raw",Uint8Array.from(o.key.match(/../g),u=>parseInt(u,16)),"AES-GCM",!1,["decrypt"]),l=Uint8Array.from(o.nonce.match(/../g),u=>parseInt(u,16)),s=new TextEncoder().encode(`AMW1-PRIVATE-INDEX\0${o.url}\0${o.offset}`),d;try{d=new Uint8Array(await crypto.subtle.decrypt({name:"AES-GCM",iv:l,additionalData:s},a,n))}catch{throw new Error("Private index key or authentication failed")}let f=await de(d);if(f.privateIndex)throw new Error("Nested private index is not supported");return f}var me="AMQ1",z=93,he=new TextEncoder,ee=new TextDecoder("utf-8",{fatal:!0});function J(e){return Uint8Array.from(e.match(/../g),t=>Number.parseInt(t,16))}function X(e){return[...e].map(t=>t.toString(16).padStart(2,"0")).join("")}function we(e){return btoa(String.fromCharCode(...e)).replaceAll("+","-").replaceAll("/","_").replace(/=+$/,"")}function De(e){let t=R(e),r=he.encode(t.url);if(r.length>255)throw new Error("Private index carrier path is too long for a QR token");let o=new Uint8Array(z+r.length);o.set(he.encode(me)),o[4]=r.length;let i=new DataView(o.buffer);return i.setBigUint64(5,BigInt(t.offset)),i.setUint32(13,t.length),o.set(J(t.sha256),17),o.set(J(t.nonce),49),o.set(J(t.key),61),o.set(r,z),we(o)}function te(e){if(typeof e!="string"||!/^[A-Za-z0-9_-]{124,464}$/.test(e))throw new Error("Invalid Amoosed QR key");let t;try{t=Uint8Array.from(atob(e.replaceAll("-","+").replaceAll("_","/")),c=>c.charCodeAt(0))}catch{throw new Error("Invalid Amoosed QR key")}if(t.length!==z+t[4]||ee.decode(t.subarray(0,4))!==me||we(t)!==e)throw new Error("Invalid Amoosed QR key");let r=new DataView(t.buffer),o=Number(r.getBigUint64(5)),i={algorithm:"AES-256-GCM",url:ee.decode(t.subarray(z)),offset:o,length:r.getUint32(13),sha256:X(t.subarray(17,49)),nonce:X(t.subarray(49,61)),key:X(t.subarray(61,93))};if(R(i),De(i)!==e)throw new Error("Invalid Amoosed QR key");return i}function ye(e){let t=typeof e=="string"?e:ee.decode(e),r=/^AMWKEY1:([A-Za-z0-9_-]{124,464})\r?\n?$/.exec(t);if(!r)throw new Error("Invalid Amoosed key file");return te(r[1]),r[1]}var qe="AMM1",re=new TextDecoder("utf-8",{fatal:!0}),Be=/^[a-z0-9][a-z0-9._-]{0,63}$/i;function Fe(e){return btoa(String.fromCharCode(...e)).replaceAll("+","-").replaceAll("/","_").replace(/=+$/,"")}function H(e){if(typeof e!="string"||!/^[A-Za-z0-9_-]{51,135}$/.test(e))throw new Error("Invalid Amoosed member key");let t;try{t=Uint8Array.from(atob(e.replaceAll("-","+").replaceAll("_","/")),i=>i.charCodeAt(0))}catch{throw new Error("Invalid Amoosed member key")}if(t.length<38||t.length!==37+t[4]||re.decode(t.subarray(0,4))!==qe||Fe(t)!==e)throw new Error("Invalid Amoosed member key");let r=re.decode(t.subarray(37)),o=t.slice(5,37);if(!Be.test(r))throw new Error("Invalid Amoosed member key");return{keyId:r,key:o}}function ge(e){let t=typeof e=="string"?e:re.decode(e),r=/^AMWMEMBER1:([A-Za-z0-9_-]{51,135})\r?\n?$/.exec(t);if(!r)throw new Error("Invalid Amoosed member key file");return H(r[1]),r[1]}var P=/^[0-9a-f]{64}$/,_e=/^\/amoosed\.com\/app\/[A-Za-z0-9._/-]+$/,pe=/^\/(?!\/)[A-Za-z0-9._/-]+$/,ze=/^[a-z0-9.+-]+\/[a-z0-9.+-]+(?:; charset=utf-8)?$/,He=/^[A-Za-z0-9._-]{1,64}$/;function be(e){return typeof e=="string"&&_e.test(e)&&!e.slice(1).split("/").some(t=>t==="."||t===".."||t==="")&&!e.endsWith("/")}function W(e,t){return!!e.encryption&&t.access!=="public"}function ve(e){if(!e||typeof e!="object"||Array.isArray(e)||Object.keys(e).some(n=>!["launch","entry","resources","encryption","encoding","identity","audioCarriers"].includes(n))||e.launch!=="/amoosed.com/boot.html"||!be(e.entry)||!Array.isArray(e.resources)||e.resources.length<1||e.resources.length>4096)throw new Error("Invalid web app manifest");let t=e.encryption!==void 0;if(e.encoding!==void 0&&(!e.encoding||typeof e.encoding!="object"||Array.isArray(e.encoding)||Object.keys(e.encoding).some(n=>!["algorithm","key"].includes(n))||e.encoding.algorithm!=="AES-256-CTR"||typeof e.encoding.key!="string"||!P.test(e.encoding.key)))throw new Error("Invalid web app encoding");if(e.identity!==void 0&&(!e.identity||typeof e.identity!="object"||Array.isArray(e.identity)||Object.keys(e.identity).some(n=>!["title","icon"].includes(n))||typeof e.identity.title!="string"||!e.identity.title||e.identity.title.length>200||e.identity.icon!=="/amoosed.com/app/favicon.ico"))throw new Error("Invalid app identity");if(t&&(!e.encryption||typeof e.encryption!="object"||Array.isArray(e.encryption)||Object.keys(e.encryption).some(n=>!["algorithm","keyId"].includes(n))||e.encryption.algorithm!=="AES-256-GCM"||typeof e.encryption.keyId!="string"||!He.test(e.encryption.keyId)))throw new Error("Invalid web app encryption");if(e.audioCarriers!==void 0){if(!e.audioCarriers||typeof e.audioCarriers!="object"||Array.isArray(e.audioCarriers)||Object.keys(e.audioCarriers).length>64)throw new Error("Invalid audio carrier map");for(let[n,a]of Object.entries(e.audioCarriers))if(!pe.test(n)||!n.endsWith(".wav")||!a||a.format!=="amw.audio-carrier.v1"||!Number.isInteger(a.length)||a.length<1||a.length>4096||!P.test(a.sha256)||!Array.isArray(a.packets)||a.packets.length<1||a.packets.length>32||!Array.isArray(a.locations)||a.locations.length!==a.packets.length*3||a.packets.some((l,s)=>l.index!==s||!Number.isInteger(l.length)||l.length<1||l.length>128||!P.test(l.sha256))||a.packets.reduce((l,s)=>l+s.length,0)!==a.length||a.locations.some(l=>!Number.isInteger(l.packet)||l.packet<0||l.packet>=a.packets.length||!Number.isFinite(l.startSeconds)||l.startSeconds<0||!Number.isFinite(l.durationSeconds)||l.durationSeconds<=0))throw new Error("Invalid audio carrier map")}let r="",o=0,i=new Set;for(let n of e.resources){if(!n||typeof n!="object"||Array.isArray(n)||Object.keys(n).some(s=>!["path","mime","length","sha256","parts","access","delivery"].includes(s))||!be(n.path)||n.path<=r||n.access!==void 0&&(!t||!["public","members"].includes(n.access))||n.delivery!==void 0&&n.delivery!=="download"||typeof n.mime!="string"||!ze.test(n.mime)||!Number.isSafeInteger(n.length)||n.length<1||n.length>1024*1024*1024||typeof n.sha256!="string"||!P.test(n.sha256)||!Array.isArray(n.parts)||n.parts.length<1||n.parts.length>16384)throw new Error("Invalid web app resource");r=n.path;let a=W(e,n);a&&o++;let l=0;for(let s of n.parts){if(!s||typeof s!="object"||Array.isArray(s)||Object.keys(s).some(d=>!["url","offset","length","sha256","codec",...a?["nonce","plainLength"]:[]].includes(d))||typeof s.url!="string"||typeof s.sha256!="string"||!P.test(s.sha256)||!pe.test(s.url)||s.url.slice(1).split("/").some(d=>!d||d==="."||d==="..")||s.url==="/favicon.ico"||s.url.startsWith("/amoosed.com/")||s.codec!==void 0&&!(s.codec==="png-lsb1"&&s.url.endsWith(".png")||s.codec==="wav-4fsk1"&&s.url.endsWith(".wav")&&e.audioCarriers?.[s.url]||["frame-luma1","frame-luma2"].includes(s.codec)&&s.url.endsWith(".png")&&s.offset===0&&s.length<=21)||!Number.isSafeInteger(s.offset)||s.offset<0||!Number.isSafeInteger(s.length)||s.length<1||s.length>4*1024*1024+(a?16:0)||a&&(typeof s.nonce!="string"||!/^[0-9a-f]{24}$/.test(s.nonce)||!Number.isSafeInteger(s.plainLength)||s.plainLength<1||s.plainLength>4*1024*1024||s.length!==s.plainLength+16))throw new Error("Invalid web app shard");if(a){if(i.has(s.nonce))throw new Error("Repeated web app nonce");i.add(s.nonce)}l+=a?s.plainLength:s.length}if(l!==n.length)throw new Error("Web app resource length mismatch")}if(t&&o===0)throw new Error("Encrypted app has no member resources");let c=e.resources.find(n=>n.path===e.entry);if(!c||!c.mime.startsWith("text/html"))throw new Error("Web app entry must be HTML");if(c.delivery==="download")throw new Error("Web app entry cannot be a download");if(e.identity){let n=e.resources.find(a=>a.path===e.identity.icon);if(!n||n.mime!=="image/x-icon"||W(e,n)||n.delivery==="download")throw new Error("App identity favicon must be public")}return e}function je(e,t){if(/^amw:\/\//i.test(e)){let o=new URL(e);if(!o.hostname||o.username||o.password)throw new Error("Invalid AmoosedWeb link");let i=["localhost","127.0.0.1"].includes(o.hostname)?"http:":"https:";return{href:new URL(`${i}//${o.host}${o.pathname}${o.search}${o.hash}`).href,internal:!0}}if(/^(?:\/(?!\/)|\.{1,2}\/)/.test(e)){let o=new URL(t),i=new URL(e,o);if(i.origin!==o.origin)throw new Error("AmoosedWeb link changed origin");return{href:i.href,internal:!0}}let r=new URL(e);if(r.protocol!=="https:"||r.username||r.password)throw new Error("Only HTTPS links supported");return{href:r.href,internal:!1}}function Ae(e,t,r){if(!e||typeof e.title!="string"||!Array.isArray(e.blocks)||e.blocks.length>1e4)throw new Error("Invalid document");let o=e.theme||{};for(let c of["background","foreground","accent"])if(o[c]!==void 0&&!/^#[0-9a-fA-F]{6}$/.test(o[c]))throw new Error("Invalid document theme");let i=t.ownerDocument.createDocumentFragment();for(let c of e.blocks){if(!c||typeof c.text!="string"||c.text.length>1e4)throw new Error("Invalid block");let n;if(c.type==="heading")n=t.ownerDocument.createElement("h2");else if(c.type==="paragraph")n=t.ownerDocument.createElement("p");else if(c.type==="link"){let a=je(c.href,r);n=t.ownerDocument.createElement("a"),n.href=a.href,a.internal?n.dataset.amwTarget=a.href:(n.rel="noopener noreferrer",n.target="_blank")}else if(c.type==="launch"){n=t.ownerDocument.createElement("button"),n.type="button",n.className="amw-launch",n.dataset.amwLaunch="";let a=c.style||{};for(let l of["background","foreground"])if(a[l]!==void 0&&!/^#[0-9a-fA-F]{6}$/.test(a[l]))throw new Error("Invalid launch style");if(a.radius!==void 0&&(!Number.isInteger(a.radius)||a.radius<0||a.radius>32))throw new Error("Invalid launch style");n.style.backgroundColor=a.background??o.accent??"#155a70",n.style.color=a.foreground??"#ffffff",a.radius!==void 0&&(n.style.borderRadius=`${a.radius}px`)}else throw new Error("Unsupported block");n.textContent=c.text,i.append(n)}t.replaceChildren(i),t.style.backgroundColor=o.background??"",t.style.color=o.foreground??"",t.style.setProperty("--amw-accent",o.accent??""),t.ownerDocument.title=e.title.slice(0,200)}var Ke=new Uint8Array([0,97,115,109,1,0,0,0,1,7,1,96,2,127,127,1,127,3,2,1,0,7,7,1,3,97,100,100,0,0,10,9,1,7,0,32,0,32,1,106,11]),Ve=`
|
|
2
|
-
struct VertexOut { @builtin(position) position: vec4f, @location(0) uv: vec2f }
|
|
3
|
-
@group(0) @binding(0) var<uniform> scene: vec4f;
|
|
4
|
-
|
|
5
|
-
@vertex fn vertex_main(@builtin(vertex_index) index: u32) -> VertexOut {
|
|
6
|
-
var positions = array<vec2f,3>(vec2f(-1.0,-1.0),vec2f(3.0,-1.0),vec2f(-1.0,3.0));
|
|
7
|
-
var out: VertexOut;
|
|
8
|
-
out.position = vec4f(positions[index],0.0,1.0);
|
|
9
|
-
out.uv = positions[index] * 0.5 + vec2f(0.5);
|
|
10
|
-
return out;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
fn noise(p: vec2f) -> f32 { return fract(sin(dot(p,vec2f(127.1,311.7))) * 43758.5453); }
|
|
14
|
-
|
|
15
|
-
@fragment fn fragment_main(input: VertexOut) -> @location(0) vec4f {
|
|
16
|
-
let aspect = scene.y / scene.z;
|
|
17
|
-
let camera = vec3f(0.0,1.55,1.0);
|
|
18
|
-
let ray = normalize(vec3f((input.uv.x - 0.5) * 1.65 * aspect,(input.uv.y - 0.5) * 1.65,-1.0));
|
|
19
|
-
var distance = 1000.0;
|
|
20
|
-
var surface = 0u;
|
|
21
|
-
if (ray.y < -0.001) { let t = -camera.y / ray.y; if (t > 0.0 && t < distance) { distance=t; surface=1u; } }
|
|
22
|
-
if (ray.y > 0.001) { let t = (3.1-camera.y) / ray.y; if (t > 0.0 && t < distance) { distance=t; surface=2u; } }
|
|
23
|
-
if (abs(ray.x) > 0.001) {
|
|
24
|
-
let wall = select(-4.4,4.4,ray.x > 0.0);
|
|
25
|
-
let t = (wall-camera.x) / ray.x;
|
|
26
|
-
if (t > 0.0 && t < distance) { distance=t; surface=3u; }
|
|
27
|
-
}
|
|
28
|
-
if (ray.z < -0.001) { let t = (-7.0-camera.z) / ray.z; if (t > 0.0 && t < distance) { distance=t; surface=4u; } }
|
|
29
|
-
let hit = camera + ray * distance;
|
|
30
|
-
var color = vec3f(0.018,0.014,0.011);
|
|
31
|
-
if (surface == 1u) {
|
|
32
|
-
let seam = step(0.982,fract(hit.x * 1.45)) + step(0.985,fract(hit.z * 0.55));
|
|
33
|
-
color = vec3f(0.056,0.050,0.045) - min(seam,1.0) * vec3f(0.023);
|
|
34
|
-
}
|
|
35
|
-
if (surface == 2u) { color = vec3f(0.030,0.025,0.022); }
|
|
36
|
-
if (surface == 3u) {
|
|
37
|
-
let line = step(0.986,fract(hit.z * 0.31));
|
|
38
|
-
color = vec3f(0.042,0.036,0.031) - line * vec3f(0.014);
|
|
39
|
-
}
|
|
40
|
-
if (surface == 4u) {
|
|
41
|
-
color = vec3f(0.035,0.026,0.019);
|
|
42
|
-
if (abs(hit.x - 2.0) < 1.28 && hit.y > 1.02 && hit.y < 2.34) {
|
|
43
|
-
color = vec3f(0.009,0.006,0.004);
|
|
44
|
-
if (abs(hit.x - 2.0) < 1.12 && hit.y > 1.16 && hit.y < 2.23) {
|
|
45
|
-
let grain = noise(floor(vec2f(hit.x * 190.0,hit.y * 160.0)) + floor(scene.x * 13.0));
|
|
46
|
-
let scan = 0.83 + 0.17 * sin(hit.y * 290.0 + scene.x * 7.0);
|
|
47
|
-
color = vec3f(0.98,0.43,0.12) * (0.55 + grain * 0.38) * scan * scene.w;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
if (abs(hit.x - 2.0) < 0.07 && hit.y > 0.58 && hit.y < 1.03) { color = vec3f(0.075,0.078,0.078); }
|
|
51
|
-
if (abs(hit.x - 2.0) < 0.5 && hit.y > 0.52 && hit.y < 0.6) { color = vec3f(0.075,0.078,0.078); }
|
|
52
|
-
}
|
|
53
|
-
let spill = 0.27 / (1.0 + pow(length(hit.xz - vec2f(2.0,-6.7)),2.0) * 0.62);
|
|
54
|
-
color += vec3f(0.48,0.19,0.06) * spill * scene.w;
|
|
55
|
-
let fog = clamp(distance / 22.0,0.0,0.58);
|
|
56
|
-
color = mix(color,vec3f(0.007,0.005,0.004),fog);
|
|
57
|
-
let vignette = 1.0 - 0.74 * pow(length(input.uv - vec2f(0.5)) * 1.35,2.0);
|
|
58
|
-
return vec4f(color * max(vignette,0.13),1.0);
|
|
59
|
-
}`;async function Ee(e){if(!e||!navigator.gpu||matchMedia("(prefers-reduced-motion: reduce)").matches)return!1;try{let t=await navigator.gpu.requestAdapter();if(!t)return!1;let r=await t.requestDevice(),o=e.getContext("webgpu");if(!o)return!1;let i=navigator.gpu.getPreferredCanvasFormat();o.configure({device:r,format:i,alphaMode:"opaque"});let c=r.createShaderModule({code:Ve}),n=(await c.getCompilationInfo()).messages.filter(m=>m.type==="error");if(n.length)throw new Error(n.map(m=>m.message).join("; "));let a=r.createRenderPipeline({layout:"auto",vertex:{module:c,entryPoint:"vertex_main"},fragment:{module:c,entryPoint:"fragment_main",targets:[{format:i}]},primitive:{topology:"triangle-list"}}),l=r.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),s=r.createBindGroup({layout:a.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:l}}]}),d=(m,y)=>m+y;try{d=(await WebAssembly.instantiate(Ke)).instance.exports.add,e.dataset.wasm="active"}catch{e.dataset.wasm="unavailable"}let f=!0;r.lost.then(()=>{f=!1,e.hidden=!0}).catch(()=>{});let u=performance.now(),w=m=>{if(!(!f||!e.isConnected)){if(!document.hidden){let y=Math.min(1920,Math.max(1,Math.round(e.clientWidth*devicePixelRatio))),h=Math.min(1080,Math.max(1,Math.round(e.clientHeight*devicePixelRatio)));(e.width!==y||e.height!==h)&&(e.width=y,e.height=h);let A=Math.floor((m-u)/50),E=.88+.12*Math.sin(d(A,7)*.093);r.queue.writeBuffer(l,0,new Float32Array([(m-u)/1e3,y,h,E]));let F=r.createCommandEncoder(),_=F.beginRenderPass({colorAttachments:[{view:o.getCurrentTexture().createView(),loadOp:"clear",storeOp:"store",clearValue:[0,0,0,1]}]});_.setPipeline(a),_.setBindGroup(0,s),_.draw(3),_.end(),r.queue.submit([F.finish()])}requestAnimationFrame(w)}};return document.body.classList.add("has-boot-scene"),e.dataset.webgpu="active",requestAnimationFrame(w),!0}catch{return!1}}var S=document.querySelector("#status"),j=document.querySelector("#unlock"),xe=document.querySelector("#content-key"),ke=document.querySelector("#member-file"),b=document.querySelector("#key-feedback"),D=document.querySelector("#index-import"),Ie=document.querySelector("#index-file"),K=document.querySelector("#index-feedback"),C=document.querySelector("#intro"),p=document.querySelector("#progress"),Ce=document.querySelector("#progress-fill"),G=document.querySelector("#loading-visual");Ee(document.querySelector("#boot-scene"));var M=[],Le=location.pathname.startsWith("/amoosed.com/app/"),B=new URL(location.href).searchParams,ne=B.has("panel"),Ge=B.has("start"),Me=B.get("shutdown")==="1",T=B.has("forget"),k=new URLSearchParams(location.hash.slice(1)).get("amw-key"),I=new URLSearchParams(location.hash.slice(1)).get("amw-member"),q=new URLSearchParams(location.hash.slice(1)).get("amw-index"),oe=B.get("return"),N=!Le&&!Me&&!ne&&!Ge&&!T&&k===null&&I===null&&oe===null;(k!==null||I!==null||q!==null)&&history.replaceState(null,"",location.pathname+location.search);function v(e,t,r,o,i){return new Promise((c,n)=>{let a=new MessageChannel,l=setTimeout(()=>n(new Error("Amoosed content check timed out")),t==="amw:installIndex"||t==="amw:installIndexToken"?12e4:15e3);a.port1.onmessage=s=>{clearTimeout(l),a.port1.close(),s.data?.error?n(new Error(s.data.error)):c(s.data)},e.postMessage({type:t,...r?{key:r}:{},...o?{bytes:o}:{},...i?{token:i}:{}},[a.port2])})}function V(e){let t=()=>{Le?location.reload():location.replace(e)};if(C.hidden)t();else{let r=matchMedia("(prefers-reduced-motion: reduce)").matches,o=()=>{let n=document.querySelector("main"),a=!1,l=()=>{a||(a=!0,n.removeEventListener("animationend",s),t())},s=f=>{f.target!==n||f.animationName!=="boot-depart"||(clearTimeout(d),setTimeout(l,300))};n.addEventListener("animationend",s),document.body.classList.add("departing");let d=setTimeout(l,3200)},i=performance.now();document.body.classList.add("ready-hold");let c=()=>{let n=Math.max(0,2e3-(performance.now()-i));S.textContent=n?`Opening in ${(n/1e3).toFixed(1)}s…`:"Opening…",n?requestAnimationFrame(c):(document.body.classList.remove("ready-hold"),r?t():o())};c()}}function Ze(){document.title="Leaving Amoosed mode",document.querySelector(".mode-label").textContent="CLOSING AMOOSED MODE";let e=document.createElement("h2");e.textContent="Leaving Amoosed mode";let t=document.createElement("p");t.textContent="Returning to the original site.",C.replaceChildren(e,t),C.hidden=!1,S.textContent="Closing Amoosed mode…",p.setAttribute("aria-label","Closing Amoosed mode"),Ce.style.width="100%",document.body.classList.add("shutting-down");let r=matchMedia("(prefers-reduced-motion: reduce)").matches?2e3:2700;requestAnimationFrame(()=>setTimeout(()=>location.replace("/"),r))}function Ue(e){Ae({...e,blocks:e.blocks.filter(r=>r.type!=="launch")},C,location.href);let t=e.theme||{};t.background&&document.documentElement.style.setProperty("--screen-background",t.background),t.foreground&&document.documentElement.style.setProperty("--screen-foreground",t.foreground),t.accent&&document.documentElement.style.setProperty("--screen-accent",t.accent),C.hidden=!1,S.textContent="Verifying Amoosed content…"}function Qe({completed:e,total:t,path:r,index:o}){typeof r=="string"&&Number.isInteger(o)&&(M.push([r,o]),M.length>512&&M.shift());let i=t?Math.floor(100*e/t):100;p.setAttribute("aria-valuenow",String(i)),Ce.style.width=`${i}%`;let c=JSON.stringify({completed:e,total:t,parts:M});for(;c.length>65536&&M.length;)M.shift(),c=JSON.stringify({completed:e,total:t,parts:M});p.dataset.amwProgress=c,S.textContent=`Loading verified content · ${e} of ${t} chunks`}function ae(e){let t;try{t=JSON.parse(p.dataset.amwProgress||"")}catch{t={completed:0,total:0,parts:[]}}p.dataset.amwProgress=JSON.stringify({...t,error:e.message.slice(0,500)}),p.hidden=!0,document.body.classList.add("load-failed"),S.hidden=!1,S.textContent="Could not open Amoosed mode. See the amoosed.com side panel for details."}function Se(e){return new Promise((t,r)=>{let o=new MessageChannel,i,c=(a,l)=>{clearTimeout(i),o.port1.close(),a?r(a):t(l)},n=()=>{clearTimeout(i),i=setTimeout(()=>c(new Error("Content loading timed out")),12e4)};o.port1.onmessage=a=>{n(),a.data?.progress?Qe(a.data.progress):a.data?.error?c(new Error(a.data.error)):(a.data?.ready||a.data?.keyRequired)&&c(null,a.data)},n(),e.postMessage({type:"amw:prepare"},[o.port2])})}async function Re(e,t){let r=ve(t.webApp),o=oe===null?null:new URL(oe,location.origin);if(o&&(o.origin!==location.origin||!r.resources.some(n=>n.path===o.pathname&&W(r,n))))throw new Error("Invalid member return route");let i=o?o.pathname+o.search:r.entry,c=!1;if(N&&(Ue(t.bootstrap??{title:t.title,blocks:t.blocks.slice(0,1),theme:t.theme}),c=(await Se(e.active)).keyRequired===!0,c||V(i)),I!==null&&(!r.encryption||k!==null||q!==null))throw new Error("This member access link does not match this app");if(!N&&(!r.encryption||!o&&!ne&&!T&&k===null&&I===null&&!W(r,r.resources.find(n=>n.path===r.entry))))V(i);else if(c||!N){let a=(await v(e.active,T?"amw:forgetKey":"amw:hasKey")).ready,l;if(!T&&(k!==null||I!==null)){let s;try{if(I!==null){let d=H(I);if(d.keyId!==r.encryption.keyId)throw new Error("This member key belongs to another edition.");s=d.key}else{if(!/^[0-9a-fA-F]{64}$/.test(k))throw new Error("The shared access link is invalid.");s=new Uint8Array(k.match(/../g).map(d=>parseInt(d,16)))}a=(await v(e.active,"amw:setKey",s)).ready===!0}catch(d){a=!1,l=d.message}finally{s?.fill(0)}}a&&!ne&&!T?V(i):a?S.textContent="This content is unlocked on this device.":(C.hidden=!0,G.hidden=!0,p.hidden=!0,document.querySelector("#key-id").textContent=r.encryption.keyId,document.body.classList.add("awaiting-key"),b.textContent=T?"The saved key was removed.":l||"",b.hidden=!b.textContent,j.hidden=!1,j.addEventListener("submit",async s=>{s.preventDefault();let d=xe.value.trim();xe.value="";let f;try{let u=ke.files?.[0];if(u){if(u.size>512)throw new Error("Member key file is too large.");let w=H(ge(new Uint8Array(await u.arrayBuffer())));if(w.keyId!==r.encryption.keyId)throw new Error("This member key belongs to another edition.");f=w.key,ke.value=""}else if(/^[0-9a-fA-F]{64}$/.test(d))f=new Uint8Array(d.match(/../g).map(w=>parseInt(w,16)));else throw new Error("Enter a 64-character key or choose a member key file.")}catch(u){b.textContent=u.message,b.hidden=!1;return}b.textContent="Checking content key…",b.hidden=!1;try{if(!(await v(e.active,"amw:setKey",f)).ready)throw new Error("Content key was rejected");if(N&&(j.hidden=!0,document.body.classList.remove("awaiting-key"),C.hidden=!1,G.hidden=!1,p.hidden=!1,!(await Se(e.active)).ready))throw new Error("Content key did not unlock the entry");V(i)}catch(u){N&&j.hidden?ae(u):(b.textContent=u.message,b.hidden=!1)}finally{f.fill(0)}}))}}function Ye(e){C.hidden=!0,G.hidden=!0,p.hidden=!0,document.body.classList.add("awaiting-index"),D.hidden=!1,D.addEventListener("submit",async t=>{t.preventDefault();let r=Ie.files?.[0];if(!r)return;K.hidden=!1,K.textContent="Checking index and this site’s carrier files…";let o=D.querySelector("button");o.disabled=!0;try{if(r.size>16*1024*1024)throw new Error("Index file is too large");let i=new Uint8Array(await r.arrayBuffer());if(r.name.toLowerCase().endsWith(".amwkey")||new TextDecoder().decode(i.subarray(0,8))==="AMWKEY1:"){let n=ye(i);await v(e.active,"amw:installIndexToken",null,null,n)}else{let n=await le(i);R(n.privateIndex),await v(e.active,"amw:installIndex",null,i)}let c=(await v(e.active,"amw:getIndex")).document;D.hidden=!0,document.body.classList.remove("awaiting-index"),G.hidden=!1,p.hidden=!1,Ie.value="",await Re(e,c)}catch(i){D.hidden?ae(i):(K.textContent=i.message,K.hidden=!1)}finally{o.disabled=!1}})}if(Me)Ze();else try{if(!("serviceWorker"in navigator))throw new Error("This browser does not support service workers");let e=await navigator.serviceWorker.register("/amoosed.com/sw.js",{scope:"/amoosed.com/",type:"module",updateViaCache:"none"});if(await navigator.serviceWorker.ready,!e.active)throw new Error("The app service worker did not activate");let t;if(q!==null){if(k!==null||I!==null)throw new Error("Use one Amoosed access link at a time");te(q),S.textContent="Checking shared Amoosed access…",await v(e.active,"amw:installIndexToken",null,null,q),t=(await v(e.active,"amw:getIndex")).document}try{t||(t=await ue(await ce("/favicon.ico",{onBootstrap:r=>{N&&Ue(r)}})))}catch(r){if(!/No AMW footer/.test(r.message))throw r;try{t=(await v(e.active,"amw:getIndex")).document}catch(o){if(!/index file is required/i.test(o.message))throw o;Ye(e)}}t&&await Re(e,t)}catch(e){ae(e)}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
document.addEventListener("click",async a=>{let t=a.target instanceof Element?a.target.closest("a[download]"):null;if(!t)return;let o=new URL(t.href,location.href);if(!(o.origin!==location.origin||!o.pathname.startsWith("/amoosed.com/app/"))){a.preventDefault();try{let e=await fetch(o.href,{cache:"no-store",credentials:"same-origin",redirect:"error"});if(!e.ok||!/^attachment(?:;|$)/i.test(e.headers.get("Content-Disposition")||""))throw new Error("Verified download is unavailable");let r=URL.createObjectURL(await e.blob()),n=document.createElement("a");n.href=r,n.download=t.download||o.pathname.split("/").at(-1),n.click(),setTimeout(()=>URL.revokeObjectURL(r),6e4)}catch(e){t.dispatchEvent(new CustomEvent("amw:download-error",{detail:e.message}))}}});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
async function b(e,t){if(!Number.isSafeInteger(t)||t<0)throw new Error("Invalid response limit");let n=e.headers.get("Content-Length");if(n!==null&&(!/^[0-9]+$/.test(n)||!Number.isSafeInteger(Number(n))||Number(n)>t))throw e.body&&await e.body.cancel().catch(()=>{}),new Error("Carrier response exceeds byte limit");if(!e.body)return new Uint8Array(0);let r=e.body.getReader(),o=[],s=0,i=!1;try{for(;;){let{done:a,value:f}=await r.read();if(a){i=!0;break}if(s+=f.byteLength,s>t)throw new Error("Carrier response exceeds byte limit");o.push(f)}}finally{i||await r.cancel().catch(()=>{}),r.releaseLock()}let c=new Uint8Array(s),l=0;for(let a of o)c.set(a,l),l+=a.byteLength;return c}var v=4096,A=128,te=1024*1024*1024,q=16*1024*1024,Re=new TextDecoder("utf-8",{fatal:!0}),M=(e,t)=>e.length===t.length&&e.every((n,r)=>n===t[r]),S=async e=>new Uint8Array(await crypto.subtle.digest("SHA-256",e)),I=(e,t)=>{let n=Number(e.getBigUint64(t,!0));if(!Number.isSafeInteger(n))throw new Error("Unsafe offset");return n},re=(e,t)=>M(e.slice(0,8),new TextEncoder().encode(t)),j=e=>JSON.parse(Re.decode(e));function Ne(e){if(e.length!==v||!re(e,"AMW1DOC\0"))throw new Error("Invalid AMW header");let t=new DataView(e.buffer,e.byteOffset,e.byteLength);if(t.getUint16(8,!0)!==1||t.getUint16(10,!0)!==v||e.slice(12,16).some(Boolean)||e.slice(120).some(Boolean))throw new Error("Unsupported AMW header");let n={total:I(t,16),manifestOffset:I(t,24),manifestLength:I(t,32),bootstrapOffset:I(t,40),bootstrapLength:I(t,48),manifestHash:e.slice(56,88),bootstrapHash:e.slice(88,120)};if(n.total>te||n.total<v||n.manifestOffset!==v||n.manifestLength>1024*1024)throw new Error("Invalid AMW bounds");return n}function We(e,t){if(e.length!==A||!re(e,"AMW1TAIL"))throw new Error("No AMW footer");let n=new DataView(e.buffer,e.byteOffset,e.byteLength),r=n.getUint16(8,!0);if(![1,2].includes(r)||n.getUint16(10,!0)!==A||e.slice(12,16).some(Boolean)||r===1&&e.slice(104).some(Boolean))throw new Error("Unsupported footer");let o={version:r,carrierLength:I(n,16),payloadOffset:I(n,24),payloadLength:I(n,32),payloadHash:e.slice(40,72),bootstrapPrefixHash:r===2?e.slice(104,128):null};if(o.carrierLength!==o.payloadOffset||o.payloadLength>te||o.payloadOffset+o.payloadLength+A!==t)throw new Error("Invalid carrier layout");return o}async function T(e,{onBootstrap:t=()=>{},fetcher:n=fetch}={}){let r=new URL(e,globalThis.location?.href);if(r.protocol!=="https:"&&!(r.protocol==="http:"&&["localhost","127.0.0.1"].includes(r.hostname)))throw new Error("HTTPS required");let o,s,i;return K(async(l,a,f=!1)=>{if(i)return f?i.slice(-A):i.slice(l,a+1);let d=await n(r,{headers:{Range:f?`bytes=-${A}`:`bytes=${l}-${a}`,...s?{"If-Range":s}:{}},cache:"no-store",credentials:"omit",redirect:"error"});if(d.status===404||d.status===410)throw new Error("No AMW footer");if(d.status===200){let E=d.headers.get("Content-Length");if(E!==null&&(!/^[0-9]+$/.test(E)||!Number.isSafeInteger(Number(E))))throw new Error("Invalid full response length");if(i=await b(d,q),i.length<A&&f)throw new Error("No AMW footer");if(E!==null&&i.length!==Number(E)||o!==void 0&&o!==i.length)throw new Error("Full response length changed");return o=i.length,f?i.slice(-A):i.slice(l,a+1)}if(d.status!==206)throw new Error("Host must support HTTP byte ranges");let u=/^bytes (\d+)-(\d+)\/(\d+)$/.exec(d.headers.get("Content-Range")||"");if(!u)throw new Error("Invalid Content-Range");let[h,w,g]=u.slice(1).map(Number);if(Number.isSafeInteger(g)&&g<A)throw new Error("No AMW footer");if(!Number.isSafeInteger(g)||o!==void 0&&o!==g||(f?w!==g-1||w-h+1!==A:h!==l||w!==a))throw new Error("Range changed or mismatched");let y=d.headers.get("ETag");if(s&&y&&y!==s)throw new Error("ETag changed during load");!s&&y&&!y.startsWith("W/")&&(s=y),o=g;let m=await b(d,w-h+1);if(m.length!==w-h+1)throw new Error("Short range response");return m},()=>o,!r.pathname.endsWith(".amw"),t)}async function F(e,{onBootstrap:t=()=>{}}={}){let n=e instanceof Uint8Array?e:new Uint8Array(e);if(n.length<A||n.length>q)throw new Error("Invalid local index size");return K(async(o,s,i=!1)=>i?n.slice(-A):n.slice(o,s+1),()=>n.length,!0,t)}async function ne(e){let t=e instanceof Uint8Array?e:new Uint8Array(e);if(t.length<v||t.length>q)throw new Error("Invalid private index size");return K(async(r,o)=>t.slice(r,o+1),()=>t.length,!1,()=>{})}async function K(e,t,n,r){let o=n?We(await e(0,0,!0),t()):null,s=o?.payloadOffset||0,i=await e(s,s+v-1),c=Ne(i);if(o&&c.total!==o.payloadLength)throw new Error("Payload length mismatch");if(!o&&t()!==c.total)throw new Error("AMW length mismatch");let l=await e(s+c.manifestOffset,s+c.manifestOffset+c.manifestLength-1);if(!M(await S(l),c.manifestHash))throw new Error("Manifest integrity failure");let a=j(l);if(a.format!=="amw.sections.v1"||!Array.isArray(a.sections)||a.sections.length!==2)throw new Error("Invalid manifest");let f=v+c.manifestLength;for(let y of a.sections){if(y.offset!==f||!Number.isSafeInteger(y.length)||y.length<1||!/^[0-9a-f]{64}$/.test(y.sha256))throw new Error("Invalid section layout");f+=y.length}if(f!==c.total||a.sections[0].id!=="bootstrap"||a.sections[1].id!=="document"||a.sections.some(y=>y.type!=="amw.document.v1")||a.sections[0].offset!==c.bootstrapOffset||a.sections[0].length!==c.bootstrapLength)throw new Error("Invalid section layout");let d=new Uint8Array(c.total);d.set(i,0),d.set(l,c.manifestOffset);let u=async y=>{let m=await e(s+y.offset,s+y.offset+y.length-1);if([...await S(m)].map(Le=>Le.toString(16).padStart(2,"0")).join("")!==y.sha256)throw new Error("Section integrity failure");return d.set(m,y.offset),m},h=await u(a.sections[0]);if(!M(await S(h),c.bootstrapHash))throw new Error("Bootstrap integrity failure");let w=j(h);if(o?.version===2){let y=await S(d.subarray(0,c.bootstrapOffset+c.bootstrapLength));if(!M(y.subarray(0,24),o.bootstrapPrefixHash))throw new Error("Bootstrap prefix integrity failure");r(w)}let g=j(await u(a.sections[1]));if(o&&!M(await S(d),o.payloadHash))throw new Error("Payload integrity failure");return(!o||o.version===1)&&r(w),g}var oe=/^[0-9a-f]{64}$/,$e=/^[0-9a-f]{24}$/,Te=16*1024*1024+16,De=e=>[...e].map(t=>t.toString(16).padStart(2,"0")).join("");function D(e){if(!e||typeof e!="object"||Array.isArray(e)||Object.keys(e).some(t=>!["algorithm","url","offset","length","sha256","nonce","key"].includes(t))||e.algorithm!=="AES-256-GCM"||typeof e.url!="string"||!/^\/(?!\/)[A-Za-z0-9._/-]+$/.test(e.url)||e.url==="/favicon.ico"||e.url.startsWith("/amoosed.com/")||e.url.slice(1).split("/").some(t=>!t||t==="."||t==="..")||!Number.isSafeInteger(e.offset)||e.offset<0||!Number.isSafeInteger(e.length)||e.length<17||e.length>Te||!oe.test(e.sha256)||!$e.test(e.nonce)||!oe.test(e.key))throw new Error("Invalid private index locator");return e}async function _(e,t=fetch,n=location.origin){if(!e?.privateIndex)return e;if(e.webApp)throw new Error("Ambiguous index file");let r=D(e.privateIndex),o=new URL(r.url,n);if(o.origin!==n)throw new Error("Private index changed origin");let s=await t(o.href,{headers:{Range:`bytes=${r.offset}-${r.offset+r.length-1}`},cache:"no-store",credentials:"omit",redirect:"error"});if(!s.ok||new URL(s.url,n).origin!==n)throw new Error("Private index carrier unavailable");let i;if(s.status===206){let u=/^bytes (\d+)-(\d+)\/(\d+)$/.exec(s.headers.get("Content-Range")||"");if(!u||Number(u[1])!==r.offset||Number(u[2])!==r.offset+r.length-1||!Number.isSafeInteger(Number(u[3])))throw new Error("Private index range mismatch");i=await b(s,r.length)}else if(s.status===200)i=(await b(s,16777216)).slice(r.offset,r.offset+r.length);else throw new Error("Private index carrier needs byte ranges");if(i.length!==r.length||De(new Uint8Array(await crypto.subtle.digest("SHA-256",i)))!==r.sha256)throw new Error("Private index integrity failure");let c=await crypto.subtle.importKey("raw",Uint8Array.from(r.key.match(/../g),u=>parseInt(u,16)),"AES-GCM",!1,["decrypt"]),l=Uint8Array.from(r.nonce.match(/../g),u=>parseInt(u,16)),a=new TextEncoder().encode(`AMW1-PRIVATE-INDEX\0${r.url}\0${r.offset}`),f;try{f=new Uint8Array(await crypto.subtle.decrypt({name:"AES-GCM",iv:l,additionalData:a},c,i))}catch{throw new Error("Private index key or authentication failed")}let d=await ne(f);if(d.privateIndex)throw new Error("Nested private index is not supported");return d}var se="AMQ1",O=93,ae=new TextEncoder,ie=new TextDecoder("utf-8",{fatal:!0});function V(e){return Uint8Array.from(e.match(/../g),t=>Number.parseInt(t,16))}function z(e){return[...e].map(t=>t.toString(16).padStart(2,"0")).join("")}function ce(e){return btoa(String.fromCharCode(...e)).replaceAll("+","-").replaceAll("/","_").replace(/=+$/,"")}function Oe(e){let t=D(e),n=ae.encode(t.url);if(n.length>255)throw new Error("Private index carrier path is too long for a QR token");let r=new Uint8Array(O+n.length);r.set(ae.encode(se)),r[4]=n.length;let o=new DataView(r.buffer);return o.setBigUint64(5,BigInt(t.offset)),o.setUint32(13,t.length),r.set(V(t.sha256),17),r.set(V(t.nonce),49),r.set(V(t.key),61),r.set(n,O),ce(r)}function B(e){if(typeof e!="string"||!/^[A-Za-z0-9_-]{124,464}$/.test(e))throw new Error("Invalid Amoosed QR key");let t;try{t=Uint8Array.from(atob(e.replaceAll("-","+").replaceAll("_","/")),s=>s.charCodeAt(0))}catch{throw new Error("Invalid Amoosed QR key")}if(t.length!==O+t[4]||ie.decode(t.subarray(0,4))!==se||ce(t)!==e)throw new Error("Invalid Amoosed QR key");let n=new DataView(t.buffer),r=Number(n.getBigUint64(5)),o={algorithm:"AES-256-GCM",url:ie.decode(t.subarray(O)),offset:r,length:n.getUint32(13),sha256:z(t.subarray(17,49)),nonce:z(t.subarray(49,61)),key:z(t.subarray(61,93))};if(D(o),Oe(o)!==e)throw new Error("Invalid Amoosed QR key");return o}var U=/^[0-9a-f]{64}$/,Be=/^\/amoosed\.com\/app\/[A-Za-z0-9._/-]+$/,le=/^\/(?!\/)[A-Za-z0-9._/-]+$/,He=/^[a-z0-9.+-]+\/[a-z0-9.+-]+(?:; charset=utf-8)?$/,je=/^[A-Za-z0-9._-]{1,64}$/;function de(e,t,n){return new TextEncoder().encode(`AMW1-SEALED\0${e.path}\0${e.mime}\0${t}\0${n}`)}function fe(e){return typeof e=="string"&&Be.test(e)&&!e.slice(1).split("/").some(t=>t==="."||t===".."||t==="")&&!e.endsWith("/")}function p(e,t){return!!e.encryption&&t.access!=="public"}function k(e){if(!e||typeof e!="object"||Array.isArray(e)||Object.keys(e).some(i=>!["launch","entry","resources","encryption","encoding","identity","audioCarriers"].includes(i))||e.launch!=="/amoosed.com/boot.html"||!fe(e.entry)||!Array.isArray(e.resources)||e.resources.length<1||e.resources.length>4096)throw new Error("Invalid web app manifest");let t=e.encryption!==void 0;if(e.encoding!==void 0&&(!e.encoding||typeof e.encoding!="object"||Array.isArray(e.encoding)||Object.keys(e.encoding).some(i=>!["algorithm","key"].includes(i))||e.encoding.algorithm!=="AES-256-CTR"||typeof e.encoding.key!="string"||!U.test(e.encoding.key)))throw new Error("Invalid web app encoding");if(e.identity!==void 0&&(!e.identity||typeof e.identity!="object"||Array.isArray(e.identity)||Object.keys(e.identity).some(i=>!["title","icon"].includes(i))||typeof e.identity.title!="string"||!e.identity.title||e.identity.title.length>200||e.identity.icon!=="/amoosed.com/app/favicon.ico"))throw new Error("Invalid app identity");if(t&&(!e.encryption||typeof e.encryption!="object"||Array.isArray(e.encryption)||Object.keys(e.encryption).some(i=>!["algorithm","keyId"].includes(i))||e.encryption.algorithm!=="AES-256-GCM"||typeof e.encryption.keyId!="string"||!je.test(e.encryption.keyId)))throw new Error("Invalid web app encryption");if(e.audioCarriers!==void 0){if(!e.audioCarriers||typeof e.audioCarriers!="object"||Array.isArray(e.audioCarriers)||Object.keys(e.audioCarriers).length>64)throw new Error("Invalid audio carrier map");for(let[i,c]of Object.entries(e.audioCarriers))if(!le.test(i)||!i.endsWith(".wav")||!c||c.format!=="amw.audio-carrier.v1"||!Number.isInteger(c.length)||c.length<1||c.length>4096||!U.test(c.sha256)||!Array.isArray(c.packets)||c.packets.length<1||c.packets.length>32||!Array.isArray(c.locations)||c.locations.length!==c.packets.length*3||c.packets.some((l,a)=>l.index!==a||!Number.isInteger(l.length)||l.length<1||l.length>128||!U.test(l.sha256))||c.packets.reduce((l,a)=>l+a.length,0)!==c.length||c.locations.some(l=>!Number.isInteger(l.packet)||l.packet<0||l.packet>=c.packets.length||!Number.isFinite(l.startSeconds)||l.startSeconds<0||!Number.isFinite(l.durationSeconds)||l.durationSeconds<=0))throw new Error("Invalid audio carrier map")}let n="",r=0,o=new Set;for(let i of e.resources){if(!i||typeof i!="object"||Array.isArray(i)||Object.keys(i).some(a=>!["path","mime","length","sha256","parts","access","delivery"].includes(a))||!fe(i.path)||i.path<=n||i.access!==void 0&&(!t||!["public","members"].includes(i.access))||i.delivery!==void 0&&i.delivery!=="download"||typeof i.mime!="string"||!He.test(i.mime)||!Number.isSafeInteger(i.length)||i.length<1||i.length>1024*1024*1024||typeof i.sha256!="string"||!U.test(i.sha256)||!Array.isArray(i.parts)||i.parts.length<1||i.parts.length>16384)throw new Error("Invalid web app resource");n=i.path;let c=p(e,i);c&&r++;let l=0;for(let a of i.parts){if(!a||typeof a!="object"||Array.isArray(a)||Object.keys(a).some(f=>!["url","offset","length","sha256","codec",...c?["nonce","plainLength"]:[]].includes(f))||typeof a.url!="string"||typeof a.sha256!="string"||!U.test(a.sha256)||!le.test(a.url)||a.url.slice(1).split("/").some(f=>!f||f==="."||f==="..")||a.url==="/favicon.ico"||a.url.startsWith("/amoosed.com/")||a.codec!==void 0&&!(a.codec==="png-lsb1"&&a.url.endsWith(".png")||a.codec==="wav-4fsk1"&&a.url.endsWith(".wav")&&e.audioCarriers?.[a.url]||["frame-luma1","frame-luma2"].includes(a.codec)&&a.url.endsWith(".png")&&a.offset===0&&a.length<=21)||!Number.isSafeInteger(a.offset)||a.offset<0||!Number.isSafeInteger(a.length)||a.length<1||a.length>4*1024*1024+(c?16:0)||c&&(typeof a.nonce!="string"||!/^[0-9a-f]{24}$/.test(a.nonce)||!Number.isSafeInteger(a.plainLength)||a.plainLength<1||a.plainLength>4*1024*1024||a.length!==a.plainLength+16))throw new Error("Invalid web app shard");if(c){if(o.has(a.nonce))throw new Error("Repeated web app nonce");o.add(a.nonce)}l+=c?a.plainLength:a.length}if(l!==i.length)throw new Error("Web app resource length mismatch")}if(t&&r===0)throw new Error("Encrypted app has no member resources");let s=e.resources.find(i=>i.path===e.entry);if(!s||!s.mime.startsWith("text/html"))throw new Error("Web app entry must be HTML");if(s.delivery==="download")throw new Error("Web app entry cannot be a download");if(e.identity){let i=e.resources.find(c=>c.path===e.identity.icon);if(!i||i.mime!=="image/x-icon"||p(e,i)||i.delivery==="download")throw new Error("App identity favicon must be public")}return e}var qe=new Uint8Array([65,77,87,80,88,49,0,0]),C=44,he=16*1024*1024,ue=e=>[...e].map(t=>t.toString(16).padStart(2,"0")).join("");function Fe(e,t){for(;t;)[e,t]=[t,e%t];return e}async function Ke(e,t,n){let r=t*n;if(!Number.isSafeInteger(r)||r<1||r>he||e.length!==r*4)throw new Error("Invalid pixel carrier dimensions");let o=[];for(let u=0;u<r;u++)e[u*4+3]===255&&o.push(u);let s=o.length*3;if(s<C*8)throw new Error("Pixel carrier is too small");let i=65537;for(;Fe(i,s)!==1;)i+=2;function c(u){if(u*8>s)throw new Error("Pixel carrier length exceeds capacity");let h=new Uint8Array(u),w=0;for(let g=0;g<u;g++)for(let y=7;y>=0;y--){let m=o[Math.floor(w/3)]*4+w%3;h[g]|=(e[m]&1)<<y,w=(w+i)%s}return h}let l=c(C);if(!qe.every((u,h)=>l[h]===u))throw new Error("No AmoosedWeb pixel payload");let a=new DataView(l.buffer).getUint32(8,!0);if(a<1||a>16*1024*1024||(a+C)*8>s)throw new Error("Invalid pixel payload length");let f=c(C+a).slice(C),d=new Uint8Array(await crypto.subtle.digest("SHA-256",f));if(ue(d)!==ue(l.slice(12,44)))throw new Error("Pixel payload integrity failure");return f}async function we(e){if(e.length>64*1024*1024)throw new Error("Pixel carrier file is too large");let t=await createImageBitmap(new Blob([e],{type:"image/png"}),{colorSpaceConversion:"none",premultiplyAlpha:"none"});try{if(t.width*t.height>he)throw new Error("Pixel carrier dimensions exceed limit");let r=new OffscreenCanvas(t.width,t.height).getContext("2d",{willReadFrequently:!0});if(!r)throw new Error("Pixel carrier canvas unavailable");return r.drawImage(t,0,0),Ke(r.getImageData(0,0,t.width,t.height).data,t.width,t.height)}finally{t.close()}}var _e=[1e3,1500,2e3,2500],Ve=Array.from({length:16},(e,t)=>t&1?3:0),X=e=>[...e].map(t=>t.toString(16).padStart(2,"0")).join(""),G=async e=>X(new Uint8Array(await crypto.subtle.digest("SHA-256",e)));function ze(e){if(e.length<44||e.length>128*1024*1024)throw new Error("Invalid audio carrier size");let t=new DataView(e.buffer,e.byteOffset,e.byteLength),n=(f,d)=>String.fromCharCode(...e.subarray(f,f+d));if(n(0,4)!=="RIFF"||n(8,4)!=="WAVE")throw new Error("Expected PCM WAV");let r,o,s;for(let f=12;f+8<=e.length;){let d=t.getUint32(f+4,!0);if(f+8+d>e.length)throw new Error("Invalid WAV chunk");n(f,4)==="fmt "&&(r=f+8,o=d),n(f,4)==="data"&&(s=[f+8,d]),f+=8+d+(d&1)}if(!r||o<16||!s||t.getUint16(r,!0)!==1)throw new Error("Expected PCM WAV");let i=t.getUint16(r+2,!0),c=t.getUint32(r+4,!0),l=t.getUint16(r+14,!0);if(![1,2].includes(i)||![16e3,44100,48e3].includes(c)||![8,16].includes(l)||s[1]%(i*l/8))throw new Error("Unsupported PCM WAV");let a=new Float32Array(s[1]/(i*l/8));for(let f=0;f<a.length;f++){let d=0;for(let u=0;u<i;u++){let h=s[0]+(f*i+u)*l/8;d+=l===8?(e[h]-128)/128:t.getInt16(h,!0)/32768}a[f]=d/i}return{samples:a,rate:c}}function Ge(e){let t=Math.round(e*.001),n=Math.round(e*.009),r=n-t;return _e.map(o=>{let s=new Float32Array(r),i=new Float32Array(r);for(let c=0;c<r;c++){let l=2*Math.PI*o*c/e;s[c]=Math.cos(l),i[c]=Math.sin(l)}return{cos:s,sin:i,first:t,last:n}})}function ye(e,t,n){let r=-1,o=0;for(let s=0;s<4;s++){let i=n[s],c=t+i.first;if(c<0||t+i.last>=e.length)return-1;let l=0,a=0;for(let d=0;d<i.cos.length;d++)l+=e[c+d]*i.cos[d],a+=e[c+d]*i.sin[d];let f=l*l+a*a;f>r&&(r=f,o=s)}return o}function Xe(e,t,n,r){let o=t/100;for(let s of[0,-1,1,-2,2,-3,3]){let i=n+Math.round(s*t/1e3),c=0;for(let h=0;h<16;h++)ye(e,Math.round(i+h*o),r)===Ve[h]&&c++;if(c<14)continue;let l=new Uint8Array(54),a=(h,w)=>{let g=new Uint8Array(w);for(let y=0;y<w;y++)for(let m=0;m<4;m++){let E=ye(e,Math.round(i+(16+(h+y)*4+m)*o),r);if(E<0)return null;g[y]=g[y]<<2|E}return g};if(l.set(a(0,54)??new Uint8Array(54)),String.fromCharCode(...l.subarray(0,4))!=="AWA1")continue;let f=new DataView(l.buffer),d=f.getUint16(12,!0);if(d<1||d>128)continue;let u=a(54,d);if(u)return{total:f.getUint32(4,!0),count:f.getUint16(8,!0),index:f.getUint16(10,!0),expected:X(l.subarray(14,46)),check:X(l.subarray(46,54)),data:u}}return null}async function ge(e,t){if(!t||t.format!=="amw.audio-carrier.v1"||!Number.isInteger(t.length)||t.length<1||t.length>4096||!/^[0-9a-f]{64}$/.test(t.sha256)||!Array.isArray(t.packets)||t.packets.length<1||t.packets.length>32||!Array.isArray(t.locations)||t.locations.length!==t.packets.length*3)throw new Error("Invalid audio carrier manifest");let{samples:n,rate:r}=ze(e),o=Ge(r),s=new Map;for(let l of t.locations){if(!Number.isInteger(l.packet)||l.packet<0||l.packet>=t.packets.length||!Number.isFinite(l.startSeconds)||l.startSeconds<0)throw new Error("Invalid audio packet location");if(s.has(l.packet))continue;let a=Xe(n,r,Math.round(l.startSeconds*r),o);if(!a||a.index!==l.packet||a.total!==t.length||a.count!==t.packets.length||a.expected!==t.sha256)continue;let f=t.packets[a.index];!f||f.index!==a.index||f.length!==a.data.length||await G(a.data)!==f.sha256||(await G(a.data)).slice(0,16)!==a.check||s.set(a.index,a.data)}if(s.size!==t.packets.length)throw new Error("Audio carrier recovery incomplete");let i=new Uint8Array(t.length),c=0;for(let l=0;l<t.packets.length;l++)i.set(s.get(l),c),c+=s.get(l).length;if(c!==i.length||await G(i)!==t.sha256)throw new Error("Audio carrier integrity failure");return i}function me(e,t,n){let r=t*n;if(!Number.isSafeInteger(r)||t<256||n<128||r>16*1024*1024||e.length!==r*4)throw new Error("Invalid frame dimensions");let o=(f,d)=>{for(;d;)[f,d]=[d,f%d];return f},s=Math.floor(r*.61803398875)|1;for(;o(s,r)!==1;)s+=2;let i=((Math.imul(t,73856093)^Math.imul(n,19349663))>>>0)%r,c=new Uint32Array(768),l=f=>{let d=f*4;return(299*(e[d]&254)+587*(e[d+1]&254)+114*(e[d+2]&254))/1e3},a=f=>{let d=f%t,u=Math.floor(f/t),h=l(f),w=0;return d>0&&(w+=Math.abs(h-l(f-1))),d+1<t&&(w+=Math.abs(h-l(f+1))),u>0&&(w+=Math.abs(h-l(f-t))),u+1<n&&(w+=Math.abs(h-l(f+t))),w};for(let f=0,d=0;f<r&&d<c.length;){let u=-1,h=-1;for(let w=0;w<16&&f<r;w++,f++){let g=(i+f*s)%r;if(e[g*4+3]!==255)continue;let y=a(g);y>h&&(u=g,h=y)}if(u>=0&&(c[d++]=u),d===c.length)return c}throw new Error("Frame needs at least 768 opaque pixels")}var P=8,pe=e=>[...e].map(t=>t.toString(16).padStart(2,"0")).join("");async function Ze(e,t,n){if(!Number.isSafeInteger(t*n)||t<256||n<128||t*n>16*1024*1024||e.length!==t*n*4)throw new Error("Invalid luminance frame dimensions");let r=me(e,t,n),o=new Uint8Array(32);for(let c=0;c<256;c++){let l=0;for(let a=0;a<3;a++)l+=e[r[c*3+a]*4]&1;l>=2&&(o[c>>>3]|=1<<7-(c&7))}if(String.fromCharCode(...o.subarray(0,4))==="AWF2")return be(o,"AWF2");function s(c,l){let a=0;for(let f=0;f<P;f++)for(let d=0;d<P;d++){let u=((l+f)*t+c+d)*4;if(e[u+3]!==255)throw new Error("Frame data region must be opaque");a+=(e[u]*299+e[u+1]*587+e[u+2]*114)/1e3}return a/64}let i=new Uint8Array(32);for(let c=0;c<256;c++){let l=c%16*2*P,a=Math.floor(c/16)*P;s(l,a)>s(l+P,a)&&(i[c>>>3]|=1<<7-(c&7))}return be(i,"AWF1")}async function be(e,t){if(String.fromCharCode(...e.subarray(0,4))!==t||new DataView(e.buffer).getUint16(4,!0)!==0||e[6]<1||e[6]>21)throw new Error("Invalid luminance frame payload");let n=e.slice(11,11+e[6]),r=new Uint8Array(await crypto.subtle.digest("SHA-256",n));if(pe(r.slice(0,4))!==pe(e.slice(7,11)))throw new Error("Luminance frame integrity failure");return n}async function Ae(e){if(e.length>64*1024*1024)throw new Error("Frame carrier file is too large");let t=await createImageBitmap(new Blob([e],{type:"image/png"}),{colorSpaceConversion:"none",premultiplyAlpha:"none"});try{if(t.width*t.height>16*1024*1024)throw new Error("Frame carrier dimensions exceed limit");let r=new OffscreenCanvas(t.width,t.height).getContext("2d",{willReadFrequently:!0});if(!r)throw new Error("Frame carrier canvas unavailable");return r.drawImage(t,0,0),Ze(r.getImageData(0,0,t.width,t.height).data,t.width,t.height)}finally{t.close()}}var Qe=e=>[...e].map(t=>t.toString(16).padStart(2,"0")).join(""),L=async e=>Qe(new Uint8Array(await crypto.subtle.digest("SHA-256",e)));async function Ee(e,t,n,r=new Map){if(e.codec==="frame-luma1"||e.codec==="frame-luma2"){r.has(e.url)||r.set(e.url,(async()=>{let l=await t(e.url,{cache:"no-store",credentials:"omit",redirect:"error"});if(!l.ok||new URL(l.url,location.origin).origin!==location.origin||l.status!==200)throw new Error("Frame carrier fetch failed");return Ae(await b(l,64*1024*1024))})());let c=(await r.get(e.url)).slice(e.offset,e.offset+e.length);if(c.length!==e.length||await L(c)!==e.sha256)throw new Error("Shard integrity failure");return c}if(e.codec==="wav-4fsk1"){r.has(e.url)||r.set(e.url,(async()=>{let l=await t(e.url,{cache:"no-store",credentials:"omit",redirect:"error"});if(!l.ok||new URL(l.url,location.origin).origin!==location.origin||l.status!==200)throw new Error("Audio carrier fetch failed");return ge(await b(l,128*1024*1024),n.audioCarriers[e.url])})());let c=(await r.get(e.url)).slice(e.offset,e.offset+e.length);if(c.length!==e.length||await L(c)!==e.sha256)throw new Error("Shard integrity failure");return c}if(e.codec==="png-lsb1"){r.has(e.url)||r.set(e.url,(async()=>{let l=await t(e.url,{cache:"no-store",credentials:"omit",redirect:"error"});if(!l.ok||new URL(l.url,location.origin).origin!==location.origin||l.status!==200)throw new Error("Pixel carrier fetch failed");return we(await b(l,64*1024*1024))})());let c=(await r.get(e.url)).slice(e.offset,e.offset+e.length);if(c.length!==e.length||await L(c)!==e.sha256)throw new Error("Shard integrity failure");return c}let o=await t(e.url,{headers:{Range:`bytes=${e.offset}-${e.offset+e.length-1}`},cache:"no-store",credentials:"omit",redirect:"error"});if(!o.ok||new URL(o.url,location.origin).origin!==location.origin)throw new Error("Carrier fetch failed");if(o.status!==206&&o.status!==200)throw new Error("Carrier host must support byte ranges");let s=await b(o,o.status===206?e.length:16*1024*1024);if(o.status===206){let i=/^bytes (\d+)-(\d+)\/(\d+)$/.exec(o.headers.get("Content-Range")||"");if(!i||Number(i[1])!==e.offset||Number(i[2])!==e.offset+e.length-1||s.length!==e.length)throw new Error("Invalid carrier range")}else if(o.status===200&&s.length<=16*1024*1024)s=s.slice(e.offset,e.offset+e.length);else throw new Error("Carrier host must support byte ranges for large files");if(s.length!==e.length||await L(s)!==e.sha256)throw new Error("Shard integrity failure");return s}async function xe(e,t,n,r,o){try{let s=new Uint8Array(n.nonce.match(/../g).map(c=>parseInt(c,16))),i=new Uint8Array(await crypto.subtle.decrypt({name:"AES-GCM",iv:s,additionalData:de(e,t,n.plainLength),tagLength:128},o,r));if(i.length!==n.plainLength)throw new Error("Plaintext length mismatch");return i}catch{throw new Error("Content key or authentication failed")}}async function Ie(e){if(!e.encoding)return null;let t=new Uint8Array(e.encoding.key.match(/../g).map(n=>parseInt(n,16)));return crypto.subtle.importKey("raw",t,"AES-CTR",!1,["decrypt"])}async function ve(e,t,n,r){if(!r)return n;let o=new TextEncoder().encode(`AMW1-SITE-CTR\0${e.path}\0${t}`),s=new Uint8Array(await crypto.subtle.digest("SHA-256",o)).slice(0,16);return new Uint8Array(await crypto.subtle.decrypt({name:"AES-CTR",counter:s,length:128},r,n))}async function ke(e){if(!e)throw new Error("Content key required");return e instanceof Uint8Array?crypto.subtle.importKey("raw",e,"AES-GCM",!1,["decrypt"]):e}async function Z(e,t=fetch,n=null){if(k(e),!e.encryption)throw new Error("This app is not encrypted");let r;for(let c of e.resources)if(p(e,c))for(let[l,a]of c.parts.entries())(!r||a.length<r.part.length)&&(r={resource:c,index:l,part:a});let o=await ke(n),s=await Ie(e),i;try{i=await Ee(r.part,t,e)}catch(c){throw new Error(`${r.part.url}: ${c.message}`)}return await xe(r.resource,r.index,r.part,await ve(r.resource,r.index,i,s),o),!0}async function H(e,t,n=fetch,r=null,o=()=>{}){k(e);let s=e.resources.find(w=>w.path===t);if(!s)return null;let i=p(e,s),c=i?await ke(r):null,l=await Ie(e),a=new Array(s.parts.length),f=new Map,d=0;await Promise.all(Array.from({length:Math.min(4,a.length)},async()=>{for(;d<a.length;){let w=d++,g=s.parts[w],y;try{y=await Ee(g,n,e,f)}catch(m){throw new Error(`${g.url}: ${m.message}`)}y=await ve(s,w,y,l),i&&(y=await xe(s,w,g,y,c)),a[w]=y,o({path:s.path,index:w})}}));let u=new Uint8Array(s.length),h=0;for(let w of a)u.set(w,h),h+=w.length;if(h!==s.length||await L(u)!==s.sha256)throw new Error("Resource integrity failure");return{bytes:u,mime:s.mime}}var Ye="/amoosed.com/app/",ee={"Cache-Control":"no-store","X-Content-Type-Options":"nosniff"},Se=32*1024*1024,N,Q=0,x=new Map,W=0;self.addEventListener("install",e=>e.waitUntil(self.skipWaiting()));self.addEventListener("activate",e=>e.waitUntil(self.clients.claim()));async function Ue(){return k((await Ce()).webApp)}async function Ce(){(!N||Date.now()-Q>5e3)&&(Q=Date.now(),N=T("/favicon.ico").catch(async e=>{if(!/No AMW footer/.test(e.message))throw e;let t=await Pe();if(!t)throw new Error("An Amoosed index file is required");return typeof t=="string"?{privateIndex:B(t)}:F(t)}).then(_).then(e=>(k(e.webApp),e)));try{return await N}catch(e){throw N=void 0,e}}function $(){return new Promise((e,t)=>{let n=indexedDB.open("amoosedweb-keys-v1",2);n.onupgradeneeded=()=>{n.result.objectStoreNames.contains("keys")||n.result.createObjectStore("keys"),n.result.objectStoreNames.contains("indexes")||n.result.createObjectStore("indexes")},n.onsuccess=()=>e(n.result),n.onerror=()=>t(n.error)})}async function Pe(){let e=await $();try{return await new Promise((t,n)=>{let r=e.transaction("indexes","readonly").objectStore("indexes").get("current");r.onsuccess=()=>t(r.result||null),r.onerror=()=>n(r.error)})}finally{e.close()}}async function Je(e){let t=await $();try{await new Promise((n,r)=>{let o=t.transaction("indexes","readwrite");o.objectStore("indexes").put(e,"current"),o.oncomplete=n,o.onerror=()=>r(o.error),o.onabort=()=>r(o.error)})}finally{t.close()}}async function Y(e){let t=await $();try{return await new Promise((n,r)=>{let o=t.transaction("keys","readonly").objectStore("keys").get(e);o.onsuccess=()=>n(o.result||null),o.onerror=()=>r(o.error)})}finally{t.close()}}async function et(e,t){let n=await $();try{await new Promise((r,o)=>{let s=n.transaction("keys","readwrite");s.objectStore("keys").put(t,e),s.oncomplete=r,s.onerror=()=>o(s.error),s.onabort=()=>o(s.error)})}finally{n.close()}}async function J(e){let t=await $();try{await new Promise((n,r)=>{let o=t.transaction("keys","readwrite");o.objectStore("keys").delete(e),o.oncomplete=n,o.onerror=()=>r(o.error),o.onabort=()=>r(o.error)})}finally{t.close()}}function R(e,t,n,r={}){return new Response(e.method==="HEAD"?null:t,{status:n,headers:{...ee,"Content-Type":"text/plain; charset=utf-8",...r}})}function Me(e){return new Response(null,{status:302,headers:{...ee,Location:`/amoosed.com/boot.html?return=${encodeURIComponent(e.pathname+e.search)}`}})}function tt(e,t,n){if(p(e,t)||n.length>Se)return;let r=x.get(t.path);for(r&&(W-=r.bytes.length),x.delete(t.path);W+n.length>Se&&x.size;){let o=x.keys().next().value;W-=x.get(o).bytes.length,x.delete(o)}x.set(t.path,{sha256:t.sha256,bytes:n,mime:t.mime}),W+=n.length}self.addEventListener("message",e=>{let t=e.ports[0];t&&e.waitUntil((async()=>{try{let n=e.source&&new URL(e.source.url);if(!n||n.origin!==self.location.origin||n.pathname!=="/amoosed.com/boot.html"&&n.pathname!=="/amoosed.com/boot"&&!n.pathname.startsWith("/amoosed.com/app/"))throw new Error("Invalid key client");if(e.data?.type==="amw:getIndex"){let o=await Ce(),s;try{await T("/favicon.ico")}catch(i){if(!/No AMW footer/.test(i.message))throw i;s=await Pe()}t.postMessage({document:o,...typeof s=="string"?{indexToken:s}:s?{indexBytes:s}:{}});return}if(e.data?.type==="amw:installIndex"||e.data?.type==="amw:installIndexToken"){if(n.pathname!=="/amoosed.com/boot.html"&&n.pathname!=="/amoosed.com/boot")throw new Error("Invalid index client");let o=e.data.type==="amw:installIndexToken",s=o?e.data.token:e.data.bytes;if(o)B(s);else if(!(s instanceof Uint8Array)||s.length<128||s.length>16*1024*1024)throw new Error("Invalid index file");try{throw await T("/favicon.ico"),new Error("This site already publishes an Amoosed index")}catch(a){if(!/No AMW footer/.test(a.message))throw a}let i=o?{privateIndex:B(s)}:await F(s);if(!i.privateIndex)throw new Error("Imported favicon has no private index locator");let c=await _(i),l=k(c.webApp);if(!l.identity)throw new Error("Imported index needs an app identity");await H(l,l.identity.icon,fetch),await Je(s),N=Promise.resolve(c),Q=Date.now(),x.clear(),W=0,t.postMessage({ready:!0});return}let r=await Ue();if(e.data?.type==="amw:getCarrierMap"){if(!n.pathname.startsWith("/amoosed.com/app/"))throw new Error("Carrier map requires an Amoosed app page");t.postMessage({resources:r.resources.map(o=>{let s=new Map;for(let i of o.parts){let c=s.get(i.url)||{url:i.url,chunks:0};c.chunks++,s.set(i.url,c)}return{path:o.path,bytes:o.length,mime:o.mime,protected:p(r,o),chunks:o.parts.length,carriers:[...s.values()]}})});return}if(e.data?.type==="amw:prepare"){let o=r.resources.find(h=>h.path===r.entry),s=p(r,o),i=s?await Y(r.encryption.keyId):null;if(s&&!i){t.postMessage({keyRequired:!0});return}let c=r.resources.filter(h=>h.delivery!=="download"&&(!p(r,h)||h.path===r.entry)),l=c.reduce((h,w)=>h+w.parts.length,0),a=0;t.postMessage({progress:{completed:a,total:l}});let f=0,u=(await Promise.allSettled(Array.from({length:Math.min(2,c.length)},async()=>{for(;f<c.length;){let h=c[f++],w;try{w=await H(r,h.path,fetch,p(r,h)?i:null,g=>t.postMessage({progress:{completed:++a,total:l,...g}}))}catch(g){throw new Error(`${h.path}: ${g.message}`)}tt(r,h,w.bytes)}}))).find(h=>h.status==="rejected");if(u){let h=u.reason;if(s&&h.message===`${r.entry}: Content key or authentication failed`){await J(r.encryption.keyId),t.postMessage({keyRequired:!0});return}throw h}t.postMessage({ready:!0});return}if(!r.encryption)throw new Error("This app is not encrypted");if(e.data?.type==="amw:hasKey"){let o=await Y(r.encryption.keyId);if(!o){t.postMessage({ready:!1,state:"missing"});return}try{await Z(r,fetch,o),t.postMessage({ready:!0,state:"valid"})}catch(s){s.message==="Content key or authentication failed"?t.postMessage({ready:!1,state:"invalid"}):t.postMessage({error:`Cannot verify encrypted carrier: ${s.message}`})}}else if(e.data?.type==="amw:forgetKey")await J(r.encryption.keyId),t.postMessage({ready:!1});else if(e.data?.type==="amw:setKey"&&e.data.key instanceof Uint8Array&&e.data.key.length===32){let o=await crypto.subtle.importKey("raw",e.data.key,"AES-GCM",!1,["decrypt"]);await Z(r,fetch,o),await et(r.encryption.keyId,o),t.postMessage({ready:!0})}else throw new Error("Invalid key message")}catch(n){t.postMessage({error:n.message})}})())});self.addEventListener("fetch",e=>{let t=new URL(e.request.url);if(!(t.origin!==self.location.origin||!t.pathname.startsWith(Ye))){if(e.request.method!=="GET"&&e.request.method!=="HEAD"){e.respondWith(Promise.resolve(R(e.request,"Method not allowed",405,{Allow:"GET, HEAD"})));return}e.respondWith((async()=>{try{let n=await Ue(),r=n.resources.some(a=>a.path===t.pathname)?t.pathname:e.request.mode==="navigate"?n.entry:null;if(!r)return R(e.request,"Not found",404);let o=n.resources.find(a=>a.path===r),s=p(n,o),i=s?await Y(n.encryption.keyId):null;if(s&&!i)return e.request.mode==="navigate"?Me(t):R(e.request,"Content key required",403);let c;try{let a=s?null:x.get(r);c=a?.sha256===o.sha256&&a.mime===o.mime?a:await H(n,r,fetch,i)}catch(a){if(!s||a.message!=="Content key or authentication failed")throw a;return await J(n.encryption.keyId),e.request.mode==="navigate"?Me(t):R(e.request,"Content key required",403)}let l={...ee,"Content-Type":c.mime,"Content-Length":String(c.bytes.length)};return o.delivery==="download"&&(l["Content-Disposition"]=`attachment; filename="${o.path.split("/").at(-1)}"`),new Response(e.request.method==="HEAD"?null:c.bytes,{headers:l})}catch(n){return R(e.request,`AmoosedWeb verification failed: ${n.message}`,502)}})())}});
|