MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 181 lines 5.0 kB view raw
1const http = require('node:http'); 2 3class ReactPromise { 4 constructor(status, value, reason) { 5 this.status = status; 6 this.value = value; 7 this.reason = reason; 8 } 9} 10 11function createPendingChunk() { 12 return new ReactPromise('pending', null, null); 13} 14 15function createResolvedModelChunk(response, value) { 16 return new ReactPromise('resolved_model', value, response); 17} 18 19function resolveModelChunk(response, chunk, value) { 20 if ('pending' !== chunk.status) { 21 chunk.reason.enqueueModel(value); 22 return; 23 } 24 chunk.status = 'resolved_model'; 25 chunk.value = value; 26 chunk.reason = response; 27} 28 29function startAsyncIterable(response, id) { 30 const seen = []; 31 const controller = { 32 enqueueModel(value) { 33 seen.push(value); 34 }, 35 close(value) { 36 seen.push(`close:${value}`); 37 }, 38 error(error) { 39 seen.push(`error:${error && error.message ? error.message : String(error)}`); 40 } 41 }; 42 response.controllers.set(id, { seen, controller }); 43 response._chunks.set(id, new ReactPromise('fulfilled', {}, controller)); 44} 45 46function processFullBinaryRow(response, id, tag, buffer, chunk) { 47 let row = ''; 48 const decoder = new TextDecoder(); 49 for (let i = 0; i < buffer.length; i++) row += decoder.decode(buffer[i], { stream: true }); 50 row += decoder.decode(chunk); 51 52 switch (tag) { 53 case 120: 54 startAsyncIterable(response, id); 55 break; 56 case 67: { 57 const resolved = response._chunks.get(id); 58 if (resolved && resolved.status === 'fulfilled') resolved.reason.close(row === '' ? '"$undefined"' : row); 59 break; 60 } 61 default: { 62 const existing = response._chunks.get(id); 63 if (existing) { 64 resolveModelChunk(response, existing, row); 65 } else { 66 response._chunks.set(id, createResolvedModelChunk(response, row)); 67 } 68 break; 69 } 70 } 71} 72 73async function parseResponseBody(body) { 74 const reader = body.getReader(); 75 const response = { 76 _chunks: new Map(), 77 controllers: new Map() 78 }; 79 80 let rowState = 0; 81 let rowID = 0; 82 let rowTag = 0; 83 let rowLength = 0; 84 const buffer = []; 85 86 while (true) { 87 const { value, done } = await reader.read(); 88 if (done) break; 89 90 let i = 0; 91 while (i < value.length) { 92 let lastIdx = -1; 93 switch (rowState) { 94 case 0: { 95 const ch = value[i++]; 96 if (ch === 58) rowState = 1; 97 else rowID = (rowID << 4) | (ch > 96 ? ch - 87 : ch - 48); 98 continue; 99 } 100 case 1: { 101 rowState = value[i]; 102 if ( 103 rowState === 84 || rowState === 65 || rowState === 79 || rowState === 111 || 104 rowState === 85 || rowState === 83 || rowState === 115 || rowState === 76 || 105 rowState === 108 || rowState === 71 || rowState === 103 || rowState === 77 || 106 rowState === 109 || rowState === 86 107 ) { 108 rowTag = rowState; 109 rowState = 2; 110 i++; 111 } else if ( 112 (rowState > 64 && rowState < 91) || rowState === 35 || rowState === 114 || rowState === 120 113 ) { 114 rowTag = rowState; 115 rowState = 3; 116 i++; 117 } else { 118 rowTag = 0; 119 rowState = 3; 120 } 121 continue; 122 } 123 case 2: { 124 const ch = value[i++]; 125 if (ch === 44) rowState = 4; 126 else rowLength = (rowLength << 4) | (ch > 96 ? ch - 87 : ch - 48); 127 continue; 128 } 129 case 3: 130 lastIdx = value.indexOf(10, i); 131 break; 132 case 4: 133 lastIdx = i + rowLength; 134 if (lastIdx > value.length) lastIdx = -1; 135 break; 136 } 137 138 const offset = value.byteOffset + i; 139 if (lastIdx > -1) { 140 const rowChunk = new Uint8Array(value.buffer, offset, lastIdx - i); 141 processFullBinaryRow(response, rowID, rowTag, buffer, rowChunk); 142 i = lastIdx; 143 if (rowState === 3) i++; 144 rowLength = 0; 145 rowID = 0; 146 rowTag = 0; 147 rowState = 0; 148 buffer.length = 0; 149 } else { 150 const partial = new Uint8Array(value.buffer, offset, value.byteLength - i); 151 buffer.push(partial); 152 rowLength -= partial.byteLength; 153 break; 154 } 155 } 156 } 157 158 return response; 159} 160 161const server = http.createServer((_req, res) => { 162 res.writeHead(200, { 'content-type': 'application/octet-stream' }); 163 res.write('1:x\n1:"a'); 164 res.write('"\n1:"b"\n'); 165 res.end(); 166}); 167 168server.listen(0, async () => { 169 const { port } = server.address(); 170 try { 171 const res = await fetch(`http://127.0.0.1:${port}/`); 172 const parsed = await parseResponseBody(res.body); 173 const state = parsed.controllers.get(1); 174 console.log(`controllerType:${typeof (state && state.controller)}`); 175 console.log(`seen:${state ? state.seen.join(',') : 'missing'}`); 176 } catch (error) { 177 console.log(`ERR:${error && error.stack ? error.stack : String(error)}`); 178 } finally { 179 server.close(); 180 } 181});