MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
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});