@webqit/webflo 0.20.12-next.0 → 0.20.13-next.0
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/package.json +1 -1
- package/site/docs/concepts/realtime.md +33 -15
- package/site/docs/concepts/routing.md +18 -18
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@ Webflo apps work this way by default.
|
|
|
8
8
|
They're powered by a rich set of APIs designed for every use case:
|
|
9
9
|
|
|
10
10
|
```js
|
|
11
|
-
return new LiveResponse(
|
|
11
|
+
return new LiveResponse({ progress: 10 }, { done: false });
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
```js
|
|
@@ -16,6 +16,7 @@ event.respondWith({ progress: 10 }, { done: false });
|
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
```js
|
|
19
|
+
event.waitUntil(promise);
|
|
19
20
|
event.waitUntilNavigate();
|
|
20
21
|
```
|
|
21
22
|
|
|
@@ -153,7 +154,7 @@ Typical Port B clients in Webflo are:
|
|
|
153
154
|
|
|
154
155
|
::: info LiveResponse
|
|
155
156
|
`LiveResponse` is the "live" version of the standard `Response` API.
|
|
156
|
-
It extends the native `Response` object with a live body, headers, and status
|
|
157
|
+
It extends the native `Response` object to support mutable response – with a live body, headers, and status that can be mutated in-place.
|
|
157
158
|
:::
|
|
158
159
|
|
|
159
160
|
#### The App UI
|
|
@@ -192,8 +193,13 @@ The background connection comes along with the upstream response obtained via `n
|
|
|
192
193
|
export default async function (event, next) {
|
|
193
194
|
if (next.stepname) return await next(); // The conventional delegation line
|
|
194
195
|
|
|
196
|
+
// Since next.stepname is falsey, this next() call falls through to the server. Response is thus a normal HTTP response from the server
|
|
195
197
|
const response = await next();
|
|
198
|
+
|
|
199
|
+
// LiveResponse.from() converts the response to a live response using the connection details embedded in the original response
|
|
196
200
|
const liveResponse = await LiveResponse.from(response);
|
|
201
|
+
|
|
202
|
+
// The background port is then used to listen to messages from the upstream
|
|
197
203
|
liveResponse.background.addEventListener("message", (message) => {
|
|
198
204
|
console.log(message);
|
|
199
205
|
});
|
|
@@ -209,13 +215,13 @@ The handler’s own `event.client` port remains its own _Port A_ for communicati
|
|
|
209
215
|
---
|
|
210
216
|
|
|
211
217
|
::: tip
|
|
212
|
-
While not explicitly mentioned, external Webflo servers are just as accessible and interactive as the local
|
|
218
|
+
While not explicitly mentioned, external Webflo servers – like Webflo-based API backends – are just as accessible and interactive as the local Webflo instance. A server-to-server request, for example, supports the same live interaction as a normal client-server request. It is just a matter of `await LiveResponse.from(await fetch('https://api.example.com/data'))`.
|
|
213
219
|
:::
|
|
214
220
|
|
|
215
221
|
## Entering Background Mode
|
|
216
222
|
|
|
217
223
|
A handler **enters background mode** when it responds to a request interactively as any of the below scenarios.
|
|
218
|
-
Webflo automatically upgrades the connection to a realtime connection — initiating the _handshake sequence_ with the client and keeping the communication open until _termination_ is triggered — as in [the Realtime Lifecycle](#appendix-a-–-the-realtime-lifecycle).
|
|
224
|
+
Webflo automatically upgrades the connection to a realtime connection — initiating the _handshake sequence_ with the client and keeping the communication open until _termination_ is triggered — as detailed in [the Realtime Lifecycle](#appendix-a-–-the-realtime-lifecycle).
|
|
219
225
|
|
|
220
226
|
### _Handler sends a message at any point in its lifecycle_
|
|
221
227
|
|
|
@@ -227,12 +233,14 @@ A handler may send a message at any point in its lifecycle by either:
|
|
|
227
233
|
event.client.postMessage({ progress: 10 });
|
|
228
234
|
```
|
|
229
235
|
|
|
230
|
-
2. or
|
|
236
|
+
2. or doing the equivalent via higher-level APIs like `event.user.confirm()`:
|
|
231
237
|
|
|
232
238
|
```js
|
|
233
239
|
const result = await event.user.confirm({ message: "Are you sure?" });
|
|
234
240
|
```
|
|
235
241
|
|
|
242
|
+
The handler automatically enters background mode.
|
|
243
|
+
|
|
236
244
|
### _Handler returns a response and explicitly marks it **not done**_
|
|
237
245
|
|
|
238
246
|
A handler may return a response and explicitly mark it **not done** by calling `event.respondWith()` (or returning `LiveResponse`) with `{ done: false }`:
|
|
@@ -245,6 +253,8 @@ event.respondWith(data, { done: false });
|
|
|
245
253
|
return new LiveResponse(data, { done: false });
|
|
246
254
|
```
|
|
247
255
|
|
|
256
|
+
The handler automatically enters background mode.
|
|
257
|
+
|
|
248
258
|
### _Handler holds down the event lifecycle via promises_
|
|
249
259
|
|
|
250
260
|
A handler may hold down the event lifecycle via promises by either:
|
|
@@ -252,12 +262,12 @@ A handler may hold down the event lifecycle via promises by either:
|
|
|
252
262
|
1. explicitly calling `event.waitUntil()` or `event.waitUntilNavigate()` before returning a response:
|
|
253
263
|
|
|
254
264
|
```js
|
|
255
|
-
event.
|
|
265
|
+
event.waitUntil(new Promise(() => {}));
|
|
256
266
|
event.respondWith(data);
|
|
257
267
|
```
|
|
258
268
|
|
|
259
269
|
```js
|
|
260
|
-
event.
|
|
270
|
+
event.waitUntilNavigate();
|
|
261
271
|
return data;
|
|
262
272
|
```
|
|
263
273
|
|
|
@@ -284,6 +294,8 @@ event.respondWith(data, async ($data /* reactive copy of data */) => {
|
|
|
284
294
|
});
|
|
285
295
|
```
|
|
286
296
|
|
|
297
|
+
The handler automatically enters background mode.
|
|
298
|
+
|
|
287
299
|
### _Handler returns a `Generator` object_
|
|
288
300
|
|
|
289
301
|
A handler may return a `Generator` object by either:
|
|
@@ -310,12 +322,16 @@ export default async function (event) {
|
|
|
310
322
|
}
|
|
311
323
|
```
|
|
312
324
|
|
|
313
|
-
|
|
325
|
+
The handler automatically enters background mode.
|
|
314
326
|
|
|
315
|
-
|
|
327
|
+
### _Handler returns a `LiveProgramHandle` object_
|
|
328
|
+
|
|
329
|
+
A handler may return a `LiveProgramHandle` object by being a ["live" function](https://github.com/webqit/use-live):
|
|
316
330
|
|
|
317
331
|
```js
|
|
318
|
-
export default async
|
|
332
|
+
export default async function(event) {
|
|
333
|
+
"use live";
|
|
334
|
+
|
|
319
335
|
const data = { progress: 0 };
|
|
320
336
|
|
|
321
337
|
setInterval(() => {
|
|
@@ -326,7 +342,7 @@ export default async live function(event) {
|
|
|
326
342
|
}
|
|
327
343
|
```
|
|
328
344
|
|
|
329
|
-
The
|
|
345
|
+
The handler automatically enters background mode.
|
|
330
346
|
|
|
331
347
|
---
|
|
332
348
|
|
|
@@ -359,7 +375,7 @@ import { LiveResponse } from '@webqit/webflo/apis';
|
|
|
359
375
|
export async function GET(event, next) {
|
|
360
376
|
if (next.stepname) return await next(); // The conventional delegation line
|
|
361
377
|
|
|
362
|
-
const res =
|
|
378
|
+
const res = new LiveResponse({ step: 1 }, { done: false });
|
|
363
379
|
setTimeout(() => res.replaceWith({ step: 2 }), 200); // [!code highlight]
|
|
364
380
|
|
|
365
381
|
return res;
|
|
@@ -368,7 +384,7 @@ export async function GET(event, next) {
|
|
|
368
384
|
|
|
369
385
|
#### Example — Mutate state in place
|
|
370
386
|
|
|
371
|
-
This handler returns a skeleton object immediately for fast page load and then builds the object tree progressively as data
|
|
387
|
+
This handler returns a skeleton object immediately for fast page load and then builds the object tree progressively as it fetches data from the relevant sources; the UI reflects every step of the object's construction.
|
|
372
388
|
|
|
373
389
|
**_Result_:** The UI renders a skeleton first, then progressively fills in as the object tree is built from the server.
|
|
374
390
|
|
|
@@ -483,7 +499,7 @@ export async function DELETE(event, next) {
|
|
|
483
499
|
|
|
484
500
|
The Webflo UI client shows the dialog (native `confirm()/prompt()` or custom UI if configured) and returns the user's response.
|
|
485
501
|
|
|
486
|
-
|
|
502
|
+
The prompt UI that the user sees can be customized in as simple as intercepting these via `app.background.addEventListener("prompt", ...)` to render your own modals.
|
|
487
503
|
|
|
488
504
|
```javascript
|
|
489
505
|
// Intercept at window.webqit.app.background
|
|
@@ -504,6 +520,8 @@ These handlers establish a chat channel and broadcast messages to all participan
|
|
|
504
520
|
|
|
505
521
|
**_Result_:** Messages appear in the chat trail every few seconds; incoming vs outgoing messages are styled differently.
|
|
506
522
|
|
|
523
|
+
On the server:
|
|
524
|
+
|
|
507
525
|
```js
|
|
508
526
|
export async function GET(event, next) {
|
|
509
527
|
if (next.stepname) return await next(); // The conventional delegation line
|
|
@@ -524,7 +542,7 @@ export async function GET(event, next) {
|
|
|
524
542
|
}
|
|
525
543
|
```
|
|
526
544
|
|
|
527
|
-
|
|
545
|
+
On the client:
|
|
528
546
|
|
|
529
547
|
```js
|
|
530
548
|
export default async function (event, next) {
|
|
@@ -73,7 +73,7 @@ Use these for conditional delegation, or per-segment rules.
|
|
|
73
73
|
### Your First Handler (Again)
|
|
74
74
|
|
|
75
75
|
Your application's routes may be designed with as many or as few handlers as desired.<br>
|
|
76
|
-
In fact, if it calls for it, the contextual parameters `next.stepname` and `next.pathname`
|
|
76
|
+
In fact, if it calls for it, it is possible to fit routing logic for multiple routes into a single handler – using the contextual parameters `next.stepname` and `next.pathname` for conditional branching.
|
|
77
77
|
|
|
78
78
|
```js
|
|
79
79
|
export default function(event, next) {
|
|
@@ -142,7 +142,7 @@ export default async function () {
|
|
|
142
142
|
```
|
|
143
143
|
|
|
144
144
|
* The request enters at the top level (`app/handler.server.js`).
|
|
145
|
-
* Each handler
|
|
145
|
+
* Each handler performs logic and either return a response or call `next()`.
|
|
146
146
|
* `next()` advances the request to the next directory level in the URL.
|
|
147
147
|
* Delegation stops when there are no further segments (`next.stepname` is falsy).
|
|
148
148
|
|
|
@@ -152,7 +152,7 @@ export default async function () {
|
|
|
152
152
|
Beyond the default parent-child flow, a handler can explicitly **reroute** a request to another path within the app by calling `next({ redirect: path })`, or `next(path)`, for short.
|
|
153
153
|
|
|
154
154
|
This is simulated below for a URL like `/products/stickers`.<br>
|
|
155
|
-
Here, the root handler conditionally reroutes the request to `/api/inventory`,
|
|
155
|
+
Here, the root handler conditionally reroutes the request to `/api/inventory`, as an internal call.
|
|
156
156
|
|
|
157
157
|
```js
|
|
158
158
|
// app/handler.server.js
|
|
@@ -236,9 +236,9 @@ Through this mechanism, Webflo lets handlers **reshape requests or responses inl
|
|
|
236
236
|
|
|
237
237
|
## The Client-Server Flow
|
|
238
238
|
|
|
239
|
-
In addition to the handler-to-handler model, Webflo also has the **client-server flow**
|
|
239
|
+
In addition to the handler-to-handler model, Webflo also has the **client-server flow** as the request travels through the application stack – from the client to the server.
|
|
240
240
|
|
|
241
|
-
Webflo
|
|
241
|
+
Webflo lets you do routing at all three layers in this flow: in the browser window (**client**), in the service worker (**worker**), and on the server (**server**).
|
|
242
242
|
|
|
243
243
|
Handlers fit into this stack by their filename suffix:
|
|
244
244
|
|
|
@@ -251,7 +251,7 @@ handler.js → Executes anywhere (default handler)
|
|
|
251
251
|
|
|
252
252
|
Together, these form a vertical routing pipeline.
|
|
253
253
|
|
|
254
|
-
Below is a conceptual diagram of how a navigation request flows
|
|
254
|
+
Below is a conceptual diagram of how a navigation request flows down the routing layers:
|
|
255
255
|
|
|
256
256
|
```js
|
|
257
257
|
┌─────────────┐ │ ┌─────────────────────────────────┐
|
|
@@ -265,17 +265,16 @@ Below is a conceptual diagram of how a navigation request flows donw the layers:
|
|
|
265
265
|
▼ └─────────────────────────────────┘
|
|
266
266
|
```
|
|
267
267
|
|
|
268
|
-
|
|
269
|
-
if defined. Otherwise, the request continues to the next layer
|
|
268
|
+
Routing in any of these layers is optional; if a layer-specific handler does not exist, Webflo checks for the unsuffixed one `handler.js`,
|
|
269
|
+
if defined. Otherwise, the request continues to the next layer until reaching the server.
|
|
270
270
|
|
|
271
|
-
|
|
272
|
-
Handlers at higher layers (like the browser) are able to respond instantly or hand the request down the stack.
|
|
271
|
+
Handlers at higher routing layers (like the browser) are able to respond instantly or hand the request down the stack.
|
|
273
272
|
|
|
274
273
|
This model grants profound flexibility — enabling progressive enhancement, offline support, and universal routing, all through the same `next()` interface.
|
|
275
274
|
|
|
276
275
|
### Layer Semantics
|
|
277
276
|
|
|
278
|
-
These layers are differentiated by
|
|
277
|
+
These routing layers are differentiated by their scope and use cases.
|
|
279
278
|
|
|
280
279
|
| Scope | Purpose | Typical Usage |
|
|
281
280
|
| :--------------------- | :--------------------------------------------- | :------------------------------------------ |
|
|
@@ -286,7 +285,7 @@ These layers are differentiated by various factors and use cases.
|
|
|
286
285
|
|
|
287
286
|
#### Client-Side Handlers
|
|
288
287
|
|
|
289
|
-
Client handlers intercept navigations directly in the browser — the first layer that sees user-initiated requests.
|
|
288
|
+
Client-side route handlers intercept user navigations directly in the browser — the first layer that sees user-initiated requests.
|
|
290
289
|
|
|
291
290
|
```js
|
|
292
291
|
// app/handler.client.js
|
|
@@ -305,13 +304,13 @@ export default async function (event, next) {
|
|
|
305
304
|
* Optionally calls `next()` to delegate the request
|
|
306
305
|
|
|
307
306
|
::: info Handler Lifecycle
|
|
308
|
-
- Client handlers begin their lifecycle *after* the initial page load.
|
|
307
|
+
- Client-side handlers begin their lifecycle *after* the initial page load.
|
|
309
308
|
- They therefore cannot intercept the first page load or page reloads.
|
|
310
309
|
:::
|
|
311
310
|
|
|
312
311
|
#### Worker-Side Handlers
|
|
313
312
|
|
|
314
|
-
Worker handlers run in the Service Worker context, bridging offline and network behavior.
|
|
313
|
+
Worker-side route handlers run in the Service Worker context, bridging offline and network behavior.
|
|
315
314
|
They are the connective tissue between local interactivity and remote resources.
|
|
316
315
|
|
|
317
316
|
```js
|
|
@@ -335,14 +334,14 @@ export default async function (event, next) {
|
|
|
335
334
|
* Can delegates to the server when offline handling isn’t possible
|
|
336
335
|
|
|
337
336
|
::: info Handler Lifecycle
|
|
338
|
-
- Worker handlers start intercepting once the app’s Service Worker is installed and activated.
|
|
337
|
+
- Worker-side handlers start intercepting once the app’s Service Worker is installed and activated.
|
|
339
338
|
- They therefore cannot intercept the very first page load that installs the app.
|
|
340
339
|
- They continue working even when the page isn’t open, making them ideal for offline logic.
|
|
341
340
|
:::
|
|
342
341
|
|
|
343
342
|
#### Server-Side Handlers
|
|
344
343
|
|
|
345
|
-
Server handlers perform the heavy lifting — database queries, integrations, etc.
|
|
344
|
+
Server-side route handlers perform the heavy lifting — database queries, integrations, etc.
|
|
346
345
|
They represent the final dynamic layer before static content resolution.
|
|
347
346
|
|
|
348
347
|
```js
|
|
@@ -362,7 +361,7 @@ export default async function (event, next) {
|
|
|
362
361
|
|
|
363
362
|
#### Universal Handlers
|
|
364
363
|
|
|
365
|
-
Universal handlers (`handler.js`) are handlers declared without any layer binding. They represent the default handler for a route. And they imply logic that can run anywhere in the client-server stack.
|
|
364
|
+
Universal route handlers (`handler.js`) are handlers declared without any layer binding. They represent the default handler for a route. And they imply logic that can run anywhere in the client-server stack.
|
|
366
365
|
They execute wherever no layer-specific handler exists for the current layer, making them perfect for universal logic.
|
|
367
366
|
|
|
368
367
|
```js
|
|
@@ -543,7 +542,8 @@ export default async function (event, next) {
|
|
|
543
542
|
export default async function (event, next) {
|
|
544
543
|
// Using event.user.isSignedIn() to check authentication
|
|
545
544
|
if (!await event.user.isSignedIn()) {
|
|
546
|
-
|
|
545
|
+
await event.redirect('/login');
|
|
546
|
+
return;
|
|
547
547
|
}
|
|
548
548
|
return next();
|
|
549
549
|
}
|