@webqit/node-live-response 0.1.7 → 0.1.8

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,27 @@ 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))
193
244
 
194
245
  ```js
195
246
  app.get('/chat', liveMode(), async (req, res) => {
196
247
  const liveRes = new LiveResponse({ title: 'Chat' });
197
- await res.send(liveRes); // resolves when live mode is established
248
+ await res.send(liveRes);
198
249
 
199
250
  req.port.addEventListener('message', (e) => {
200
251
  req.port.postMessage(e.data);
@@ -206,7 +257,7 @@ app.get('/chat', liveMode(), async (req, res) => {
206
257
  });
207
258
  ```
208
259
 
209
- Then on the client:
260
+ On the client:
210
261
 
211
262
  ```html
212
263
  <!doctype html>
@@ -241,48 +292,77 @@ Then on the client:
241
292
  </body>
242
293
  ```
243
294
 
295
+ > [!TIP]
296
+ > This example can be previewed live by running:
297
+ >
298
+ > ```
299
+ > cd node-live-response
300
+ > node test/server3.js
301
+ > ```
302
+ > _Open localhost:3000 to view_
303
+
244
304
  ---
245
305
 
246
- ## What `node-live-response` does
306
+ ## What This Library Adds
247
307
 
248
- The library:
308
+ `@webqit/node-live-response` augments the standard Node/Express request lifecycle:
249
309
 
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.
310
+ * Adds `req.port` for bidirectional messaging
311
+ * Adds `req.signal` for live session lifecycle tracking
312
+ * Patches `res.send()` / `res.end()` to accept `LiveResponse`
313
+ * Adds `res.die()` to explicitly terminate live interaction
253
314
 
254
315
  ---
255
316
 
256
- ## Lifecycle contract
317
+ ## Lifecycle Contract
318
+
319
+ Live interaction has its own lifecycle, separate from the HTTP lifecycle.
257
320
 
258
- ### When interactivity starts
321
+ ### Interactivity starts
259
322
 
260
- Interactivity begins **after** the server sends a `LiveResponse`:
323
+ Interactivity begins when you send a `LiveResponse`:
261
324
 
262
325
  ```js
263
- res.send(liveRes);
326
+ await res.send(liveRes);
264
327
  ```
265
328
 
266
329
  That is the moment the client learns that the response is interactive and joins the live channel.
267
330
 
268
- * Live state projection and `replaceWith()` are only meaningful *after* this moment
269
- * Messages sent on `req.port` *before* this moment are queued
331
+ The `send()` method, this time, returns promise that resolves when the client has joined the live channel.
332
+
333
+ 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.
334
+
335
+ The transition to an "open" connection may also be observed via:
336
+
337
+ ```js
338
+ await req.port.readyStateChange('open');
339
+ ```
270
340
 
271
- (Contrast this with Webflo, where interaction is implicit and automatic.)
341
+ ---
272
342
 
273
- ### When interactivity ends
343
+ ### Interactivity ends
274
344
 
275
- Live mode ends only when you explicitly call `res.die()`. This must be called to end interactivity:
345
+ Live interaction ends when the LiveResponse port on the client side is closed or when you explicitly call:
276
346
 
277
347
  ```js
278
348
  res.die();
279
349
  ```
280
350
 
281
- Ending the HTTP response does **not** end live interaction. The request lifecycle and the live lifecycle are not coupled.
351
+ on the server. This method aborts the request lifecycle signal exposed at `req.signal`.
352
+
353
+ Note that ending the HTTP response does **not** end the live session.
354
+
355
+ The HTTP lifecycle and the live lifecycle are independent.
356
+
357
+ The transition to a "closed" connection may be observed via:
358
+
359
+ ```js
360
+ await req.port.readyStateChange('close');
361
+ ```
282
362
 
283
363
  ---
284
364
 
285
- ## Learn more
365
+ ## Learn More
286
366
 
287
367
  * [LiveResponse docs](https://github.com/webqit/fetch-plus#1-live-state-projection-via-mutable-response-bodies)
288
368
  * [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.8",
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);