import assets from './generated.js'; import {apiRoute} from './api.js'; import {projectFor} from './projects.js'; import {glyph,icon} from './icons.js'; export const escapeHtml=value=>String(value).replaceAll('&','&').replaceAll('<','<').replaceAll('>','>').replaceAll('"','"').replaceAll("'",'''); export const json=(body,status=200)=>Response.json(body,{status,headers:{'Cache-Control':'no-store','X-Content-Type-Options':'nosniff'}}); export function navigation(project){const name=escapeHtml(project.name);const isAccount=project.name==='account';return `skip to content`;} const offline=project=>`${escapeHtml(project.name)} offline${navigation(project)}

you are offline.

reconnect to use ${escapeHtml(project.name)} and sign in securely.

try again
`; export function manifest(project){return {id:'/',name:project.name,short_name:project.name,description:project.description,start_url:project.start||'/',scope:'/',display:'standalone',background_color:'#ffffff',theme_color:'#000000',icons:[{src:`/_dm/icon-192.png?v=${assets.release}`,sizes:'192x192',type:'image/png',purpose:'any maskable'},{src:`/_dm/icon-512.png?v=${assets.release}`,sizes:'512x512',type:'image/png',purpose:'any maskable'}]};} export function platformRoute(request,project,release=assets.release,shell=[]){ const path=new URL(request.url).pathname;let body,type; if(path==='/_dm/platform.css'){body=assets['platform.css'];type='text/css; charset=utf-8';} else if(path==='/_dm/platform.js'){body=assets['platform.js'];type='text/javascript; charset=utf-8';} else if(['/manifest.webmanifest','/site.webmanifest','/_dm/manifest.webmanifest'].includes(path)){body=JSON.stringify(manifest(project));type='application/manifest+json';} else if(path==='/sw.js'){body=assets.serviceWorker.replaceAll('__PROJECT__',project.name).replaceAll('__RELEASE__',release).replace('__SHELL__',JSON.stringify(project.offline?shell:[])).replace('__OFFLINE__',JSON.stringify(!!project.offline));type='text/javascript; charset=utf-8';} else if(path==='/_dm/offline'){body=offline(project);type='text/html; charset=utf-8';} else if(path==='/icon.svg'||path==='/_dm/icon.svg'){body=icon(project.name);type='image/svg+xml';} else if(/^\/_dm\/icon-(180|192|512)\.png$/.test(path)||/^\/(icon-(192|512)\.png|apple-touch-icon\.png)$/.test(path)){const size=path.includes('180')||path.includes('apple-touch')?180:path.includes('512')?512:192;body=Uint8Array.from(atob(assets.icons[project.name][size]),c=>c.charCodeAt(0));type='image/png';} else return null; if(!['GET','HEAD'].includes(request.method))return new Response('method not allowed',{status:405,headers:{Allow:'GET, HEAD'}}); return new Response(request.method==='HEAD'?null:body,{headers:{'Content-Type':type,'Cache-Control':'no-cache','X-Content-Type-Options':'nosniff',...(path==='/sw.js'?{'Service-Worker-Allowed':'/'}:{})}}); } export function present(request,response,project){ const url=new URL(request.url);if(project.name==='proxy'&&url.pathname!=='/api/docs'&&(url.pathname!=='/'||url.searchParams.has('url')))return response; if(response.headers.has('X-DM-Platform')||request.method!=='GET'||response.status!==200||!response.headers.get('Content-Type')?.includes('text/html'))return response; const headers=new Headers(response.headers);for(const key of ['Content-Length','ETag','Content-MD5'])headers.delete(key); // Public offline shells contain no identity; authenticated pages must never enter a cache. headers.set('X-DM-Platform','1');headers.set('Cache-Control',project.offline?'no-cache':'private, no-store');headers.set('X-Content-Type-Options','nosniff');headers.set('Referrer-Policy','no-referrer'); return new HTMLRewriter() .on('head',{element(el){el.append(``,{html:true});}}) .on('link[rel="manifest"], link[rel="icon"], link[rel="apple-touch-icon"]',{element(el){el.remove();}}) .on('meta[name="theme-color"]',{element(el){el.remove();}}) .on('body',{element(el){el.setAttribute('data-dm-project',project.name);el.prepend(navigation(project),{html:true});}}) .on('main',{element(el){if(!el.getAttribute('id'))el.setAttribute('id','dm-content');}}) .transform(new Response(response.body,{status:response.status,headers})); } export function standardize(worker,options={}){return {...worker,async fetch(request,env,ctx){const project=options.project||projectFor(request);if(!project)return worker.fetch(request,env,ctx);const url=new URL(request.url);if(project.hostname&&url.hostname==='www.'+project.hostname){url.hostname=project.hostname;return new Response(null,{status:308,headers:{Location:url.href}});}const api=apiRoute(request,project);if(api)return present(request,api,project);const route=platformRoute(request,project,options.release,options.shell);if(route)return route;return present(request,await worker.fetch(request,env,ctx),project);}};} export function withAssets(worker,files,release,project){ const serve=request=>{const path=new URL(request.url).pathname;const file=files[path==='/'?'/index.html':path];if(!file)return new Response('not found',{status:404});if(!['GET','HEAD'].includes(request.method))return new Response('method not allowed',{status:405});return new Response(request.method==='HEAD'?null:Uint8Array.from(atob(file.body),c=>c.charCodeAt(0)),{headers:{'Content-Type':file.type,'Cache-Control':'no-cache','X-Content-Type-Options':'nosniff'}});}; return standardize({...worker,fetch(request,env,ctx){const path=new URL(request.url).pathname;if(files[path==='/'?'/index.html':path])return serve(request);return worker.fetch(request,{...env,ASSETS:{fetch:serve}},ctx);}},{project,release,shell:['/',...(project.shell||[]),...Object.keys(files).filter(p=>p!=='/index.html'&&!p.endsWith('.map')&&!p.endsWith('sw.js'))]}); }