@webqit/webflo 0.11.11 β†’ 0.11.14

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.
Files changed (2) hide show
  1. package/README.md +534 -292
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -20,7 +20,6 @@ Ok, we've put all of that up for a straight read!
20
20
  + [Installation](#installation)
21
21
  + [Concepts](#concepts)
22
22
  + [Webflo Applications](#webflo-applications)
23
- + [Workflow API](#workflow-api)
24
23
  + [Webflo Config](#webflo-config)
25
24
  + [Technology Stack](#technology-stack)
26
25
  + [Getting Started](#getting-started)
@@ -124,7 +123,7 @@ This and much more - ahead!
124
123
  </details>
125
124
 
126
125
  <details>
127
- <summary><b>Build <i>future-proof anything</i></b> by banking more on web standards and less on abstractions! Webflo <i>just follows</i> where a native feature, standard, or conventional HTML, CSS or JS <i>just works</i>!</summary>
126
+ <summary><b>Build <i>future-proof anything</i></b> by banking more on web standards and less on abstractions! Webflo <i>just follows</i> where a native feature, standard, or conventional HTML, CSS or JS <i>already works</i>!</summary>
128
127
 
129
128
  Here's a glimpse of the standards-based stack you get of Webflo!
130
129
 
@@ -195,13 +194,13 @@ Other important definitions like project `name`, package `type`, and *aliases* f
195
194
  }
196
195
  ```
197
196
 
198
- All is now set! The commands `npm start` and `npm run generate` will be coming in often during development.
197
+ And that gets it all ready! The commands `npm start` and `npm run generate` will be coming in often during development.
199
198
 
200
199
  ### "Hello World!"
201
200
 
202
201
  To be sure that Webflo is listening, run `npx webflo help` on the terminal. An overview of available commands should be shown.
203
202
 
204
- If you can't wait to say *Hello World!* πŸ˜…, you can have an HTML page say that right now!
203
+ If you can't wait to say *Hello World!* πŸ˜…, you can have an HTML page say that right away!
205
204
  + Create an `index.html` file in a new subdirectory `public`.
206
205
 
207
206
  ```shell
@@ -225,7 +224,7 @@ If you can't wait to say *Hello World!* πŸ˜…, you can have an HTML page say that
225
224
  + Start the Webflo server and visit `http://localhost:3000` on your browser to see your page. πŸ˜ƒ
226
225
 
227
226
  ```bash
228
- $ npm start
227
+ npm start
229
228
  ```
230
229
 
231
230
  ## Concepts
@@ -241,6 +240,8 @@ If you can't wait to say *Hello World!* πŸ˜…, you can have an HTML page say that
241
240
 
242
241
  Whether building a *server-based*, *browser-based*, or *universal* application, Webflo gives you one consistent way to handle routing and navigation: using *handler functions*!
243
242
 
243
+ You just define an `index.js` file with a function that gets called to handle a request! No setup!
244
+
244
245
  ```js
245
246
  /**
246
247
  [server|client|worker]
@@ -250,10 +251,13 @@ export default function(event, context, next) {
250
251
  }
251
252
  ```
252
253
 
253
- > **Note**
254
- > <br>Other [*method*](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods)-specific function names may be used: `get`, `post`, `put`, `patch`, `del` (for *delete*), `options`, `head`, etc.
254
+ <details>
255
+ <summary>More details...</summary>
255
256
 
256
- Each function receives an `event` object representing details - e.g. `event.request`, `event.url`, `event.session` - about the current request. ([Details ahead](#workflow-api).)
257
+ > Function name may also be specific to a [*HTTP method*](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods): `get`, `post`, `put`, `patch`, `del` (for *delete*), `options`, `head`, etc.
258
+ </details>
259
+
260
+ Each function receives an `event` object representing details about the request - e.g. `event.request`, `event.url`, `event.session`. ([Details ahead](#workflow-api).)
257
261
 
258
262
  For *server-based* applications (e.g. traditional web apps and API backends), server-side handlers go into a directory named `server`.
259
263
 
@@ -270,8 +274,11 @@ export default function(event, context, next) {
270
274
  }
271
275
  ```
272
276
 
273
- > **Note**
274
- > <br>The above function responds on starting the server - `npm start` on your terminal - and visiting http://localhost:3000.
277
+ <details>
278
+ <summary>How it works...</summary>
279
+
280
+ > The above function responds on starting the server - `npm start` on your terminal - and visiting http://localhost:3000.
281
+ </details>
275
282
 
276
283
  For *browser-based* applications (e.g. Single Page Apps), client-side handlers go into a directory named `client`.
277
284
 
@@ -288,8 +295,11 @@ export default function(event, context, next) {
288
295
  }
289
296
  ```
290
297
 
291
- > **Note**
292
- > <br>The above function is built as part of your application's JS bundle on running the `npm run generate` command. (It is typically bundled to the file `./public/bundle.js`. And the `--auto-embed` flag in that command gets it automatically embedded on your `./public/index.html` page as `<script type="module" src="/bundle.js"></script>`.) Then it responds from right in the browser on visiting http://localhost:3000.
298
+ <details>
299
+ <summary>How it works...</summary>
300
+
301
+ > The above function is built as part of your application's JS bundle on running the `npm run generate` command. (It is typically bundled to the file `./public/bundle.js`. And the `--auto-embed` flag in that command gets it automatically embedded on your `./public/index.html` page as `<script type="module" src="/bundle.js"></script>`.) Then it responds from right in the browser on visiting http://localhost:3000.
302
+ </details>
293
303
 
294
304
  For *browser-based* applications that want to support offline usage via Service-Workers (e.g Progressive Web Apps), Webflo allows us to define equivalent handlers for requests hitting the Service Worker. These worker-based handlers go into a directory named `worker`.
295
305
 
@@ -306,10 +316,13 @@ export default function(event, context, next) {
306
316
  }
307
317
  ```
308
318
 
309
- > **Note**
310
- > <br>The above function is built as part of your application's Service Worker JS bundle on running the `npm run generate` command. (It is typically bundled to the file `./public/worker.js`, and the main application bundle automatically connects to it.) Then it responds from within the Service Worker on visiting http://localhost:3000. (More details [ahead](#service-workers).)
319
+ <details>
320
+ <summary>How it works...</summary>
321
+
322
+ > The above function is built as part of your application's Service Worker JS bundle on running the `npm run generate` command. (It is typically bundled to the file `./public/worker.js`, and the main application bundle automatically connects to it.) Then it responds from within the Service Worker on visiting http://localhost:3000. (More details [ahead](#service-workers).)
323
+ </details>
311
324
 
312
- So, depending on what's being built, an application's handler functions may take the following form (in part or in whole as with universal applications):
325
+ So, depending on what's being built, an application's handler functions may take the following form (in part or in whole):
313
326
 
314
327
  ```shell
315
328
  client
@@ -344,7 +357,38 @@ server
344
357
  └── stickers/index.js ------------------ http://localhost:3000/products/stickers
345
358
  ```
346
359
 
347
- Each step calls a `next()` function to forward the current request to the next step (if any), is able to pass a `context` object along, and can *recompose* the return value.
360
+ Each step calls a `next()` function to forward the current request to the next step.
361
+
362
+ ```js
363
+ /**
364
+ server
365
+ β”œβ”€β”€ index.js
366
+ */
367
+ export default async function(event, context, next) {
368
+ if (next.stepname) {
369
+ return next();
370
+ }
371
+ return { title: 'Home | FluffyPets' };
372
+ }
373
+ ```
374
+
375
+ ```js
376
+ /**
377
+ server
378
+ β”œβ”€β”€ products/index.js
379
+ */
380
+ export default function(event, context, next) {
381
+ if (next.stepname) {
382
+ return next();
383
+ }
384
+ return { title: 'Products' };
385
+ }
386
+ ```
387
+
388
+ <details>
389
+ <summary>More details...</summary>
390
+
391
+ Each step can pass a `context` object to a child step, and can *recompose* its return value.
348
392
 
349
393
  ```js
350
394
  /**
@@ -361,18 +405,56 @@ export default async function(event, context, next) {
361
405
  }
362
406
  ```
363
407
 
408
+ <details>
409
+ <summary>Even more details...</summary>
410
+
411
+ The `next()` function can be used to re-direct the current request to a different route - using a relative or absolute URL.
412
+
413
+ ```js
414
+ /**
415
+ server
416
+ β”œβ”€β”€ index.js
417
+ */
418
+ export default async function(event, context, next) {
419
+ if (next.stepname === 'products') {
420
+ return next( context, '/api/products?params=allowed' ); // With an absolute URL
421
+ }
422
+ return { title: 'Home | FluffyPets' };
423
+ }
424
+ ```
425
+
364
426
  ```js
365
427
  /**
366
428
  server
367
429
  β”œβ”€β”€ products/index.js
368
430
  */
369
- export default function(event, context, next) {
431
+ export default async function(event, context, next) {
370
432
  if (next.stepname) {
371
433
  return next();
372
434
  }
373
- return { title: 'Products' };
435
+ return next( context, '../api/products?params=allowed' ); // With a relative URL
436
+ }
437
+ ```
438
+
439
+ The `next()` function can also run as an independent request - using [the same parameters as with the WHATWG Request constructor](https://developer.mozilla.org/en-US/docs/Web/API/Request/Request#parameters).
440
+
441
+ ```js
442
+ /**
443
+ server
444
+ β”œβ”€β”€ index.js
445
+ */
446
+ export default async function(event, context, next) {
447
+ if (next.stepname === 'products') {
448
+ return next( context, '/api/products?params=allowed', {
449
+ method: 'get', { headers: { Authorization: 'djjdd' } }
450
+ });
451
+ }
452
+ return { title: 'Home | FluffyPets' };
374
453
  }
375
454
  ```
455
+ </details>
456
+
457
+ </details>
376
458
 
377
459
  This step-based workflow helps to decomplicate routing and gets us scaling horizontally as our application grows larger.
378
460
 
@@ -469,7 +551,7 @@ export default async function(event, context, next) {
469
551
  }
470
552
  ```
471
553
 
472
- Now we get the following layout-to-URL mapping for our application:
554
+ Now we get the following handler-to-URL mapping for our application:
473
555
 
474
556
  ```shell
475
557
  my-app
@@ -504,7 +586,7 @@ export default async function(event, context, next) {
504
586
  }
505
587
  ```
506
588
 
507
- Our overall layout-to-URL mapping for this application now becomes:
589
+ Our overall handler-to-URL mapping for this application now becomes:
508
590
 
509
591
  ```shell
510
592
  my-app
@@ -566,12 +648,15 @@ But, we can also access the route in a way that gets the data rendered into the
566
648
  Now, for Single Page Applications, subsequent navigations, after the initial page load, just ask for the data on destination URLs and perform [Client-Side Rendering](#client-and-server-side-rendering) on the same running document. Navigation is sleek and instant!
567
649
 
568
650
  > **Note**
569
- > <br>Unless disabled, [SPA Routing](#spa_routing) is automatically built into your app's JS bundle from the `npm run generate` command. So, it just works!
651
+ > <br>Unless disabled, [SPA Routing](#spa-routing) is automatically built into your app's JS bundle from the `npm run generate` command. So, it just works!
570
652
 
571
653
  With no extra work, your application can function as either a *Multi Page App (MPA)* or a *Single Page App (SPA)*!
572
654
 
573
- > **Note**
574
- > <br>In a Single Page Application, all pages are based off a single `index.html` document. In a Multi Page Application, pages are individual `index.html` documents - ideally. But, Server-Side Rendering makes it possible to serve the same, but dynamically-rendered, `index.html` document across page loads - essentially an SPA architecture hiding on the server. But, here, lets take Multi Page Applications for an individual-page architecture.
655
+ <details>
656
+ <summary>Some disambiguation...</summary>
657
+
658
+ > In a Single Page Application, all pages are based off a single `index.html` document. In a Multi Page Application, pages are individual `index.html` documents - ideally. But, Server-Side Rendering makes it possible to serve the same, but dynamically-rendered, `index.html` document across page loads - essentially an SPA architecture hiding on the server. But, here, lets take Multi Page Applications for an individual-page architecture.
659
+ </details>
575
660
 
576
661
  #### Layout and Templating Overview
577
662
 
@@ -587,7 +672,7 @@ my-app
587
672
  └── footer.html ------------------------------ <footer></footer> <!-- To appear at bottom of each index.html page -->
588
673
  ```
589
674
 
590
- In a Single Page Application, each page is the same `index.html` document, and it is often necessary to have the main page sections change on each route. These sections can be defined per-route and *imported* to the document on navigating to their respective URLs.
675
+ In a Single Page Application, each page is the same `index.html` document, and it is often necessary to have the main page sections change on each route. These sections can be defined per-route and *imported* to the document on navigating to their respective route.
591
676
 
592
677
  ```html
593
678
  my-app
@@ -600,7 +685,7 @@ my-app
600
685
 
601
686
  This, in both cases, is templating - the ability to define HTML *partials* once, and have them reused multiple times. Webflo just concerns itself with templating, and the choice of a Multi Page Application or Single Page Application becomes yours! And heck, you can even have the best of both worlds in the same application - with an architecture we'll call [Multi SPA](#in-a-multi-spa-layout)! It's all a *layout* thing!
602
687
 
603
- Now, with pages in Webflo being [DOM-based](#overview) (both client-side and [server-side](#oohtml-ssr)), documents can be manipulated directly with DOM APIs, e.g. to replace or insert nodes, attributes, etc. But even better, templating in Webflo is based on the [HTML Modules](https://github.com/webqit/oohtml#html-modules) and [HTML Imports](https://github.com/webqit/oohtml#html-imports) features in [OOHTML](#oohtml) - unless disabled in config. These features provide a powerful declarative templating system on top of the standard [HTML `<template>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template) element - with a *module*, *export* and *import* paradigm.
688
+ Now, with pages in Webflo being [DOM-based](#overview) (both client-side and [server-side](#oohtml-ssr)), documents can be manipulated directly with DOM APIs, e.g. to replace or insert nodes, attributes, etc. But even better, templating in Webflo is based on the [HTML Modules](https://github.com/webqit/oohtml#html-modules) and [HTML Imports](https://github.com/webqit/oohtml#html-imports) features in [OOHTML](#oohtml) - unless disabled in config. These features provide a powerful declarative templating system on top of the standard [HTML `<template>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template) element - all in a *module*, *export* and *import* paradigm.
604
689
 
605
690
  Here, you are able to define reusable contents in a `<template>` element...
606
691
 
@@ -622,7 +707,7 @@ Here, you are able to define reusable contents in a `<template>` element...
622
707
  </body>
623
708
  ```
624
709
 
625
- The *module* element - `<template name>` - is able to load its contents from a remote `.html` file that serves as a bundle:
710
+ The *module* element - `<template>` - is able to load its contents from a remote `.html` file that serves as a bundle:
626
711
 
627
712
  ```html
628
713
  <!--
@@ -634,6 +719,10 @@ public
634
719
  ```
635
720
 
636
721
  ```html
722
+ <!--
723
+ public
724
+ β”œβ”€β”€ index.html
725
+ -->
637
726
  <head>
638
727
  <template name="routes" src="/bundle.html"></template>
639
728
  </head>
@@ -643,7 +732,7 @@ What [we'll see shortly](#bundling) is how multiple standalone `.html` files - e
643
732
 
644
733
  #### In a Multi Page Layout
645
734
 
646
- In a Multi Page layout (as [above](#layout-and-templating-overview)), generic contents - e.g. header and footer sections, etc. - are typically bundled into one `bundle.html` file that can be embedded on each page of the application.
735
+ In a Multi Page layout (as seen [earlier](#layout-and-templating-overview)), generic contents - e.g. header and footer sections, etc. - are typically bundled into one `bundle.html` file that can be embedded on each page of the application.
647
736
 
648
737
  ```html
649
738
  <!--
@@ -702,12 +791,15 @@ public/products
702
791
  </html>
703
792
  ```
704
793
 
705
- > **Note**
706
- > <br>In this architecture, navigation is traditional - a new page loads each time. The `bundle.js` script comes with the appropriate OOHTML support level required for the imports to function.
794
+ <details>
795
+ <summary>How it works...</summary>
796
+
797
+ > In this architecture, navigation is traditional - a new page loads each time. The `bundle.js` script comes with the appropriate OOHTML support level required for the imports to function.
798
+ </details>
707
799
 
708
800
  #### In a Single Page Layout
709
801
 
710
- In a Single Page layout (as seen [earlier](#layout-and-templating-overview)), page-specific contents - e.g. main sections - are typically bundled together into one `bundle.html` file that can be embedded on the document root. Nested routes end up as nested `<template>` elements that form the equivalent of thw application's URL structure.
802
+ In a Single Page layout (as seen [earlier](#layout-and-templating-overview)), page-specific contents - e.g. main sections - are typically bundled together into one `bundle.html` file that can be embedded on the document root. Nested routes end up as nested `<template>` elements that form the equivalent of the application's URL structure.
711
803
 
712
804
  ```html
713
805
  <!--
@@ -723,7 +815,7 @@ public
723
815
  <main exportgroup="main.html">Welcome to our Home Page</main>
724
816
  ```
725
817
 
726
- Now, the `<main>` elements are each imported on navigating to their respective URLs. This time, Webflo takes care of setting the URL path as a global `template` attribute on the `<body>` element such that `<import>` elements that inherit this global attribute are resolved from its current value.
818
+ Now, the `<main>` elements are each imported on navigating to their respective routes. This time, Webflo takes care of setting the URL path as a global `template` attribute on the `<body>` element such that `<import>` elements that inherit this global attribute are resolved from its current value.
727
819
 
728
820
  ```html
729
821
  <!--
@@ -744,8 +836,11 @@ public
744
836
  </html>
745
837
  ```
746
838
 
747
- > **Note**
748
- > <br>In this architecture, navigation is instant and sleek - Webflo prevents a full page reload, obtains and sets data at `document.state.data` for the new URL, then sets the `template` attribute on the `<body>` element to the new URL path. The `bundle.js` script comes with the appropriate OOHTML support level required for the imports to function.
839
+ <details>
840
+ <summary>How it works...</summary>
841
+
842
+ > In this architecture, navigation is instant and sleek - Webflo prevents a full page reload, obtains and sets data at `document.state.data` for the new URL, then sets the `template` attribute on the `<body>` element to the new URL path. The `bundle.js` script comes with the appropriate OOHTML support level required for the imports to function.
843
+ </details>
749
844
 
750
845
  #### In a Multi SPA Layout
751
846
 
@@ -765,7 +860,11 @@ my-app
765
860
  └── footer.html ------------------------------ <footer></footer> <!-- To appear at bottom of each document root -->
766
861
  ```
767
862
 
768
- The above gives us three document roots: `/index.html`, `/about/index.html`, `/prodcuts/index.html`. The `/prodcuts` route doubles as a Single Page Application such that visiting the `/prodcuts` route loads the document root `/prodcuts/index.html` and lets Webflo SPA routing determine which of `/prodcuts/main.html`, `/prodcuts/free/main.html`, `/prodcuts/paid/main.html` is imported on a given URL.
863
+ <details>
864
+ <summary>How it works...</summary>
865
+
866
+ > The above gives us three document roots: `/index.html`, `/about/index.html`, `/prodcuts/index.html`. The `/prodcuts` route doubles as a Single Page Application such that visiting the `/prodcuts` route loads the document root `/prodcuts/index.html` and lets Webflo SPA routing determine which of `/prodcuts/main.html`, `/prodcuts/free/main.html`, `/prodcuts/paid/main.html` is imported on a given URL.
867
+ </details>
769
868
 
770
869
  Webflo ensures that only the amount of JavaScript for a document root is actually loaded! So, above, a common JavaScript build is shared across the three document roots alongside an often tiny root-specific build.
771
870
 
@@ -777,7 +876,7 @@ public
777
876
  <!DOCTYPE html>
778
877
  <html>
779
878
  <head>
780
- <script type="module" src="webflo.bundle.js"></script>
879
+ <script type="module" src="/webflo.bundle.js"></script>
781
880
  <script type="module" src="/products/bundle.js"></script>
782
881
  <template name="pages" src="/bundle.html"></template>
783
882
  </head>
@@ -793,7 +892,7 @@ public
793
892
  <!DOCTYPE html>
794
893
  <html>
795
894
  <head>
796
- <script type="module" src="webflo.bundle.js"></script>
895
+ <script type="module" src="/webflo.bundle.js"></script>
797
896
  <script type="module" src="/about/bundle.js"></script>
798
897
  <template name="pages" src="/bundle.html"></template>
799
898
  </head>
@@ -809,7 +908,7 @@ public
809
908
  <!DOCTYPE html>
810
909
  <html>
811
910
  <head>
812
- <script type="module" src="webflo.bundle.js"></script>
911
+ <script type="module" src="/webflo.bundle.js"></script>
813
912
  <script type="module" src="/bundle.js"></script>
814
913
  <template name="pages" src="/bundle.html"></template>
815
914
  </head>
@@ -908,8 +1007,11 @@ However, since the `document` objects in Webflo natively support [OOHTML](#oohtm
908
1007
  </html>
909
1008
  ```
910
1009
 
911
- > **Note**
912
- > <br>Now, this comes logical since logic is the whole essence of the HTML `<script>` element, after all! Compared to other syntax alternatives, this uniquely enables us to do all things logic in the actual language for logic - JavaScript. Then, OOHTML gives us more by extending the regular `<script>` element with the `subscript` type which gets any JavaScript code to be *reactive*!
1010
+ <details>
1011
+ <summary>Re-introducing logic in the actual language for logic - JavaScript...</summary>
1012
+
1013
+ > Now, this comes logical being that logic is the whole essence of the HTML `<script>` element after all! Compared to other syntax alternatives, this uniquely enables us to do all things logic in the actual language for logic - JavaScript. Then, OOHTML gives us more by extending the regular `<script>` element with the `subscript` type which gets any JavaScript code to be *reactive*!
1014
+ </details>
913
1015
 
914
1016
  Note that because these scripts are naturally reactive, we do not require any `setTimeout()` construct like we required earlier in the case of the classic `<script>` element. These expressions self-update as the values they depend on become available, removed, or updated - i.e. as `document.state` gets updated.
915
1017
 
@@ -1064,15 +1166,22 @@ Observer.observe(state, propertyName, change => {
1064
1166
 
1065
1167
  This way, all the moving parts of your application remain coordinated, and can easily be rendered to reflect them on the UI!
1066
1168
 
1067
- For all things application state, Webflo leverages the [State API](https://github.com/webqit/oohtml#state-api) that's natively available in OOHTML-based documents - both client-side and server-side. This API exposes an application-wide `document.state` object and a per-element `element.state` object. And these are *live* read/write objects that can be observed for property changes using the [Observer API](#the-observer-api). It comes off as the simplest approach to state and reactivity!
1169
+ Now, for all things application state, Webflo leverages the [State API](https://github.com/webqit/oohtml#state-api) that's natively available in OOHTML-based documents - both client-side and server-side. This API exposes an application-wide `document.state` object and a per-element `element.state` object. And these are *live* read/write objects that can be observed for property changes using the [Observer API](#the-observer-api). It comes off as the simplest approach to state and reactivity!
1068
1170
 
1069
1171
  > **Note**
1070
- > <br>The State API is not available when the [OOHTML support level](#oohtml) in config is switched away from `full` and `scripting`.
1172
+ > <br>The State API is available as long as the [OOHTML support level](#oohtml) in config is left as `full`, or set to `scripting`.
1071
1173
 
1072
1174
  #### The `document.state.data` Object
1073
1175
 
1074
1176
  This property reperesents the application data at any point in time - obtained from route handers on each navigation. Webflo simply updates this property and lets the page's [rendering logic](#client-and-server-side-rendering), or other parts of the application, take over.
1075
1177
 
1178
+ ```js
1179
+ console.log(document.state.data) // { title: 'Home | FluffyPets' }
1180
+ ```
1181
+
1182
+ <details>
1183
+ <summary>More examples...</summary>
1184
+
1076
1185
  ```js
1077
1186
  Observer.observe(document.state, 'data', e => {
1078
1187
  console.log('Current page data is: ', e.value);
@@ -1085,6 +1194,7 @@ Observer.observe(document.state, 'data', e => {
1085
1194
  document.title = title;
1086
1195
  </script>
1087
1196
  ```
1197
+ </details>
1088
1198
 
1089
1199
  #### The `document.state.url` Object
1090
1200
 
@@ -1094,6 +1204,9 @@ This is a *live* object that reperesents the properties of the application URL a
1094
1204
  console.log(document.state.url) // { hash, host, hostname, href, origin, password, pathname, port, protocol, search, searchParams, username }
1095
1205
  ```
1096
1206
 
1207
+ <details>
1208
+ <summary>More examples...</summary>
1209
+
1097
1210
  ```js
1098
1211
  Observer.observe(document.state.url, 'hash', e => {
1099
1212
  console.log(document.state.url.hash === e.value); // true
@@ -1132,6 +1245,7 @@ document.addEventListener('synthetic-navigation', e => {
1132
1245
  document.title = 'Login as ' + role;
1133
1246
  </script>
1134
1247
  ```
1248
+ </details>
1135
1249
 
1136
1250
  ### Requests and Responses
1137
1251
 
@@ -1199,6 +1313,9 @@ Where workflows throw an exception, an *error* status is implied.
1199
1313
 
1200
1314
  Handlers can set [response cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) via the standard `Response` constructor, or using the standard `Headers.set()` method.
1201
1315
 
1316
+ <details>
1317
+ <summary>Examples...</summary>
1318
+
1202
1319
  ```js
1203
1320
  let response = event.Response(data, { headers: { 'Set-Cookie': cookieString }});
1204
1321
 
@@ -1220,19 +1337,20 @@ response.headers.cookies = { 'Cookie-1': cookieObject };
1220
1337
  response.headers.cookies = { 'Cookie-2': cookie2Object };
1221
1338
 
1222
1339
  console.log(response.headers.cookies); // { 'Cookie-1': cookieObject, 'Cookie-2': cookie2Object };
1223
- ````
1340
+ ```
1224
1341
 
1225
1342
  Set cookies are [accessed](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cookie) on the next request via request headers.
1226
1343
 
1227
1344
  ```js
1228
1345
  console.log(event.request.headers.get('Cookie')); // Cookie-1=cookie-val&Cookie-2=cookie2-val;
1229
- ````
1346
+ ```
1230
1347
 
1231
1348
  Webflo also offers a *convenience* method.
1232
1349
 
1233
1350
  ```js
1234
1351
  console.log(event.request.headers.cookies); // { 'Cookie-1': 'cookie-val', 'Cookie-2': 'cookie2-val' };
1235
1352
  ```
1353
+ </details>
1236
1354
 
1237
1355
  ### Webflo Applications
1238
1356
 
@@ -1251,11 +1369,18 @@ On being loaded, the state of the application is initialized, or is restored thr
1251
1369
 
1252
1370
  ##### SPA Navigation
1253
1371
 
1254
- Unless disabled in config, it is factored-in at build time for the application client JS to be able to automatially figure out when to intercept a navigation event and prevent a full page reload, and when not to. It follows the following rules:
1372
+ Unless disabled in config, it is factored-in at build time for the application client JS to be able to automatially figure out when to intercept a navigation event and prevent a full page reload, and when not to.
1373
+
1374
+ <details>
1375
+ <summary>How it works...</summary>
1376
+
1377
+ SPA Navigation follows the following rules:
1378
+
1255
1379
  + When it ascertains that the destination URL is based on the current running `index.html` document in the browser (an SPA architecture), a full page reload is prevented for *soft* navigation. But where the destination URL points out of the current document root (a [Multi SPA](#in-a-multi-spa-layout) architecture), navigation is allowed as a normal page load, and a new page root is loaded.
1256
1380
  + If navigation is initiated with any of the following keys pressed: Meta Key, Alt Key, Shift Key, Ctrl Key, navigation is allowed to work the default way - regardless of the first rule above.
1257
1381
  + If navigation is initiated from a link element that has the `target` attribute, or the `download` attribute, navigation is allowed to work the default way - regardless of the first rule above.
1258
1382
  + If navigation is initiated from a form element that has the `target` attribute, navigation is allowed to work the default way - regardless of the first rule above.
1383
+ </details>
1259
1384
 
1260
1385
  <details>
1261
1386
  <summary>Config (Default)</summary>
@@ -1264,7 +1389,9 @@ Unless disabled in config, it is factored-in at build time for the application c
1264
1389
  { "spa_navigation": true }
1265
1390
  ```
1266
1391
 
1267
- > File: `.webqit/webflo/client.json` | Command: `webflo config client spa_navigation=TRUE`
1392
+ > **File: `.webqit/webflo/client.json`**
1393
+
1394
+ > **Command: `webflo config client spa_navigation=TRUE`**
1268
1395
  </details>
1269
1396
 
1270
1397
  ##### SPA State
@@ -1279,75 +1406,98 @@ This is a *live* object that exposes the network activity and network state of t
1279
1406
  console.log(document.state.network) // { requesting, remote, error, redirecting, connectivity, }
1280
1407
  ```
1281
1408
 
1282
- + **`network.requesting`: `null|Object`** - This property tells when a request is ongoing, in which case it exposes the `params` object used to initiate the request.
1283
-
1284
- On the UI, this could be used to hide a menu drawer that may have been open.
1285
-
1286
- ```html
1287
- <menu-drawer>
1288
- <script type="subscript">
1289
- let { network: { requesting } } = document.state;
1290
- if (requesting) {
1291
- $(this).attr('open', false);
1292
- }
1293
- </script>
1294
- </menu-drawer>
1295
- ```
1296
-
1297
- + **`network.remote`: `null|String`** - This property tells when a remote request is ongoing - usually the same navigation requests as at `network.requesting`, but when not handled by any client-side route handlers, or when `next()`ed to this point by route handlers. The `remote` property also goes live when a route handler calls the special `fetch()` function that they recieve on their fourth parameter.
1298
-
1299
- On the UI, this could be used to show/hide a spinner, or progress bar, to provide a visual cue.
1300
-
1301
- ```html
1302
- <progress-bar>
1303
- <script type="subscript">
1304
- let { network: { remote } } = document.state;
1305
- $(this).attr('hidden', !remote);
1306
- </script>
1307
- </progress-bar>
1308
- ```
1309
-
1310
- + **`network.error`: `null|Error`** - This property tells when a request is *errored* in which case it contains an `Error` instance of the error. For requests that can be retried, the `Error` instance also has a custom `retry()` method.
1311
-
1312
- On the UI, this could be used to show/hide cute error elements.
1313
-
1314
- ```html
1315
- <nice-error>
1316
- <script type="subscript">
1317
- let { network: { error } } = document.state;
1318
- $(this).attr('hidden', !error);
1319
- </script>
1320
- </nice-error>
1321
- ```
1322
-
1323
- + **`network.redirecting`: `null|String`** - This property tells when a client-side redirect is ongoing - see [Scenario 4: Single Page Navigation Requests and Responses](#scenario-4-single-page-navigation-requests-and-responses) - in which case it exposes the destination URL.
1324
-
1325
- On the UI, this could be used to prevent further interactions with the outgoing page.
1326
-
1327
- ```html
1328
- <body>
1329
- <script type="subscript">
1330
- let { network: { redirecting } } = document.state;
1331
- $(this).css(redirecting ? { pointerEvents: 'none', filter: 'blur(2)' } : { pointerEvents: 'auto', filter: 'blur(0)' });
1332
- </script>
1333
- </body>
1334
- ```
1335
-
1336
- + **`network.connectivity`: `String`** - This property tells of [the browser's ability to connect to the network](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine): `online`, `offline`.
1337
-
1338
- On the UI, this could be used to show/hide a connectivity status.
1339
-
1340
- ```html
1341
- <body>
1342
- <script type="subscript">
1343
- let { network: { connectivity } } = document.state;
1344
- $(this).attr( 'connectivity', connectivity });
1345
- </script>
1346
- </body>
1347
- ```
1409
+ <details>
1410
+ <summary>Property: <code>.network.requesting</code>: <code>`null|Object</code></summary>
1411
+
1412
+ This property tells when a request is ongoing, in which case it exposes the `params` object used to initiate the request.
1413
+
1414
+ On the UI, this could be used to hide a menu drawer that may have been open.
1415
+
1416
+ ```html
1417
+ <menu-drawer>
1418
+ <script type="subscript">
1419
+ let { network: { requesting } } = document.state;
1420
+ if (requesting) {
1421
+ $(this).attr('open', false);
1422
+ }
1423
+ </script>
1424
+ </menu-drawer>
1425
+ ```
1426
+ </details>
1427
+
1428
+ <details>
1429
+ <summary>Property: <code>.network.remote</code>: <code>`null|String</code></summary>
1430
+
1431
+ This property tells when a remote request is ongoing - usually the same navigation requests as at `network.requesting`, but when not handled by any client-side route handlers, or when `next()`ed to this point by route handlers. The `remote` property also goes live when a route handler calls the special `fetch()` function that they recieve on their fourth parameter.
1432
+
1433
+ On the UI, this could be used to show/hide a spinner, or progress bar, to provide a visual cue.
1434
+
1435
+ ```html
1436
+ <progress-bar>
1437
+ <script type="subscript">
1438
+ let { network: { remote } } = document.state;
1439
+ $(this).attr('hidden', !remote);
1440
+ </script>
1441
+ </progress-bar>
1442
+ ```
1443
+ </details>
1444
+
1445
+ <details>
1446
+ <summary>Property: <code>.network.error</code>: <code>`null|Error</code></summary>
1447
+
1448
+ This property tells when a request is *errored* in which case it contains an `Error` instance of the error. For requests that can be retried, the `Error` instance also has a custom `retry()` method.
1449
+
1450
+ On the UI, this could be used to show/hide cute error elements.
1451
+
1452
+ ```html
1453
+ <nice-error>
1454
+ <script type="subscript">
1455
+ let { network: { error } } = document.state;
1456
+ $(this).attr('hidden', !error);
1457
+ </script>
1458
+ </nice-error>
1459
+ ```
1460
+ </details>
1461
+
1462
+ <details>
1463
+ <summary>Property: <code>.network.redirecting</code>: <code>`null|String</code></summary>
1464
+
1465
+ This property tells when a client-side redirect is ongoing - see [Scenario 4: Single Page Navigation Requests and Responses](#scenario-4-single-page-navigation-requests-and-responses) - in which case it exposes the destination URL.
1466
+
1467
+ On the UI, this could be used to prevent further interactions with the outgoing page.
1468
+
1469
+ ```html
1470
+ <body>
1471
+ <script type="subscript">
1472
+ let { network: { redirecting } } = document.state;
1473
+ $(this).css(redirecting ? { pointerEvents: 'none', filter: 'blur(2)' } : { pointerEvents: 'auto', filter: 'blur(0)' });
1474
+ </script>
1475
+ </body>
1476
+ ```
1477
+ </details>
1478
+
1479
+ <details>
1480
+ <summary>Property: <code>.network.connectivity</code>: <code>`String</code></summary>
1481
+
1482
+ This property tells of [the browser's ability to connect to the network](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine): `online`, `offline`.
1483
+
1484
+ On the UI, this could be used to show/hide a connectivity status.
1485
+
1486
+ ```html
1487
+ <body>
1488
+ <script type="subscript">
1489
+ let { network: { connectivity } } = document.state;
1490
+ $(this).attr( 'connectivity', connectivity });
1491
+ </script>
1492
+ </body>
1493
+ ```
1494
+ </details>
1348
1495
 
1349
1496
  Here are some additional examples with the [Observer API](#the-observer-api).
1350
1497
 
1498
+ <details>
1499
+ <summary>Visualize the network state...</summary>
1500
+
1351
1501
  ```js
1352
1502
  // Visualize the network state
1353
1503
  let onlineVisualizer = changes => {
@@ -1358,6 +1508,10 @@ let onlineVisualizer = changes => {
1358
1508
  Observer.observe(document.state.network, onlineVisualizer);
1359
1509
  // Or: Observer.observe(document, [ ['state', 'network'] ], onlineVisualizer, { subtree: true });
1360
1510
  ```
1511
+ </details>
1512
+
1513
+ <details>
1514
+ <summary>Visualize "connectivity"...</summary>
1361
1515
 
1362
1516
  ```js
1363
1517
  // Visualize the 'connectivity' property
@@ -1367,6 +1521,10 @@ let connectivityVisualizer = e => {
1367
1521
  Observer.observe(document.state.network, 'connectivity', connectivityVisualizer);
1368
1522
  // Or: Observer.observe(document.state, [ ['network', 'connectivity'] ], connectivityeVisualizer);
1369
1523
  ```
1524
+ </details>
1525
+
1526
+ <details>
1527
+ <summary>Catch request errors; attempt a retry...</summary>
1370
1528
 
1371
1529
  ```js
1372
1530
  // Catch request errors; attempt a retry
@@ -1379,6 +1537,7 @@ Observer.observe(document.state.network, 'error', e => {
1379
1537
  }
1380
1538
  });
1381
1539
  ```
1540
+ </details>
1382
1541
 
1383
1542
  ##### Form Actions
1384
1543
 
@@ -1412,216 +1571,298 @@ Webflo client-side applications are intended to provide an app-like-first experi
1412
1571
  { "service_worker_support": true }
1413
1572
  ```
1414
1573
 
1415
- > File: `.webqit/webflo/client.json` | Command: `webflo config client service_worker_support=TRUE`
1574
+ > **File: `.webqit/webflo/client.json`**
1575
+
1576
+ > **Command: `webflo config client service_worker_support=TRUE`**
1416
1577
  </details>
1417
1578
 
1418
1579
  ##### Fetching Strategy
1419
1580
 
1420
- + **Network First** - This strategy tells the Service Worker to always attempt fetching from the network first for given resources, before fetching from the cache. On every successful network fetch, a copy of the response is saved to the cache for next time. (This is good for resources that need to be fresh to the user on a "best effort" basis.) Unless changed, this is Webflo's default fetching strategy. When not the default strategy, a list of specific URLs that should be fetched this way can be configured.
1421
-
1422
- <details>
1423
- <summary>Config (Default)</summary>
1581
+ <details>
1582
+ <summary>Network First</summary>
1424
1583
 
1425
- ```json
1426
- { "default_fetching_strategy": "network-first" }
1427
- ```
1584
+ This strategy tells the Service Worker to always attempt fetching from the network first for given resources, before fetching from the cache. On every successful network fetch, a copy of the response is saved to the cache for next time. (This is good for resources that need to be fresh to the user on a "best effort" basis.) Unless changed, this is Webflo's default fetching strategy. When not the default strategy, a list of specific URLs that should be fetched this way can be configured.
1428
1585
 
1429
- *To list specific URLs...*
1586
+ <details>
1587
+ <summary>Config (Default)</summary>
1430
1588
 
1431
- ```json
1432
- { "network_first_urls": [ "/logo.png" ] }
1433
- ```
1589
+ ```json
1590
+ { "default_fetching_strategy": "network-first" }
1591
+ ```
1434
1592
 
1435
- > File: `.webqit/webflo/worker.json` | Command: `webflo config worker default_fetching_strategy=network-first`
1436
- </details>
1593
+ *To list specific URLs...*
1437
1594
 
1438
- + **Cache First** - This strategy tells the Service Worker to always attempt fetching from the cache first for given resources, before fetching from the network. After serving a cached response, or where not found in cache, a network fetch happens and a copy of the response is saved to the cache for next time. (This is good for resources that do not critially need to be fresh to the user.) When not the default strategy, a list of specific URLs that should be fetched this way can be configured.
1439
-
1440
- <details>
1441
- <summary>Config (Alternative)</summary>
1595
+ ```json
1596
+ { "network_first_urls": [ "/logo.png" ] }
1597
+ ```
1442
1598
 
1443
- ```json
1444
- { "default_fetching_strategy": "cache-first" }
1445
- ```
1599
+ > **File: `.webqit/webflo/worker.json`**
1446
1600
 
1447
- *To list specific URLs...*
1601
+ > **Command: `webflo config worker default_fetching_strategy=network-first`**
1602
+ </details>
1603
+ </details>
1448
1604
 
1449
- ```json
1450
- { "cache_first_urls": [ "/logo.png" ] }
1451
- ```
1605
+ <details>
1606
+ <summary>Cache First</summary>
1452
1607
 
1453
- > File: `.webqit/webflo/worker.json` | Command: `webflo config worker default_fetching_strategy=cache-first`
1454
- </details>
1608
+ This strategy tells the Service Worker to always attempt fetching from the cache first for given resources, before fetching from the network. After serving a cached response, or where not found in cache, a network fetch happens and a copy of the response is saved to the cache for next time. (This is good for resources that do not critially need to be fresh to the user.) When not the default strategy, a list of specific URLs that should be fetched this way can be configured.
1455
1609
 
1456
- + **Network Only** - This strategy tells the Service Worker to always fetch given resources from the network only. They are simply not available when offline. (This is good for resources that critially need to be fresh to the user.) When not the default strategy, a list of specific URLs that should be fetched this way can be configured.
1457
-
1458
- <details>
1459
- <summary>Config (Alternative)</summary>
1610
+ <details>
1611
+ <summary>Config</summary>
1460
1612
 
1461
- ```json
1462
- { "default_fetching_strategy": "network-only" }
1463
- ```
1613
+ ```json
1614
+ { "default_fetching_strategy": "cache-first" }
1615
+ ```
1464
1616
 
1465
- *To list specific URLs...*
1617
+ *To list specific URLs...*
1466
1618
 
1467
- ```json
1468
- { "network_only_urls": [ "/logo.png" ] }
1469
- ```
1619
+ ```json
1620
+ { "cache_first_urls": [ "/logo.png" ] }
1621
+ ```
1470
1622
 
1471
- > File: `.webqit/webflo/worker.json` | Command: `webflo config worker default_fetching_strategy=network-only`
1472
- </details>
1623
+ > **File: `.webqit/webflo/worker.json`**
1473
1624
 
1474
- + **Cache Only** - This strategy tells the Service Worker to always fetch given resources from the cache only. (This is good for resources that do not change often.) When not the default strategy, a list of specific URLs that should be fetched this way can be configured. The listed resources are pre-cached ahead of when they'll be needed - and are served from the cache each time. (Pre-caching happens on the one-time `install` event of the Service Worker.)
1625
+ > **Command: `webflo config worker default_fetching_strategy=cache-first`**
1626
+ </details>
1627
+ </details>
1475
1628
 
1476
- <details>
1477
- <summary>Config (Alternative)</summary>
1629
+ <details>
1630
+ <summary>Network Only</summary>
1478
1631
 
1479
- ```json
1480
- { "default_fetching_strategy": "cache-only" }
1481
- ```
1632
+ This strategy tells the Service Worker to always fetch given resources from the network only. They are simply not available when offline. (This is good for resources that critially need to be fresh to the user.) When not the default strategy, a list of specific URLs that should be fetched this way can be configured.
1482
1633
 
1483
- *To list specific URLs...*
1634
+ <details>
1635
+ <summary>Config</summary>
1484
1636
 
1485
- ```json
1486
- { "cache_only_urls": [ "/logo.png" ] }
1487
- ```
1637
+ ```json
1638
+ { "default_fetching_strategy": "network-only" }
1639
+ ```
1488
1640
 
1489
- > File: `.webqit/webflo/worker.json` | Command: `webflo config worker default_fetching_strategy=cache-only`
1490
- </details>
1641
+ *To list specific URLs...*
1491
1642
 
1492
- In all cases above, the convention for specifying URLs for a strategy accepts [URL patterns](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern) - against which URLs can be matched on the fly. For example, to place all files in an `/image` directory (and subdirectories) on the *Cache First* strategy, the pattern `/image/*` can be used. To place all `.svg` files in an `/icons` directory (including subdirectories) on the *Cache Only* strategy, the pattern `/icons/*.svg` can be used. (Specifically for the *Cache Only* strategy, patterns are resolved at Service Worker build-time, and each pattern must match, at least, a file.)
1643
+ ```json
1644
+ { "network_only_urls": [ "/logo.png" ] }
1645
+ ```
1646
+
1647
+ > **File: `.webqit/webflo/worker.json`**
1648
+
1649
+ > **Command: `webflo config worker default_fetching_strategy=network-only`**
1650
+ </details>
1651
+ </details>
1652
+
1653
+ <details>
1654
+ <summary>Cache Only</summary>
1655
+
1656
+ This strategy tells the Service Worker to always fetch given resources from the cache only. (This is good for resources that do not change often.) When not the default strategy, a list of specific URLs that should be fetched this way can be configured. The listed resources are pre-cached ahead of when they'll be needed - and are served from the cache each time. (Pre-caching happens on the one-time `install` event of the Service Worker.)
1657
+
1658
+ <details>
1659
+ <summary>Config</summary>
1660
+
1661
+ ```json
1662
+ { "default_fetching_strategy": "cache-only" }
1663
+ ```
1664
+
1665
+ *To list specific URLs...*
1666
+
1667
+ ```json
1668
+ { "cache_only_urls": [ "/logo.png" ] }
1669
+ ```
1670
+
1671
+ > **File: `.webqit/webflo/worker.json`**
1672
+
1673
+ > **Command: `webflo config worker default_fetching_strategy=cache-only`**
1674
+ </details>
1675
+ </details>
1676
+
1677
+ In all cases above, the convention for specifying URLs for a strategy accepts an [URL patterns](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern) - against which URLs can be matched on the fly. For example, to place all files in an `/image` directory (and subdirectories) on the *Cache First* strategy, the pattern `/image/*` can be used. To place all `.svg` files in an `/icons` directory (including subdirectories) on the *Cache Only* strategy, the pattern `/icons/*.svg` can be used. (Specifically for the *Cache Only* strategy, patterns are resolved at Service Worker build-time, and each pattern must match, at least, a file.)
1678
+
1679
+ <details>
1680
+ <summary>Example...</summary>
1493
1681
 
1494
1682
  ```json
1495
1683
  { "cache_only_urls": [ "/icons/*.svg" ] }
1496
1684
  ```
1685
+ </details>
1497
1686
 
1498
1687
  ##### Cross-Thread Communications
1499
1688
 
1500
1689
  A couple APIs exists in browsers for establishing a two-way communication channel between a page and its Service Worker, for firing UI Notifications from either ends, and for implementing Push Notifications. Webflo offers to simply this with a unifying set of conventions:
1501
1690
 
1502
- + The `workport` API - an object with simple methods for working with *cross-thread* messages, UI and Push Notifications.
1503
-
1504
- On both the client and worker side of your application, the `workport` object is accessible from route handlers as `this.runtime.workport`.
1505
-
1506
- ```js
1507
- /**
1508
- [client|worker]
1509
- β”œβ”€β”€ index.js
1510
- */
1511
- export default async function(event, context, next) {
1512
- let { workport } = this.runtime;
1513
- workport.messaging.post({ ... });
1514
- return { ... };
1515
- }
1516
- ```
1691
+ ###### The `workport` API
1517
1692
 
1518
- For cross-thread messaging, both sides of the API exposes the following methods:
1519
-
1520
- + **`.messaging.post()`** - for sending arbitrary data to the other side. E.g. `workport.messaging.post({ type: 'TEST' })`.
1521
- + **`.messaging.listen()`** - for listening to `message` event from the other side. E.g. `workport.messaging.listen(event => console.log(event.data.type))`. (See [`window: onmessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/message_event), [`worker: onmessage`](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/message_event).)
1522
- + **`.messaging.request()`** - for sending *replyable* messages to the other side, using the [MessageChannel](https://developer.mozilla.org/docs/Web/API/MessageChannel/MessageChannel) API.
1523
-
1524
- ```js
1525
- // On the worker side
1526
- workport.messaging.listen(event => {
1527
- console.log(event.data);
1528
- if (event.ports[0]) {
1529
- event.ports[0].postMessage({ type: 'WORKS' });
1530
- }
1531
- });
1532
- ```
1533
-
1534
- ```js
1535
- // On the client side
1536
- let response = await workport.messaging.request({ type: 'TEST' });
1537
- console.log(response); // { type: 'WORKS' }
1538
- ```
1539
-
1540
- + **`.messaging.channel()`** - for sending *broadcast* messages to the other side - including all other browsing contents that live on the same origin, using the [Broadcast Channel](https://developer.mozilla.org/docs/Web/API/Broadcast_Channel_API) API.
1541
-
1542
- ```js
1543
- // On the worker side
1544
- let channelId = 'channel-1';
1545
- workport.messaging.channel(channelId).listen(event => {
1546
- console.log(event.data);
1547
- });
1548
- ```
1549
-
1550
- ```js
1551
- // On the client side
1552
- let channelId = 'channel-1';
1553
- workport.messaging.channel(channelId).broadcast({ type: 'TEST' });
1554
- ```
1555
-
1556
- For [UI Nofitications](https://developer.mozilla.org/en-US/docs/Web/API/notification), both sides of the API exposes the following methods:
1557
-
1558
- + **`.nofitications.fire()`** - for firing up a UI notification. This uses the [`Nofitications constructor`](https://developer.mozilla.org/en-US/docs/Web/API/Notification/Notification), and thus, accepts the same arguments as the constructor. But it returns a `Promise` that resolves when the notification is *clicked* or *closed*, but rejects when the notification encounters an error, or when the application isn't granted the [notification permission](https://developer.mozilla.org/en-US/docs/Web/API/Notification/requestPermission).
1559
-
1560
- ```js
1561
- let title = 'Test Nofitication';
1562
- let options = { body: '...', icon: '...', actions: [ ... ] };
1563
- workport.nofitications.fire(title, options).then(event => {
1564
- console.log(event.action);
1565
- });
1566
- ```
1567
-
1568
- + **`.nofitications.listen()`** - (in Service-Workers) for listening to [`notificationclick`](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/notificationclick_event) events. (Handlers are called each time a notification is clicked.)
1569
-
1570
- ```js
1571
- workport.nofitications.listen(event => {
1572
- console.log(event.action);
1573
- });
1574
- ```
1575
-
1576
- For [Push Nofitications](https://developer.mozilla.org/en-US/docs/Web/API/Push_API), the client-side of the API exposes the following methods:
1577
-
1578
- + **`.push.subscribe()`** - the equivalent of the [`PushManager.subscribe()`](https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe) method. (But this can also take the *applicationServerKey* as a first argument, and other options as a second argument, in which case it automatically runs the key through an `urlBase64ToUint8Array()` function.)
1579
- + **`.push.unsubscribe()`** - the equivalent of the [`PushSubscription.unsubscribe()`](https://developer.mozilla.org/en-US/docs/Web/API/PushSubscription/unsubscribe) method.
1580
- + **`.push.getSubscription()`** - the equivalent of the [`PushManager.getSubscription()`](https://developer.mozilla.org/en-US/docs/Web/API/PushManager/getSubscription) method.
1693
+ This is an object with simple methods for working with *cross-thread* messages, UI and Push Notifications.
1581
1694
 
1582
- The worker-side of the API exposes the following methods:
1583
-
1584
- + **`.push.listen()`** - for listening to the [`push`](https://developer.mozilla.org/en-US/docs/Web/API/PushEvent) from within Service Workers. E.g. `workport.push.listen(event => console.log(event.data.type))`.
1695
+ On both the client and worker side of your application, the `workport` object is accessible from route handlers as `this.runtime.workport`.
1585
1696
 
1586
- + Route *events* - simple route events that fire when messaging and notification events happen.
1697
+ ```js
1698
+ /**
1699
+ [client|worker]
1700
+ β”œβ”€β”€ index.js
1701
+ */
1702
+ export default async function(event, context, next) {
1703
+ let { workport } = this.runtime;
1704
+ workport.messaging.post({ ... });
1705
+ return { ... };
1706
+ }
1707
+ ```
1708
+
1709
+ For cross-thread messaging, both sides of the API exposes the following methods:
1710
+
1711
+ <details>
1712
+ <summary>Method: <code>.messaging.post()</code></summary>
1713
+
1714
+ The `.messaging.post()` method is used for sending any arbitrary data to the other side. E.g. `workport.messaging.post({ type: 'TEST' })`.
1715
+ </details>
1716
+
1717
+ <details>
1718
+ <summary>Method: <code>.messaging.listen()</code></summary>
1719
+
1720
+ The `.messaging.listen()` method is used for registering a listener to the `message` event from the other side. E.g. `workport.messaging.listen(event => console.log(event.data.type))`. (See [`window: onmessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/message_event), [`worker: onmessage`](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/message_event).)
1721
+ </details>
1722
+
1723
+ <details>
1724
+ <summary>Method: <code>.messaging.request()</code></summary>
1725
+
1726
+ The `.messaging.request()` method is used for sending a message to the other side and obtaing a response, using the [MessageChannel](https://developer.mozilla.org/docs/Web/API/MessageChannel/MessageChannel) API.
1727
+
1728
+ ```js
1729
+ // On the worker side
1730
+ workport.messaging.listen(event => {
1731
+ console.log(event.data);
1732
+ if (event.ports[0]) {
1733
+ event.ports[0].postMessage({ type: 'WORKS' });
1734
+ }
1735
+ });
1736
+ ```
1737
+
1738
+ ```js
1739
+ // On the client side
1740
+ let response = await workport.messaging.request({ type: 'TEST' });
1741
+ console.log(response); // { type: 'WORKS' }
1742
+ ```
1743
+ </details>
1744
+
1745
+ <details>
1746
+ <summary>Method: <code>.messaging.channel()</code></summary>
1747
+
1748
+ The `.messaging.channel()` method is used for sending *broadcast* messages to the other side - including all other browsing contents that live on the same origin, using the [Broadcast Channel](https://developer.mozilla.org/docs/Web/API/Broadcast_Channel_API) API.
1749
+
1750
+ ```js
1751
+ // On the worker side
1752
+ let channelId = 'channel-1';
1753
+ workport.messaging.channel(channelId).listen(event => {
1754
+ console.log(event.data);
1755
+ });
1756
+ ```
1757
+
1758
+ ```js
1759
+ // On the client side
1760
+ let channelId = 'channel-1';
1761
+ workport.messaging.channel(channelId).broadcast({ type: 'TEST' });
1762
+ ```
1763
+ </details>
1764
+
1765
+ For [UI Nofitications](https://developer.mozilla.org/en-US/docs/Web/API/notification), both sides of the API exposes the following methods:
1766
+
1767
+ <details>
1768
+ <summary>Method: <code>.nofitications.fire()</code></summary>
1769
+
1770
+ The `.nofitications.fire()` method is used for firing up a UI notification. This uses the [`Nofitications constructor`](https://developer.mozilla.org/en-US/docs/Web/API/Notification/Notification), and thus, accepts the same arguments as the constructor. But it returns a `Promise` that resolves when the notification is *clicked* or *closed*, but rejects when the notification encounters an error, or when the application isn't granted the [notification permission](https://developer.mozilla.org/en-US/docs/Web/API/Notification/requestPermission).
1771
+
1772
+ ```js
1773
+ let title = 'Test Nofitication';
1774
+ let options = { body: '...', icon: '...', actions: [ ... ] };
1775
+ workport.nofitications.fire(title, options).then(event => {
1776
+ console.log(event.action);
1777
+ });
1778
+ ```
1779
+ </details>
1780
+
1781
+ <details>
1782
+ <summary>Method: <code>.nofitications.listen()</code></summary>
1783
+
1784
+ The `.nofitications.listen()` method (in Service-Workers) is used for listening to [`notificationclick`](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/notificationclick_event) events. (Handlers are called each time a notification is clicked.)
1785
+
1786
+ ```js
1787
+ workport.nofitications.listen(event => {
1788
+ console.log(event.action);
1789
+ });
1790
+ ```
1791
+ </details>
1792
+
1793
+ For [Push Nofitications](https://developer.mozilla.org/en-US/docs/Web/API/Push_API), the client-side of the API exposes the following methods:
1794
+
1795
+ <details>
1796
+ <summary>Method: <code>.push.subscribe()</code></summary>
1797
+
1798
+ The `.push.subscribe()` method is the equivalent of the [`PushManager.subscribe()`](https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe) method. (But this can also take the *applicationServerKey* as a first argument, and other options as a second argument, in which case it automatically runs the key through an `urlBase64ToUint8Array()` function.)
1799
+ </details>
1800
+
1801
+ <details>
1802
+ <summary>Method: <code>.push.unsubscribe()</code></summary>
1803
+
1804
+ The `.push.unsubscribe()` method is the equivalent of the [`PushSubscription.unsubscribe()`](https://developer.mozilla.org/en-US/docs/Web/API/PushSubscription/unsubscribe) method.
1805
+ </details>
1806
+
1807
+ <details>
1808
+ <summary>Method: <code>.push.getSubscription()</code></summary>
1809
+
1810
+ The `.push.getSubscription()` method is the equivalent of the [`PushManager.getSubscription()`](https://developer.mozilla.org/en-US/docs/Web/API/PushManager/getSubscription) method.
1811
+ </details>
1812
+
1813
+ The worker-side of the API exposes the following methods:
1814
+
1815
+ <details>
1816
+ <summary>Method: <code>.push.listen()</code></summary>
1817
+
1818
+ The `.push.listen()` method is for listening to the [`push`](https://developer.mozilla.org/en-US/docs/Web/API/PushEvent) from within Service Workers. E.g. `workport.push.listen(event => console.log(event.data.type))`.
1819
+ </details>
1820
+
1821
+ ###### Route *events*
1822
+
1823
+ These are simple route events that fire when messaging and notification events happen.
1824
+
1825
+ On both the client and worker side of your application, you can define an event listener alongside your *root* route handler. The event listener is called to handle all messaging and notification events that happen.
1826
+
1827
+ ```js
1828
+ /**
1829
+ [client|worker]
1830
+ β”œβ”€β”€ index.js
1831
+ */
1832
+ export default async function(event, context, next) {
1833
+ return { ... };
1834
+ }
1835
+ export async function alert(event, context, next) {
1836
+ return { ... };
1837
+ }
1838
+ ```
1839
+
1840
+ The event type is given in the `event.type` property. This could be:
1841
+
1842
+ + **`message`** - both client and worker side. For *replyable* messages, the event handler's return value is automatically sent back as response.
1843
+ + **`notificationclick`** - worker side.
1844
+ + **`push`** - worker side.
1845
+
1846
+ <details>
1847
+ <summary>Advanced...</summary>
1848
+
1849
+ The `next()` function could be used to delegate the handling of an event to step handlers where defined. This time, the path name must be given as a second argument to the call.
1850
+
1851
+ ```js
1852
+ /**
1853
+ worker
1854
+ β”œβ”€β”€ index.js
1855
+ */
1856
+ export async function alert(event, context, next) {
1857
+ if (event.type === 'push') {
1858
+ await next(context, '/services/push');
1859
+ return;
1860
+ }
1861
+ console.log(event.type);
1862
+ }
1863
+ ```
1864
+ </details>
1587
1865
 
1588
- On both the client and worker side of your application, you can define an event listener alongside your *root* route handler. The event listener is called to handle all messaging and notification events that happen.
1589
-
1590
- ```js
1591
- /**
1592
- [client|worker]
1593
- β”œβ”€β”€ index.js
1594
- */
1595
- export default async function(event, context, next) {
1596
- return { ... };
1597
- }
1598
- export async function alert(event, context, next) {
1599
- return { ... };
1600
- }
1601
- ```
1602
-
1603
- The event type is given in the `event.type` property. This could be:
1604
-
1605
- + **`message`** - both client and worker side. For *replyable* messages, the event handler's return value is automatically sent back as response.
1606
- + **`notificationclick`** - worker side.
1607
- + **`push`** - worker side.
1608
-
1609
- The `next()` function could be used to delegate the handling of an event to step handlers where defined. This time, the path name must be given as a second argument to the call.
1610
-
1611
- ```js
1612
- /**
1613
- worker
1614
- β”œβ”€β”€ index.js
1615
- */
1616
- export async function alert(event, context, next) {
1617
- if (event.type === 'push') {
1618
- await next(context, '/services/push');
1619
- return;
1620
- }
1621
- console.log(event.type);
1622
- }
1623
- ```
1624
-
1625
1866
  #### API Backends
1626
1867
 
1627
1868
  In Webflo, an API backend is what you, in essence, come off with with your server-side routes.
@@ -1694,16 +1935,15 @@ A simple tool, like [`staticgen`](https://github.com/tj/staticgen), or the basic
1694
1935
  }
1695
1936
  ```
1696
1937
 
1697
- > **Note**
1698
- > <br>Above, we used the `-P` flag to specify the output directory as `public`, the `-nv` flag to opt into β€œnon-verbose” mode which outputs less information, the `-r` flag to get it to crawl and download recursively, and the `-E` flag to get it to add the `.html` extension to generated files.
1699
-
1700
- You have a static site!
1938
+ <details>
1939
+ <summary>How it works...</summary>
1701
1940
 
1702
- ### Workflow API
1941
+ > Above, we used the `-P` flag to specify the output directory as `public`, the `-nv` flag to opt into β€œnon-verbose” mode which outputs less information, the `-r` flag to get it to crawl and download recursively, and the `-E` flag to get it to add the `.html` extension to generated files.
1942
+ </details>
1703
1943
 
1704
- > TODO
1944
+ *Happy static!*
1705
1945
 
1706
- ### Webflo Config
1946
+ ## Webflo Config
1707
1947
 
1708
1948
  Webflo comes *convention-first*! But it is entirely configurable for when you need it! The easiest way to do this is to run the command `webflo config` and follow the walkthrough. To simply get an overview, use the command `webflo config help`, and all commands and their description are shown.
1709
1949
 
@@ -1713,7 +1953,7 @@ Webflo applications are often built on/with the following technologies.
1713
1953
 
1714
1954
  ### OOHTML
1715
1955
 
1716
- [OOHTML](https://github.com/webqit/oohtml) is a proposed set of new features for HTML that makes it fun to hand-author your UI! Within OOHTML are [HTML Modules](https://github.com/webqit/oohtml#html-modules) and [HTML Imports](https://github.com/webqit/oohtml#html-imports), [Reactive Scripts](https://github.com/webqit/oohtml#subscript) and more!
1956
+ [OOHTML](https://github.com/webqit/oohtml) is a proposed set of new features for HTML that makes it fun to hand-author your HTML documents! Within OOHTML are [HTML Modules](https://github.com/webqit/oohtml#html-modules) and [HTML Imports](https://github.com/webqit/oohtml#html-imports), [Reactive Scripts](https://github.com/webqit/oohtml#subscript) and more!
1717
1957
 
1718
1958
  Webflo natively supports OOHTML in full! But it is also possible to switch this to none, or to partial support - when specific features aren't needed anywhere in your application. Server-side and client-side support for OOHTML exist independently. This is good when, for example, your application places more importance on SSR, and less on CSR, in which case a reduced support for OOHTML can reduce the overall client JS bundle size.
1719
1959
 
@@ -1726,7 +1966,9 @@ Webflo natively supports OOHTML in full! But it is also possible to switch this
1726
1966
 
1727
1967
  *Values: `full`, `namespacing`, `scripting`, `templating`, `none` - See [details at OOHTML SSR](https://github.com/webqit/oohtml-ssr#options)*
1728
1968
 
1729
- > File: `.webqit/webflo/client.json` | Command: `webflo config client oohtml_support=full`
1969
+ > **File: `.webqit/webflo/client.json`**
1970
+
1971
+ > **Command: `webflo config client oohtml_support=full`**
1730
1972
 
1731
1973
  > File: `.webqit/webflo/server.json` | Command: `webflo config server oohtml_support=full`
1732
1974
  </details>
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.11.11",
15
+ "version": "0.11.14",
16
16
  "license": "MIT",
17
17
  "repository": {
18
18
  "type": "git",