@webqit/node-live-response 0.1.7 → 0.1.9

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
@@ -1,10 +1,15 @@
1
1
  # LiveResponse for Node.js & Express
2
2
 
3
- This package brings **LiveResponse** to traditional Node.js and Express backends.
3
+ This package brings **LiveResponse** to traditional Node.js and Express servers.
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. More details in the [LiveResponse docs](https://github.com/webqit/fetch-plus?tab=readme-ov-file#section-1-liveresponse).
5
+ LiveResponse extends the HTTP request/response model with **persistent interactivity**. You send a response and keep it open as a live communication channel.
6
6
 
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.
7
+ Instead of closing after delivery, the response becomes interactive.
8
+
9
+ For the full conceptual model, see the [LiveResponse docs](https://github.com/webqit/fetch-plus?tab=readme-ov-file#section-1-liveresponse).
10
+
11
+ If you’re building a system where live interactivity is a first-class architectural primitive, use **[Webflo](https://github.com/webqit/webflo)**. Live responses are native there.
12
+ This package exists for cases where you want LiveResponse inside an otherwise conventional Node.js or Express backend.
8
13
 
9
14
  ---
10
15
 
@@ -18,7 +23,7 @@ npm install @webqit/node-live-response
18
23
 
19
24
  ## One-Time Setup
20
25
 
21
- The first thing you do is enable live mode against your server instance. This sets up the transport and request bookkeeping required for LiveResponse.
26
+ The first thing you do is enable live mode on your HTTP server instance. This installs the transport layer and request bookkeeping required for live sessions.
22
27
 
23
28
  ### Node.js HTTP server
24
29
 
@@ -48,9 +53,12 @@ const liveMode = enableLive(server);
48
53
 
49
54
  ## Usage
50
55
 
51
- The returned `liveMode` function is your per-route live mode switch. Call it on a route where you want live mode enabled.
56
+ The returned `liveMode` function enables live mode per route.
57
+
58
+ It works both as:
52
59
 
53
- This function works as both a direct function and an Express middleware.
60
+ * a direct function (Node HTTP)
61
+ * an Express middleware
54
62
 
55
63
  ### Node.js HTTP server
56
64
 
@@ -59,12 +67,12 @@ async function handler(req, res) {
59
67
  liveMode(req, res);
60
68
 
61
69
  const liveRes = new LiveResponse('Hello world');
62
- await res.send(liveRes); // resolves when live mode is established
70
+ await res.send(liveRes); // resolves when the live connection is established
63
71
 
64
- // ------- interactive phase -------
72
+ // ---- interactive phase ----
65
73
 
66
74
  setTimeout(() => {
67
- res.die();
75
+ res.die(); // explicitly end live mode
68
76
  }, 5000);
69
77
  }
70
78
  ```
@@ -74,9 +82,9 @@ async function handler(req, res) {
74
82
  ```js
75
83
  app.get('/counter', liveMode(), async (req, res) => {
76
84
  const liveRes = new LiveResponse('Hello world');
77
- await res.send(liveRes); // resolves when live mode is established
85
+ await res.send(liveRes); // resolves when the live connection is established
78
86
 
79
- // ------- interactive phase -------
87
+ // ---- interactive phase ----
80
88
 
81
89
  setTimeout(() => {
82
90
  res.die();
@@ -86,15 +94,21 @@ app.get('/counter', liveMode(), async (req, res) => {
86
94
 
87
95
  ---
88
96
 
89
- ## Live interaction patterns
97
+ ## Interaction Patterns
90
98
 
91
99
  LiveResponse supports three core interaction patterns.
92
100
 
101
+ ---
102
+
93
103
  ### 1. Live state projection
94
104
 
95
- A live object can be sent as the response body.
105
+ Send a mutable object as the response body.
106
+
107
+ Mutations on the server automatically propagate to the client.
96
108
 
97
- Mutating that object on the server automatically reflects on the client. (More in the [LiveResponse docs](https://github.com/webqit/fetch-plus#1-live-state-projection-via-mutable-response-bodies))
109
+ (More in the [LiveResponse docs](https://github.com/webqit/fetch-plus#1-live-state-projection-via-mutable-response-bodies))
110
+
111
+ On the server:
98
112
 
99
113
  ```js
100
114
  import { Observer } from '@webqit/observer';
@@ -103,7 +117,7 @@ app.get('/counter', liveMode(), async (req, res) => {
103
117
  const state = { count: 0 };
104
118
 
105
119
  const liveRes = new LiveResponse(state);
106
- await res.send(liveRes); // resolves when live mode is established
120
+ await res.send(liveRes);
107
121
 
108
122
  const interval = setInterval(() => {
109
123
  Observer.set(state, 'count', state.count + 1);
@@ -116,7 +130,7 @@ app.get('/counter', liveMode(), async (req, res) => {
116
130
  });
117
131
  ```
118
132
 
119
- Then on the client:
133
+ On the client:
120
134
 
121
135
  ```html
122
136
  <!doctype html>
@@ -141,17 +155,41 @@ Then on the client:
141
155
  </html>
142
156
  ```
143
157
 
158
+ > [!TIP]
159
+ > This example can be previewed live by running:
160
+ >
161
+ > ```
162
+ > cd node-live-response
163
+ > node test/server1.js
164
+ > ```
165
+ > _Open localhost:3000 to view_
166
+
167
+ ---
168
+
144
169
  ### 2. Response swapping
145
170
 
146
- A live response can be replaced with a new one without opening a new request. It gives you a multi-response architecture. (More in the [LiveResponse docs](https://github.com/webqit/fetch-plus?tab=readme-ov-file#2-a-multi-response-architecture-via-response-swaps))
171
+ Replace the current response with a new one without issuing a new HTTP request.
172
+
173
+ This enables a multi-response architecture over a single request.
174
+
175
+ (More in the [LiveResponse docs](https://github.com/webqit/fetch-plus?tab=readme-ov-file#2-a-multi-response-architecture-via-response-swaps))
176
+
177
+ On the server:
147
178
 
148
179
  ```js
149
180
  app.get('/news', liveMode(), async (req, res) => {
150
- const liveRes = new LiveResponse({ headline: 'Breaking: Hello World' }, { done: false });
151
- await res.send(liveRes); // resolves when live mode is established
181
+ const liveRes = new LiveResponse(
182
+ { headline: 'Breaking: Hello World' },
183
+ { done: false }
184
+ );
185
+
186
+ await res.send(liveRes);
152
187
 
153
188
  setTimeout(() => {
154
- liveRes.replaceWith({ headline: 'Update: Still Hello World' }, { done: false });
189
+ liveRes.replaceWith(
190
+ { headline: 'Update: Still Hello World' },
191
+ { done: false }
192
+ );
155
193
  }, 3_000);
156
194
 
157
195
  setTimeout(() => {
@@ -164,7 +202,7 @@ app.get('/news', liveMode(), async (req, res) => {
164
202
  });
165
203
  ```
166
204
 
167
- Then on the client:
205
+ On the client:
168
206
 
169
207
  ```html
170
208
  <!doctype html>
@@ -187,14 +225,29 @@ Then on the client:
187
225
  </body>
188
226
  ```
189
227
 
228
+ > [!TIP]
229
+ > This example can be previewed live by running:
230
+ >
231
+ > ```
232
+ > cd node-live-response
233
+ > node test/server2.js
234
+ > ```
235
+ > _Open localhost:3000 to view_
236
+
237
+ ---
238
+
190
239
  ### 3. Bidirectional messaging
191
240
 
192
- Live responses can exchange messages bidirectionally. (More in the [LiveResponse docs](https://github.com/webqit/fetch-plus?tab=readme-ov-file#3-bidirectional-messaging-via-message-ports))
241
+ Exchange messages between client and server through a message port.
242
+
243
+ (More in the [LiveResponse docs](https://github.com/webqit/fetch-plus?tab=readme-ov-file#3-bidirectional-messaging-via-message-ports))
244
+
245
+ On the server:
193
246
 
194
247
  ```js
195
248
  app.get('/chat', liveMode(), async (req, res) => {
196
249
  const liveRes = new LiveResponse({ title: 'Chat' });
197
- await res.send(liveRes); // resolves when live mode is established
250
+ await res.send(liveRes);
198
251
 
199
252
  req.port.addEventListener('message', (e) => {
200
253
  req.port.postMessage(e.data);
@@ -206,7 +259,7 @@ app.get('/chat', liveMode(), async (req, res) => {
206
259
  });
207
260
  ```
208
261
 
209
- Then on the client:
262
+ On the client:
210
263
 
211
264
  ```html
212
265
  <!doctype html>
@@ -241,48 +294,77 @@ Then on the client:
241
294
  </body>
242
295
  ```
243
296
 
297
+ > [!TIP]
298
+ > This example can be previewed live by running:
299
+ >
300
+ > ```
301
+ > cd node-live-response
302
+ > node test/server3.js
303
+ > ```
304
+ > _Open localhost:3000 to view_
305
+
244
306
  ---
245
307
 
246
- ## What `node-live-response` does
308
+ ## What This Library Adds
247
309
 
248
- The library:
310
+ `@webqit/node-live-response` augments the standard Node/Express request lifecycle:
249
311
 
250
- * attaches `req.port` and `req.signal` to the request object.
251
- * patches `res.send()` / `res.end()` to accept `LiveResponse`. Note that calling res.send() with a LiveResponse gives you back a promise that resolves when the connection is live. This promise is to be awaited before interacting with the live response.
252
- * adds `res.die()` to the response object. This must be called to end interactivity.
312
+ * Adds `req.port` for bidirectional messaging
313
+ * Adds `req.signal` for live session lifecycle tracking
314
+ * Patches `res.send()` / `res.end()` to accept `LiveResponse`
315
+ * Adds `res.die()` to explicitly terminate live interaction
253
316
 
254
317
  ---
255
318
 
256
- ## Lifecycle contract
319
+ ## Lifecycle Contract
320
+
321
+ Live interaction has its own lifecycle, separate from the HTTP lifecycle.
257
322
 
258
- ### When interactivity starts
323
+ ### Interactivity starts
259
324
 
260
- Interactivity begins **after** the server sends a `LiveResponse`:
325
+ Interactivity begins when you send a `LiveResponse`:
261
326
 
262
327
  ```js
263
- res.send(liveRes);
328
+ await res.send(liveRes);
264
329
  ```
265
330
 
266
331
  That is the moment the client learns that the response is interactive and joins the live channel.
267
332
 
268
- * Live state projection and `replaceWith()` are only meaningful *after* this moment
269
- * Messages sent on `req.port` *before* this moment are queued
333
+ The `send()` method, this time, returns promise that resolves when the client has joined the live channel.
270
334
 
271
- (Contrast this with Webflo, where interaction is implicit and automatic.)
335
+ You may await this promise where necessary, but messages or response swaps issued before the connection is fully established are automatically queued and flushed once live mode becomes active.
272
336
 
273
- ### When interactivity ends
337
+ The transition to an "open" connection may also be observed via:
274
338
 
275
- Live mode ends only when you explicitly call `res.die()`. This must be called to end interactivity:
339
+ ```js
340
+ await req.port.readyStateChange('open');
341
+ ```
342
+
343
+ ---
344
+
345
+ ### Interactivity ends
346
+
347
+ Live interaction ends when the LiveResponse port on the client side is closed or when you explicitly call:
276
348
 
277
349
  ```js
278
350
  res.die();
279
351
  ```
280
352
 
281
- Ending the HTTP response does **not** end live interaction. The request lifecycle and the live lifecycle are not coupled.
353
+ on the server. This method aborts the request lifecycle signal exposed at `req.signal`.
354
+
355
+ Note that ending the HTTP response does **not** end the live session.
356
+
357
+ The HTTP lifecycle and the live lifecycle are independent.
358
+
359
+ The transition to a "closed" connection may be observed via:
360
+
361
+ ```js
362
+ await req.port.readyStateChange('close');
363
+ ```
282
364
 
283
365
  ---
284
366
 
285
- ## Learn more
367
+ ## Learn More
286
368
 
287
369
  * [LiveResponse docs](https://github.com/webqit/fetch-plus#1-live-state-projection-via-mutable-response-bodies)
288
370
  * [Webflo](https://github.com/webqit/webflo)
package/package.json CHANGED
@@ -15,7 +15,7 @@
15
15
  "realtime",
16
16
  "webqit"
17
17
  ],
18
- "version": "0.1.7",
18
+ "version": "0.1.9",
19
19
  "license": "MIT",
20
20
  "repository": {
21
21
  "type": "git",
@@ -36,10 +36,8 @@
36
36
  "version:next": "npm version prerelease --preid=next"
37
37
  },
38
38
  "dependencies": {
39
- "@webqit/fetch-plus": "^0.1.21",
40
- "@webqit/port-plus": "^0.1.19",
41
- "i": "^0.3.7",
42
- "npm": "^11.7.0",
39
+ "@webqit/fetch-plus": "^0.1.29",
40
+ "@webqit/port-plus": "^0.1.22",
43
41
  "ws": "^8.19.0"
44
42
  },
45
43
  "devDependencies": {
package/test/server1.js CHANGED
@@ -22,7 +22,7 @@ async function handler(req, res) {
22
22
  const state = { count: 0 };
23
23
 
24
24
  const liveRes = new LiveResponse(state);
25
- await res.send(liveRes); // resolves when live mode is established
25
+ res.send(liveRes); // resolves when live mode is established
26
26
 
27
27
  const interval = setInterval(() => {
28
28
  Observer.set(state, 'count', state.count + 1);