@webqit/node-live-response 0.1.0 → 0.1.1

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 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. You want to see the [LiveResponse docs](https://github.com/webqit/fetch-plus?tab=readme-ov-file#section-1-liveresponse) for more details.
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
- }, 1000);
110
+ }, 1_000);
111
111
 
112
112
  setTimeout(() => {
113
113
  clearInterval(interval);
114
114
  res.die();
115
- }, 10000);
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
- }, 3000);
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
- }, 6000);
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('response', (e) => {
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 liveRes = LiveResponse.from(fetch('/chat'));
218
- liveRes.port.addEventListener('message', (e) => {
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
- liveRes.port.postMessage(msg.value);
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.0",
18
+ "version": "0.1.1",
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": "file:../fetch-plus",
40
- "@webqit/port-plus": "^0.1.12"
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
- if (originalSend) {
96
- res.send = (value) => {
97
- if (value instanceof LiveResponse) {
98
- commitLiveResponse(value);
99
- return req.port.readyStateChange('open').then(() => res);
100
- }
101
- return originalSend(value);
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
  });
@@ -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>
@@ -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>
@@ -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>
@@ -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 !== '/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
+ console.log('_________________________', state.count);
29
+ Observer.set(state, 'count', state.count + 1);
30
+ }, 1_000);
31
+
32
+ setTimeout(() => {
33
+ clearInterval(interval);
34
+ res.die();
35
+ }, 60_000);
36
+ }
@@ -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
+ }
@@ -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