const TEMPLATES = {
login: async () => 'Hi, Logged in user!',
homeStore: async () => 'Your local store is open until 18:00 today',
}
class ReplacerStream extends TransformStream {
constructor(substitutions) {
super({
transform: async(chunk, controller) => {
chunk = this.buffer + this.textDecoder.decode(chunk);
for (const [key, tmplFn] of Object.entries(this.substitutions)) {
const target = '{{' + key + '}}';
while (chunk.includes(target)) {
const pos = chunk.indexOf(target);
chunk = chunk.substring(0,pos) + (await tmplFn()) + chunk.substring(pos + target.length);
}
}
this.buffer = chunk.substr(0 - this.bufferLen);
controller.enqueue(
this.textEncoder.encode(
chunk.substr(0, chunk.length - this.buffer.length)
)
);
}
});
this.substitutions = substitutions;
this.textEncoder = new TextEncoder();
this.textDecoder = new TextDecoder();
this.buffer = '';
this.bufferLen = Object.keys(substitutions).reduce((maxLen, k) => Math.max(maxLen, k.length), 0) + 4;
}
}
async function handleRequest(req) {
const templateEngine = new ReplacerStream(TEMPLATES);
const beResp = await fetch(req, { backend: 'origin_0' });
let resp = beResp;
const isHTML = (beResp.headers.get('content-type') ?? '').startsWith('text/html');
if (isHTML && beResp.body) {
const newBody = beResp.body.pipeThrough(templateEngine);
resp = new Response(newBody, {
headers: { ...beResp.headers, 'cache-control': 'no-store, private' }
});
}
return resp;
}
addEventListener('fetch', ev => ev.respondWith(handleRequest(ev.request)));