@webqit/webflo 0.20.12 → 0.20.13-next.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/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "vanila-javascript"
13
13
  ],
14
14
  "homepage": "https://webqit.io/tooling/webflo",
15
- "version": "0.20.12",
15
+ "version": "0.20.13-next.1",
16
16
  "license": "MIT",
17
17
  "repository": {
18
18
  "type": "git",
@@ -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(data, { done: false });
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 all of which can be mutated in-place.
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 server. A server-to-server interaction, for example, is just a matter of `await LiveResponse.from(await fetch('https://api.example.com/data'))`.
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 achieving the same through higher-level APIs like `event.user.confirm()`:
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.waitUntilNavigate();
265
+ event.waitUntil(new Promise(() => {}));
256
266
  event.respondWith(data);
257
267
  ```
258
268
 
259
269
  ```js
260
- event.waitUntil(new Promise(() => {}));
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
- ### _Handler returns a `State` object_
325
+ The handler automatically enters background mode.
314
326
 
315
- A handler may return a `State` object by declaring a `live` function:
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 live function(event) {
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 `live` keyword is a shorthand for defining a quantum function that returns a `State` object.
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 = await LiveResponse.from({ step: 1 }, { done: false });
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 arrives; the UI reflects every step of the object's construction.
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
- Customization is as simple as intercepting these via `preventDefault()` to render your own modals.
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
- Then on the client:
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` totally make it possible to fit routing logic into a single handler.
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 may perform logic and either return a response or call `next()`.
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`, all within the same app.
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** which represents the _vertical_ flow of the same request through the application stack (client server).
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 follows a model that supports request handling and routing at all three layers in this stack: the browser window layer (**client**), the service worker layer (**worker**), the server layer (**server**).
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 donw the layers:
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
- Handlers across this vertical stack are optional; if a layer-specific file doesn’t exist, Webflo automatically falls back to the unsuffixed one `handler.js`,
269
- if defined. Otherwise, the request continues to the next layer. Each request gets a graceful path from local logic to remote fulfillment.
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
- As with the horizontal flow, each layer may intercept, fulfill, or delegate down the request.<br>
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 various factors and use cases.
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
- return new Response(null, { status: 302, headers: { Location: '/login' } });
545
+ await event.redirect('/login');
546
+ return;
547
547
  }
548
548
  return next();
549
549
  }
@@ -155,7 +155,7 @@ async function bundleScript({ $context, $source, which, outfile, asModule = true
155
155
  plugins: [UseLiveTransform()],
156
156
  ...(restParams.buildParams || {})
157
157
  };
158
- if (!asModule) {
158
+ if (asModule && which !== 'server') {
159
159
  // Support top-level await
160
160
  // See: https://github.com/evanw/esbuild/issues/253#issuecomment-826147115
161
161
  bundlingConfig.banner.js += '(async () => {';
@@ -11,7 +11,7 @@ export class ClientSideWorkport extends EventTarget {
11
11
 
12
12
  static async initialize(parentNode, file, params = {}) {
13
13
  const registration = (await navigator.serviceWorker.getRegistration())
14
- || (await navigator.serviceWorker.register(file, { scope: '/', ...params }));
14
+ || (await navigator.serviceWorker.register(file, { scope: '/', type: 'module', ...params }));
15
15
  return new this(parentNode, registration, params);
16
16
  }
17
17
 
@@ -1,42 +0,0 @@
1
- import Fs from 'fs/promises';
2
- import { parse, compile, matchPrologDirective, serialize } from '@webqit/use-live';
3
-
4
- export function UseLiveTransform() {
5
- return {
6
- name: 'uselive-transform',
7
- setup(build) {
8
- build.onLoad({ filter: /\.(js|mjs|ts|jsx|tsx)$/ }, async (args) => {
9
- const code = await Fs.readFile(args.path, 'utf8');
10
-
11
- // Super dirty detection
12
- if (matchPrologDirective(code)) {
13
- // Actual check...
14
-
15
- let ast;
16
- try { ast = parse(code, parserParams); } catch (e) { console.error(args.path, '\nUseLive transform error:', e); }
17
-
18
- if (ast?.isLiveProgram || ast?.hasLiveFunctions) {
19
- const result = await compile(parserParams.sourceType+'-file', ast, {
20
- liveMode: ast.isLiveProgram, // Regarding top-level
21
- fileName: args.path,
22
- });
23
- return { contents: serialize(result), loader: 'js' };
24
- }
25
- }
26
-
27
- return { contents: code, loader: 'default' };
28
- });
29
- }
30
- };
31
- }
32
-
33
- export const parserParams = {
34
- ecmaVersion: 'latest',
35
- sourceType: 'module',
36
- executionMode: 'RegularProgram', // 'LiveProgram'
37
- allowReturnOutsideFunction: true,
38
- allowAwaitOutsideFunction: true,
39
- allowSuperOutsideMethod: false,
40
- preserveParens: false,
41
- locations: true,
42
- };