@webqit/node-live-response 0.1.0 → 0.1.2
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 +21 -12
- package/package.json +4 -3
- package/src/index.js +10 -12
- package/test/index1.html +23 -0
- package/test/index2.html +22 -0
- package/test/index3.html +34 -0
- package/test/server1.js +35 -0
- package/test/server2.js +36 -0
- package/test/server3.js +32 -0
- package/test/server.js +0 -10
- /package/test/{index.html → main.test.js} +0 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This package brings **LiveResponse** to traditional Node.js and Express backends.
|
|
4
4
|
|
|
5
|
-
LiveResponse is a new response model that extends the HTTP request/response model with interactivity. It allows you to send a response and keep it open for interaction.
|
|
5
|
+
LiveResponse is a new response model that extends the HTTP request/response model with interactivity. It allows you to send a response and keep it open for interaction. More details in the [LiveResponse docs](https://github.com/webqit/fetch-plus?tab=readme-ov-file#section-1-liveresponse).
|
|
6
6
|
|
|
7
7
|
The definitive way to get full, always‑on interactivity as a core architectural primitive is **[Webflo](https://github.com/webqit/webflo)**. Live responses are native and automatic there. This package exists for cases where you want LiveResponse inside an otherwise conventional Node.js or Express backend.
|
|
8
8
|
|
|
@@ -107,12 +107,12 @@ app.get('/counter', liveMode(), async (req, res) => {
|
|
|
107
107
|
|
|
108
108
|
const interval = setInterval(() => {
|
|
109
109
|
Observer.set(state, 'count', state.count + 1);
|
|
110
|
-
},
|
|
110
|
+
}, 1_000);
|
|
111
111
|
|
|
112
112
|
setTimeout(() => {
|
|
113
113
|
clearInterval(interval);
|
|
114
114
|
res.die();
|
|
115
|
-
},
|
|
115
|
+
}, 60_000);
|
|
116
116
|
});
|
|
117
117
|
```
|
|
118
118
|
|
|
@@ -147,17 +147,20 @@ A live response can be replaced with a new one – without opening a new request
|
|
|
147
147
|
|
|
148
148
|
```js
|
|
149
149
|
app.get('/news', liveMode(), async (req, res) => {
|
|
150
|
-
const liveRes = new LiveResponse({ headline: 'Breaking: Hello World' });
|
|
150
|
+
const liveRes = new LiveResponse({ headline: 'Breaking: Hello World' }, { done: false });
|
|
151
151
|
await res.send(liveRes); // resolves when live mode is established
|
|
152
152
|
|
|
153
153
|
setTimeout(() => {
|
|
154
|
-
liveRes.replaceWith({ headline: 'Update: Still Hello World' });
|
|
155
|
-
},
|
|
154
|
+
liveRes.replaceWith({ headline: 'Update: Still Hello World' }, { done: false });
|
|
155
|
+
}, 3_000);
|
|
156
156
|
|
|
157
157
|
setTimeout(() => {
|
|
158
158
|
liveRes.replaceWith({ headline: 'Final: Goodbye' });
|
|
159
|
+
}, 6_000);
|
|
160
|
+
|
|
161
|
+
setTimeout(() => {
|
|
159
162
|
res.die();
|
|
160
|
-
},
|
|
163
|
+
}, 60_000);
|
|
161
164
|
});
|
|
162
165
|
```
|
|
163
166
|
|
|
@@ -170,13 +173,15 @@ Then on the client:
|
|
|
170
173
|
<script src="https://unpkg.com/@webqit/fetch-plus/dist/main.js"></script>
|
|
171
174
|
</head>
|
|
172
175
|
<body>
|
|
176
|
+
|
|
173
177
|
<h1></h1>
|
|
178
|
+
|
|
174
179
|
<script type="module">
|
|
175
180
|
const { LiveResponse } = window.webqit;
|
|
176
181
|
|
|
177
182
|
const liveRes = LiveResponse.from(fetch('/news'));
|
|
178
|
-
liveRes.addEventListener('
|
|
179
|
-
document.querySelector('h1').textContent = e.body.headline;
|
|
183
|
+
liveRes.addEventListener('replace', (e) => {
|
|
184
|
+
document.querySelector('h1').textContent = e.data.body.headline;
|
|
180
185
|
});
|
|
181
186
|
</script>
|
|
182
187
|
</body>
|
|
@@ -194,6 +199,10 @@ app.get('/chat', liveMode(), async (req, res) => {
|
|
|
194
199
|
req.port.addEventListener('message', (e) => {
|
|
195
200
|
req.port.postMessage(e.data);
|
|
196
201
|
});
|
|
202
|
+
|
|
203
|
+
setTimeout(() => {
|
|
204
|
+
res.die();
|
|
205
|
+
}, 60_000);
|
|
197
206
|
});
|
|
198
207
|
```
|
|
199
208
|
|
|
@@ -214,8 +223,8 @@ Then on the client:
|
|
|
214
223
|
<script type="module">
|
|
215
224
|
const { LiveResponse } = window.webqit;
|
|
216
225
|
|
|
217
|
-
const
|
|
218
|
-
|
|
226
|
+
const { port } = await LiveResponse.from(fetch('/chat')).now();
|
|
227
|
+
port.addEventListener('message', (e) => {
|
|
219
228
|
const li = document.createElement('li');
|
|
220
229
|
li.textContent = e.data;
|
|
221
230
|
log.appendChild(li);
|
|
@@ -224,7 +233,7 @@ Then on the client:
|
|
|
224
233
|
const msg = document.querySelector('#msg');
|
|
225
234
|
msg.addEventListener('keydown', (e) => {
|
|
226
235
|
if (e.key === 'Enter') {
|
|
227
|
-
|
|
236
|
+
port.postMessage(msg.value);
|
|
228
237
|
msg.value = '';
|
|
229
238
|
}
|
|
230
239
|
});
|
package/package.json
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"realtime",
|
|
16
16
|
"webqit"
|
|
17
17
|
],
|
|
18
|
-
"version": "0.1.
|
|
18
|
+
"version": "0.1.2",
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"repository": {
|
|
21
21
|
"type": "git",
|
|
@@ -36,8 +36,9 @@
|
|
|
36
36
|
"version:next": "npm version prerelease --preid=next"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@webqit/fetch-plus": "
|
|
40
|
-
"@webqit/port-plus": "^0.1.
|
|
39
|
+
"@webqit/fetch-plus": "^0.1.11",
|
|
40
|
+
"@webqit/port-plus": "^0.1.15",
|
|
41
|
+
"ws": "^8.19.0"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
44
|
"chai": "^4.3.4",
|
package/src/index.js
CHANGED
|
@@ -29,7 +29,7 @@ export function enableLive(server) {
|
|
|
29
29
|
export const portRegistry = new Map();
|
|
30
30
|
|
|
31
31
|
export function setupLiveRoute(req, res) {
|
|
32
|
-
const port = new StarPort();
|
|
32
|
+
const port = new StarPort({ handshake: 1, postAwaitsOpen: true, autoClose: true });
|
|
33
33
|
const portId = crypto.randomUUID();
|
|
34
34
|
|
|
35
35
|
portRegistry.set(portId, port);
|
|
@@ -92,16 +92,14 @@ export function setupLiveRoute(req, res) {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
// ---- intercept Express-style response exits ----
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
95
|
+
res.send = (value) => {
|
|
96
|
+
if (value instanceof LiveResponse) {
|
|
97
|
+
commitLiveResponse(value);
|
|
98
|
+
return req.port.readyStateChange('open').then(() => res);
|
|
99
|
+
}
|
|
100
|
+
return (originalSend || originalEnd)(value);
|
|
101
|
+
};
|
|
102
|
+
|
|
105
103
|
res.end = (...args) => {
|
|
106
104
|
// Only end live mode if no LiveResponse was ever committed
|
|
107
105
|
if (!hasLiveResponse) {
|
|
@@ -124,7 +122,7 @@ export function handleUpgrade(server) {
|
|
|
124
122
|
}
|
|
125
123
|
|
|
126
124
|
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
127
|
-
const wsPort = new WebSocketPort(ws);
|
|
125
|
+
const wsPort = new WebSocketPort(ws, { handshake: 1, postAwaitsOpen: true });
|
|
128
126
|
portRegistry.get(portId).addPort(wsPort);
|
|
129
127
|
});
|
|
130
128
|
});
|
package/test/index1.html
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
|
|
3
|
+
<head>
|
|
4
|
+
<title>Live Counter</title>
|
|
5
|
+
<script src="https://unpkg.com/@webqit/fetch-plus/dist/main.js"></script>
|
|
6
|
+
</head>
|
|
7
|
+
|
|
8
|
+
<body>
|
|
9
|
+
|
|
10
|
+
<h1></h1>
|
|
11
|
+
|
|
12
|
+
<script type="module">
|
|
13
|
+
const { LiveResponse, Observer } = window.webqit;
|
|
14
|
+
|
|
15
|
+
const { body: state } = await LiveResponse.from(fetch('/counter')).now();
|
|
16
|
+
|
|
17
|
+
Observer.observe(state, () => {
|
|
18
|
+
document.querySelector('h1').textContent = 'Count: ' + state.count;
|
|
19
|
+
});
|
|
20
|
+
</script>
|
|
21
|
+
</body>
|
|
22
|
+
|
|
23
|
+
</html>
|
package/test/index2.html
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
|
|
3
|
+
<head>
|
|
4
|
+
<title>Live News</title>
|
|
5
|
+
<script src="https://unpkg.com/@webqit/fetch-plus/dist/main.js"></script>
|
|
6
|
+
</head>
|
|
7
|
+
|
|
8
|
+
<body>
|
|
9
|
+
|
|
10
|
+
<h1></h1>
|
|
11
|
+
|
|
12
|
+
<script type="module">
|
|
13
|
+
const { LiveResponse } = window.webqit;
|
|
14
|
+
|
|
15
|
+
const liveRes = LiveResponse.from(fetch('/news'));
|
|
16
|
+
liveRes.addEventListener('replace', (e) => {
|
|
17
|
+
document.querySelector('h1').textContent = e.data.body.headline;
|
|
18
|
+
});
|
|
19
|
+
</script>
|
|
20
|
+
</body>
|
|
21
|
+
|
|
22
|
+
</html>
|
package/test/index3.html
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
|
|
3
|
+
<head>
|
|
4
|
+
<title>Live Chat</title>
|
|
5
|
+
<script src="https://unpkg.com/@webqit/fetch-plus/dist/main.js"></script>
|
|
6
|
+
</head>
|
|
7
|
+
|
|
8
|
+
<body>
|
|
9
|
+
|
|
10
|
+
<h1>Chat</h1>
|
|
11
|
+
<ul id="log"></ul>
|
|
12
|
+
<input id="msg" placeholder="Type and press enter" />
|
|
13
|
+
|
|
14
|
+
<script type="module">
|
|
15
|
+
const { LiveResponse } = window.webqit;
|
|
16
|
+
|
|
17
|
+
const { port } = await LiveResponse.from(fetch('/chat')).now();
|
|
18
|
+
port.addEventListener('message', (e) => {
|
|
19
|
+
const li = document.createElement('li');
|
|
20
|
+
li.textContent = e.data;
|
|
21
|
+
log.appendChild(li);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const msg = document.querySelector('#msg');
|
|
25
|
+
msg.addEventListener('keydown', (e) => {
|
|
26
|
+
if (e.key === 'Enter') {
|
|
27
|
+
port.postMessage(msg.value);
|
|
28
|
+
msg.value = '';
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
</script>
|
|
32
|
+
</body>
|
|
33
|
+
|
|
34
|
+
</html>
|
package/test/server1.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import http from 'http';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { enableLive, Observer } from '../src/index.js';
|
|
4
|
+
import { LiveResponse } from '@webqit/fetch-plus';
|
|
5
|
+
|
|
6
|
+
const server = http.createServer(handler);
|
|
7
|
+
const liveMode = enableLive(server);
|
|
8
|
+
|
|
9
|
+
server.listen(3000);
|
|
10
|
+
console.log('Server started on port 3000');
|
|
11
|
+
|
|
12
|
+
// ------------
|
|
13
|
+
|
|
14
|
+
async function handler(req, res) {
|
|
15
|
+
// --------------------------
|
|
16
|
+
if (req.url !== '/counter') {
|
|
17
|
+
return fs.createReadStream('./test/index1.html').pipe(res);
|
|
18
|
+
}
|
|
19
|
+
liveMode(req, res);
|
|
20
|
+
// --------------------------
|
|
21
|
+
|
|
22
|
+
const state = { count: 0 };
|
|
23
|
+
|
|
24
|
+
const liveRes = new LiveResponse(state);
|
|
25
|
+
await res.send(liveRes); // resolves when live mode is established
|
|
26
|
+
|
|
27
|
+
const interval = setInterval(() => {
|
|
28
|
+
Observer.set(state, 'count', state.count + 1);
|
|
29
|
+
}, 1_000);
|
|
30
|
+
|
|
31
|
+
setTimeout(() => {
|
|
32
|
+
clearInterval(interval);
|
|
33
|
+
res.die();
|
|
34
|
+
}, 60_000);
|
|
35
|
+
}
|
package/test/server2.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import http from 'http';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { enableLive, Observer } from '../src/index.js';
|
|
4
|
+
import { LiveResponse } from '@webqit/fetch-plus';
|
|
5
|
+
|
|
6
|
+
const server = http.createServer(handler);
|
|
7
|
+
const liveMode = enableLive(server);
|
|
8
|
+
|
|
9
|
+
server.listen(3000);
|
|
10
|
+
console.log('Server started on port 3000');
|
|
11
|
+
|
|
12
|
+
// ------------
|
|
13
|
+
|
|
14
|
+
async function handler(req, res) {
|
|
15
|
+
// --------------------------
|
|
16
|
+
if (req.url !== '/news') {
|
|
17
|
+
return fs.createReadStream('./test/index2.html').pipe(res);
|
|
18
|
+
}
|
|
19
|
+
liveMode(req, res);
|
|
20
|
+
// --------------------------
|
|
21
|
+
|
|
22
|
+
const liveRes = new LiveResponse({ headline: 'Breaking: Hello World' }, { done: false });
|
|
23
|
+
await res.send(liveRes); // resolves when live mode is established
|
|
24
|
+
|
|
25
|
+
setTimeout(() => {
|
|
26
|
+
liveRes.replaceWith({ headline: 'Update: Still Hello World' }, { done: false });
|
|
27
|
+
}, 3_000);
|
|
28
|
+
|
|
29
|
+
setTimeout(() => {
|
|
30
|
+
liveRes.replaceWith({ headline: 'Final: Goodbye' });
|
|
31
|
+
}, 6_000);
|
|
32
|
+
|
|
33
|
+
setTimeout(() => {
|
|
34
|
+
res.die();
|
|
35
|
+
}, 60_000);
|
|
36
|
+
}
|
package/test/server3.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import http from 'http';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { enableLive, Observer } from '../src/index.js';
|
|
4
|
+
import { LiveResponse } from '@webqit/fetch-plus';
|
|
5
|
+
|
|
6
|
+
const server = http.createServer(handler);
|
|
7
|
+
const liveMode = enableLive(server);
|
|
8
|
+
|
|
9
|
+
server.listen(3000);
|
|
10
|
+
console.log('Server started on port 3000');
|
|
11
|
+
|
|
12
|
+
// ------------
|
|
13
|
+
|
|
14
|
+
async function handler(req, res) {
|
|
15
|
+
// --------------------------
|
|
16
|
+
if (req.url !== '/chat') {
|
|
17
|
+
return fs.createReadStream('./test/index3.html').pipe(res);
|
|
18
|
+
}
|
|
19
|
+
liveMode(req, res);
|
|
20
|
+
// --------------------------
|
|
21
|
+
|
|
22
|
+
const liveRes = new LiveResponse({ title: 'Chat' });
|
|
23
|
+
await res.send(liveRes); // resolves when live mode is established
|
|
24
|
+
|
|
25
|
+
req.port.addEventListener('message', (e) => {
|
|
26
|
+
req.port.postMessage(e.data);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
setTimeout(() => {
|
|
30
|
+
res.die();
|
|
31
|
+
}, 60_000);
|
|
32
|
+
}
|
package/test/server.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { enableLive } from '../src/index.js';
|
|
2
|
-
import { LiveResponse } from '@webqit/fetch-plus';
|
|
3
|
-
|
|
4
|
-
const server = http.createServer((req, res) => {
|
|
5
|
-
live(req, res);
|
|
6
|
-
res.send(new LiveResponse('Hello world'));
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
const live = enableLive(server);
|
|
10
|
-
server.listen(3000);
|
|
File without changes
|