@webqit/webflo 0.11.4 → 0.11.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -135,13 +135,13 @@ For when your application involves routing:
135
135
  + [WHATWG URL](https://url.spec.whatwg.org/) and [WHATWG URLPattern](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern) are used for all things *URL* and *URL pattern matching*, respectively - across client, server, and Service Worker environments. ([Details ahead](#))
136
136
 
137
137
  For when your application involves pages and a UI:
138
- + [The HTML Standard](https://html.spec.whatwg.org/) is held for all things *markup* - across client, server, and Service Worker environments! Webflo is all about using conventional `.html`-based pages and templates, valid HTML syntax, etc. You are able to get away with a "zero-JavaScript" proposition or with *Progressive Enhancement* that makes do with "just-enough JavaScript"!
138
+ + [The HTML Standard](https://html.spec.whatwg.org/) is held for all things *markup* - across client, server, and Service Worker environments! Webflo is all about using conventional `.html`-based pages and templates, valid HTML syntax, etc. You are able to get away with a "zero-JavaScript" proposition, or a *Progressive Enhancement* proposition that makes do with "just-enough JavaScript"!
139
139
 
140
- > Your markup is also easily extendable with [OOHTML](https://github.com/webqit/oohtml) - a set of new features for HTML that makes it fun to hand-author your UI! Within OOHTML are [HTML Modules (`<template name="partials"></template>`)](https://github.com/webqit/oohtml#html-modules) and [HTML Imports (`<import template="partials"></import>`)](https://github.com/webqit/oohtml#html-imports), [Reactive Scripts (`<script type="subscript"></script>`)](https://github.com/webqit/oohtml#subscript) and more!
140
+ > Your markup is also easily extendable with [OOHTML](https://github.com/webqit/oohtml) - a 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!
141
141
 
142
142
  + [WHATWG DOM](https://dom.spec.whatwg.org/) is universally available - not only on the client-side, but also on the server-side via [OOHTML-SSR](https://github.com/webqit/oohtml-ssr) - for all things *dynamic pages*: rendering, manipulation, interactivity, etc.
143
143
 
144
- > Your DOM is also easily enrichable with [Custom Elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements), plus [Subscript Elements](https://github.com/webqit/oohtml#subscript) and [The State API (`document.state` and `element.state`)](https://github.com/webqit/oohtml#state-api) from OOHTML.
144
+ > Your DOM is also easily enrichable with [Custom Elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements), plus [Subscript Elements](https://github.com/webqit/oohtml#subscript) and [The State API](https://github.com/webqit/oohtml#state-api) from OOHTML.
145
145
 
146
146
  For when your application needs to give an app-like experience:
147
147
  + [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API), extended with full support for routing, come into play for offline and [Progressive Web Apps (PWA)](https://web.dev/progressive-web-apps/) capabilities.
@@ -153,7 +153,7 @@ This and more - ahead! For building web-native apps!
153
153
 
154
154
  ## Installation
155
155
 
156
- Every Webflo project starts on an empty directory that you can create on your machine. The command below will make a new directory `my-app` from the terminal and navigate into it.
156
+ Every Webflo project starts on an empty directory that you can create on your machine. The command below makes a new directory `my-app` from the terminal and navigates into it.
157
157
 
158
158
  ```shell
159
159
  mkdir my-app
@@ -198,7 +198,7 @@ All is now set! The commands `npm start` and `npm run generate` will be coming i
198
198
 
199
199
  ### "Hello World!"
200
200
 
201
- To be sure that Webflo is listening, run `npx webflo help` on the terminal. An overview of available commands will be shown.
201
+ To be sure that Webflo is listening, run `npx webflo help` on the terminal. An overview of available commands should be shown.
202
202
 
203
203
  If you can't wait to say *Hello World!* 😅, you can have an HTML page say that right now!
204
204
  + Create an `index.html` file in a new subdirectory `public`.
@@ -238,7 +238,7 @@ If you can't wait to say *Hello World!* 😅, you can have an HTML page say that
238
238
 
239
239
  ### Handler Functions and Layout
240
240
 
241
- Whether building a *server-based*, *browser-based*, or *universal* application, Webflo gives us one consistent way to handle routing and navigation: using *handler functions*!
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*!
242
242
 
243
243
  ```js
244
244
  /**
@@ -249,7 +249,7 @@ export default function(event, context, next) {
249
249
  }
250
250
  ```
251
251
 
252
- Each function receives an `event` object representing details - e.g. `event.request`, `event.url`, `event.session` - about the current flow. (Details ahead.)
252
+ Each function receives an `event` object representing details - e.g. `event.request`, `event.url`, `event.session` - about the current request. (Details ahead.)
253
253
 
254
254
  For *server-based* applications (e.g. traditional web apps and API backends), server-side handlers go into a directory named `server`.
255
255
 
@@ -372,7 +372,7 @@ export default function(event, context, next) {
372
372
 
373
373
  This step-based workflow helps to decomplicate routing and gets us scaling horizontally as our application grows larger.
374
374
 
375
- Workflows may be designed with *wildcard* steps using a hyphen `-` as step name. Wildcard steps match all paths at the given level of the route! A `this.stepname` property can always be used to see the current URL step that matched.
375
+ Workflows may be designed with *wildcard* steps using a hyphen `-` as step name. At runtime, a wildcard step matches any URL segment at its level in the layout! A `this.stepname` property could be used to see which URL segment has been matched.
376
376
 
377
377
  ```js
378
378
  /**
@@ -424,7 +424,7 @@ Webflo takes a *default action* when `next()` is called at the *edge* of the wor
424
424
 
425
425
  For workflows in **the `/server` directory**, the *default action* of `next()`ing at the edge is to go match and return a static file in the `public` directory.
426
426
 
427
- So, above, should our handler receive static file requests like `http://localhost:3000/logo.png`, the expression `return next()` would get Webflo to match and return the logo at `public/logo.png`, if any; a `404` response otherwise.
427
+ So, above, should our handler receive static file requests like `http://localhost:3000/logo.png`, the statement `return next()` would get Webflo to match and return the logo at `public/logo.png`, if any; a `404` response otherwise.
428
428
 
429
429
  ```shell
430
430
  my-app
@@ -433,7 +433,7 @@ my-app
433
433
  ```
434
434
 
435
435
  > **Note**
436
- > <br>The root handler effectively becomes the single point of entry to the application - being that it sees even static requests!
436
+ > <br>The root handler effectively becomes the single point of entry to the application - being that it sees even requests for static files!
437
437
 
438
438
  Now, for workflows in **the `/worker` directory**, the *default action* of `next()`ing at the edge is to send the request through the network to the server. (But Webflo will know to attempt resolving the request from the application's caching system built into the Service Worker.)
439
439
 
@@ -703,7 +703,7 @@ public/products
703
703
 
704
704
  #### In a Single Page Layout
705
705
 
706
- In a Single Page layout (as [above](#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 to form the equivalent of their URL structure.
706
+ 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.
707
707
 
708
708
  ```html
709
709
  <!--
@@ -836,7 +836,7 @@ The `--auto-embed` flag gets the bundler to automatically embed the generated `b
836
836
 
837
837
  With pages in Webflo being [DOM-based](#overview) (both client-side and [server-side](https://github.com/webqit/oohtml-ssr)), we are able to access and manipulate documents and elements using familiar DOM APIs - e.g. to replace or insert contents, attributes, etc. Rendering in Webflo is based on this concept!
838
838
 
839
- Here, Webflo simply makes sure that the data obtained from each route is available as part of the `document` object, such that it is accessible to our rendering logic as a `data` property on the `document.state` object - `document.state.data`. (The `document.state` object is always available unless disabled in config.)
839
+ Here, Webflo simply makes sure that the data obtained from each route is available as part of the `document` object, such that it is accessible to our rendering logic as a `data` property on the [`document.state`](#the-idea-of-state) object - [`document.state.data`](#the-documentstatedata-object).
840
840
 
841
841
  So, we could embed a script on our page and render this data on the relevant parts of the document.
842
842
 
@@ -929,7 +929,9 @@ Going forward, we can get to write more succinct code! Using the [Namespaced HTM
929
929
  document.title = title;
930
930
  let { headline1, headline2 } = this.namespace;
931
931
  $(headline1).html(title);
932
- $(headline2).html(title);
932
+ if (headline2) {
933
+ $(headline2).html(title);
934
+ }
933
935
  </script>
934
936
  </body>
935
937
  </html>
@@ -1021,16 +1023,122 @@ export async function render(event, data, next) {
1021
1023
 
1022
1024
  Custom render functions must return a value, and `window` objects are accepted. (Actually, any object that has a `toString()` method can be returned.)
1023
1025
 
1026
+ #### The Idea of State
1027
+
1028
+ There often needs to be a central point in an application where things are stored and managed. You could think of it is having a global object initialized `window.store = {}` on which different parts of an application can store and retrieve values. This is the basic idea of state. But it also doesn't go without the idea of *observability* - something that lets the different parts of the application observe and respond to changes made on this object!
1029
+
1030
+ *State* and *Observability* in Webflo applications come down to this basic form: there is an object...
1031
+
1032
+ ```js
1033
+ state = {}
1034
+ ```
1035
+
1036
+ ...and there is a way to observe property changes on it...
1037
+
1038
+ ```js
1039
+ Observer.observe(state, changes => {
1040
+ changes.forEach(change => {
1041
+ console.log(change.name, change.value);
1042
+ });
1043
+ });
1044
+ ```
1045
+
1046
+ ```js
1047
+ Observer.observe(state, propertyName, change => {
1048
+ console.log(change.name, change.value);
1049
+ });
1050
+ ```
1051
+
1052
+ ...plus, all references to the object and its properties from within embedded Subscript code are reactive.
1053
+
1054
+ ```html
1055
+ <script type="subscript">
1056
+ // Always log the value of this property in realtime
1057
+ console.log(state.propertyName);
1058
+ </script>
1059
+ ```
1060
+
1061
+ This way, all the moving parts of your application remain coordinated, and can easily be rendered to reflect them on the UI!
1062
+
1063
+ 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](https://github.com/webqit/observer). It comes off as the simplest approach to state and reactivity!
1064
+
1065
+ > **Note**
1066
+ > <br>The State API is not available when the OOHTML support level in config is switched away from `full` and `scripting`.
1067
+
1068
+ #### The `document.state.data` Object
1069
+
1070
+ 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.
1071
+
1072
+ ```js
1073
+ Observer.observe(document.state, 'data', e => {
1074
+ console.log('Current page data is: ', e.value);
1075
+ });
1076
+ ```
1077
+
1078
+ ```html
1079
+ <script type="subscript">
1080
+ let { title } = document.state.data;
1081
+ document.title = title;
1082
+ </script>
1083
+ ```
1084
+
1085
+ #### The `document.state.url` Object
1086
+
1087
+ This is a *live* object that reperesents the properties of the application URL at any point in time. The object exposes the same URL properties as with the [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) API, but as *live* properties that can be observed as navigation happens, and modified to initiate navigation - all using the [Observer API](https://github.com/webqit/observer).
1088
+
1089
+ ```js
1090
+ console.log(document.state.url) // { hash, host, hostname, href, origin, password, pathname, port, protocol, search, searchParams, username }
1091
+ ```
1092
+
1093
+ ```js
1094
+ Observer.observe(document.state.url, 'hash', e => {
1095
+ console.log(document.state.url.hash === e.value); // true
1096
+ });
1097
+ ```
1098
+
1099
+ ```js
1100
+ // Navigates to "/login#form" as if a link was clicked
1101
+ document.addEventListener('synthetic-navigation', e => {
1102
+ Observer.set(document.state.url, 'href', '/login#form');
1103
+ });
1104
+
1105
+ // Or...
1106
+ document.addEventListener('synthetic-navigation', e => {
1107
+ Observer.set(document.state.url, { pathname: '/login', hash: '#form' });
1108
+ });
1109
+
1110
+ console.log(document.state.url.hash); // #form
1111
+ ```
1112
+
1113
+ There is also the *convenience* `query` property that offers the URL parameters as a *live* object.
1114
+
1115
+ ```js
1116
+ // For URL: http://localhost:3000/login?as=student
1117
+ console.log(document.state.url.query.as) // student
1118
+
1119
+ // Re-rewrite the URL and initiate navigation by simply modifying a query parameter
1120
+ document.addEventListener('synthetic-navigation', e => {
1121
+ Observer.set(document.state.url.query, 'as', 'business');
1122
+ });
1123
+ ```
1124
+
1125
+ ```html
1126
+ <script type="subscript">
1127
+ let { query: { as: role } } = document.state.url;
1128
+ document.title = 'Login as ' + role;
1129
+ </script>
1130
+ ```
1131
+
1024
1132
  ### Requests and Responses
1025
1133
 
1026
- On each request, the event object passed to route handlers exposes the incoming request as `event.request`. This is an instance of `event.Request` - an extension of the [WHATWG Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) class. The event object also exposes `event.Response` - an extension of the [WHATWG Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) class, for returning instance-based responses.
1134
+ On each request, the event object passed to route handlers exposes the incoming request as `event.request`. This is an instance of `event.Request` - an extension of the [WHATWG Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) class. The event object also exposes `event.Response` - an extension of the [WHATWG Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) class, for returning instance-based responses. You enjoy routing that is based on standard interfaces!
1027
1135
 
1028
- Now, routes in Webflo can be designed for different types of request/response scenarios. Webflo does the heavy lifting on each request/response flow!
1136
+ Routes in Webflo can be designed for different types of request/response scenarios. Here are some important ones:
1029
1137
 
1030
1138
  #### Scenario 1: Static File Requests and Responses
1031
1139
 
1032
1140
  Static file requests like `http://localhost:3000/logo.png` are expected to get a file response. These requests are automatically handled by Webflo when `next()`ed forward by route handlers, or where there are no route handlers.
1033
- + On the server, Webflo serves files from the `public` directory. File conents along with the appropriate headers like [`Content-Type`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type), [`Content-Length`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Length), etc. are returned as an instance of `event.Response`. Where a request has an [`Accept-Encoding`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding) header set (e.g. `gzip`, `br`) and there exists a matching *compressed version* of the said file on the file system (e.g. `./public/logo.png.gz`, `./public/logo.png.br`), the compressed version is served and the appropriate [`Content-Encoding`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding) response header is set.
1141
+ + On the server, Webflo serves files from the `public` directory. File contents along with the appropriate headers like [`Content-Type`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type), [`Content-Length`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Length), etc. are returned as an instance of `event.Response`. Where a request has an [`Accept-Encoding`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding) header set (e.g. `gzip`, `br`) and there exists a matching *compressed version* of the said file on the file system (e.g. `./public/logo.png.gz`, `./public/logo.png.br`), the compressed version is served and the appropriate [`Content-Encoding`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding) response header is set.
1034
1142
  + On the client, Webflo serves static files from the network, or from the application cache, where available.
1035
1143
 
1036
1144
  #### Scenario 2: API Requests and Responses
@@ -1120,81 +1228,22 @@ Webflo also offers a *convenience* method.
1120
1228
 
1121
1229
  ```js
1122
1230
  console.log(event.request.headers.cookies); // { 'Cookie-1': 'cookie-val', 'Cookie-2': 'cookie2-val' };
1123
- ````
1231
+ ```
1124
1232
 
1125
1233
  ### Webflo Applications
1126
1234
 
1127
- In just a few concepts, Webflo comes ready for any type of application! Now, additional details of a Webflo app - depending on the type - are covered in the following sections.
1235
+ In just a few concepts, Webflo comes ready for any type of application!
1128
1236
 
1129
- + [Application State](#application-state)
1130
1237
  + [Client-Side Applications](#client-side-applications)
1238
+ + [Progressive Web Apps](#progressive-web-apps)
1131
1239
  + [API Backends](#api-backends)
1132
1240
  + [Static Sites](#static-sites)
1133
1241
 
1134
- #### Application State
1135
-
1136
- 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](https://github.com/webqit/observer). It comes off as the simplest approach to state and reactivity!
1137
-
1138
- > **Note**
1139
- > <br>The State API is not available when the OOHTML support level in config is switched away from `full` and `scripting`.
1140
-
1141
- ##### The `document.state.data` Object
1142
-
1143
- This property represents the data obtained from route handers on each navigation. Webflo simply exposes this data and lets the page's [rendering logic](#client-and-server-side-rendering), or other parts of the application, take over.
1144
-
1145
- ```js
1146
- Observer.observe(document.state, 'data', e => {
1147
- console.log('Current page data is: ', e.value);
1148
- });
1149
- ```
1150
-
1151
- ##### The `document.state.url` Object
1152
-
1153
- This is a *live* object that reperesents the properties of the application URL at any point in time. The object exposes the same URL properties as with the [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) API, but as *live* properties that can be observed as navigation happens, and modified to initiate navigation - all using the [Observer API](https://github.com/webqit/observer).
1154
-
1155
- ```js
1156
- console.log(document.state.url) // { hash, host, hostname, href, origin, password, pathname, port, protocol, search, searchParams, username }
1157
- ```
1158
-
1159
- ```js
1160
- Observer.observe(document.state.url, 'hash', e => {
1161
- console.log(document.state.url.hash === e.value); // true
1162
- });
1163
- ```
1164
-
1165
- ```js
1166
- // Navigates to "/login#form" as if a link was clicked
1167
- document.addEventListener('synthetic-navigation', e => {
1168
- Observer.set(document.state.url, 'href', '/login#form');
1169
- });
1170
-
1171
- // Or...
1172
- document.addEventListener('synthetic-navigation', e => {
1173
- Observer.set(document.state.url, { pathname: '/login', hash: '#form' });
1174
- });
1175
-
1176
- console.log(document.state.url.hash); // #form
1177
- ```
1178
-
1179
- There is also the *convenience* `query` property that offers the URL parameters as a *live* object.
1180
-
1181
- ```js
1182
- // For URL: http://localhost:3000/login?as=student
1183
- console.log(document.state.url.query.as) // student
1184
-
1185
- // Re-rewrite the URL and initiate navigation by simply modifying a query parameter
1186
- document.addEventListener('synthetic-navigation', e => {
1187
- Observer.set(document.state.url.query, 'as', 'business');
1188
- });
1189
- ```
1190
-
1191
1242
  #### Client-Side Applications
1192
1243
 
1193
- Web pages that embed the Webflo client JS bundle deliver a great user experience.
1194
- + **First-paint-ready.** On the first page request, you get a [server-rendered](#client-and-server-side-rendering) HTML page that's optimized for the first paint of your application.
1195
- + **Fluid and app-like.** On being loaded, the state of the application is restored through hydration, and [subsequent navigations](#spa-navigation) are sleek and instant, while performing [Client-Side Rendering](#client-and-server-side-rendering).
1244
+ Web pages that embed the Webflo client JS bundle deliver a great user experience. It's simple: the `npm run generate` command does both the building and embedding of the script, or scripts, for the document root, or document roots (in a [Multi Page](#in-a-multi-page-layout) / [Multi SPA](#in-a-multi-spa-layout) layout)!
1196
1245
 
1197
- For these client-side applications, the `npm run generate` command does both the building and embedding of the script for each document root in the application.
1246
+ On being loaded, the state of the application is initialized, or is restored through hydration - where [Server-Side Rendering](#client-and-server-side-rendering) was involved to optimize for first paint, and an app-like experience kicks in! For [Single-Page Applications](#in-a-single-page-layout), [Client-Side Rendering](#client-and-server-side-rendering) is performed on each navigation.
1198
1247
 
1199
1248
  ##### SPA Navigation
1200
1249
 
@@ -1208,23 +1257,84 @@ Unless disabled in [config](#spa_navigation), it is factored-in at build time fo
1208
1257
 
1209
1258
  ##### SPA State
1210
1259
 
1211
- In addition to [the universal concept of state](#application-state) of a Webflo application, state on the client side also includes the following aspects of the client-side lifecycle that can be used to provide visual cues on the UI.
1260
+ On the client side of a Webflo application, [the idea of state](#the-idea-of-state) also includes the following aspects of the client-side lifecycle that can be used to provide visual cues on the UI.
1212
1261
 
1213
1262
  ###### The `document.state.network` Object
1214
1263
 
1215
1264
  This is a *live* object that exposes the network activity and network state of the application.
1216
1265
 
1217
1266
  ```js
1218
- console.log(document.state.network) // { requesting, remote, error, redirecting, online, }
1267
+ console.log(document.state.network) // { requesting, remote, error, redirecting, connectivity, }
1219
1268
  ```
1220
1269
 
1221
1270
  + **`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.
1271
+
1272
+ On the UI, this could be used to hide a menu drawer that may have been open.
1273
+
1274
+ ```html
1275
+ <menu-drawer>
1276
+ <script type="subscript">
1277
+ let { network: { requesting } } = document.state;
1278
+ if (requesting) {
1279
+ $(this).attr('open', false);
1280
+ }
1281
+ </script>
1282
+ </menu-drawer>
1283
+ ```
1284
+
1222
1285
  + **`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.
1286
+
1287
+ On the UI, this could be used to show/hide a spinner, or progress bar, to provide a visual cue.
1288
+
1289
+ ```html
1290
+ <progress-bar>
1291
+ <script type="subscript">
1292
+ let { network: { remote } } = document.state;
1293
+ $(this).attr('hidden', !remote);
1294
+ </script>
1295
+ </progress-bar>
1296
+ ```
1297
+
1223
1298
  + **`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.
1299
+
1300
+ On the UI, this could be used to show/hide cute error elements.
1301
+
1302
+ ```html
1303
+ <nice-error>
1304
+ <script type="subscript">
1305
+ let { network: { error } } = document.state;
1306
+ $(this).attr('hidden', !error);
1307
+ </script>
1308
+ </nice-error>
1309
+ ```
1310
+
1224
1311
  + **`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.
1225
- + **`network.online`: `Boolean`** - This property tells of [the browser's ability to connect to the network](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine).
1226
-
1227
- Now, being a *live* object means that `document.state.network` can be observed using the [Observer API](https://github.com/webqit/observer).
1312
+
1313
+ On the UI, this could be used to prevent further interactions with the outgoing page.
1314
+
1315
+ ```html
1316
+ <body>
1317
+ <script type="subscript">
1318
+ let { network: { redirecting } } = document.state;
1319
+ $(this).css(redirecting ? { pointerEvents: 'none', filter: 'blur(2)' } : { pointerEvents: 'auto', filter: 'blur(0)' });
1320
+ </script>
1321
+ </body>
1322
+ ```
1323
+
1324
+ + **`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`.
1325
+
1326
+ On the UI, this could be used to show/hide a connectivity status.
1327
+
1328
+ ```html
1329
+ <body>
1330
+ <script type="subscript">
1331
+ let { network: { connectivity } } = document.state;
1332
+ $(this).attr( 'connectivity', connectivity });
1333
+ </script>
1334
+ </body>
1335
+ ```
1336
+
1337
+ Here are some additional examples with the [Observer API](https://github.com/webqit/observer).
1228
1338
 
1229
1339
  ```js
1230
1340
  // Visualize the network state
@@ -1238,12 +1348,12 @@ Observer.observe(document.state.network, onlineVisualizer);
1238
1348
  ```
1239
1349
 
1240
1350
  ```js
1241
- // Visualize the 'online' property
1242
- let onlineVisualizer = e => {
1243
- console.log('You are ', e.value ? 'online' : 'offline');
1351
+ // Visualize the 'connectivity' property
1352
+ let connectivityVisualizer = e => {
1353
+ console.log('You are ', e.value);
1244
1354
  };
1245
- Observer.observe(document.state.network, 'online', onlineVisualizer);
1246
- // Or: Observer.observe(document.state, [ ['network', 'online'] ], onlineVisualizer);
1355
+ Observer.observe(document.state.network, 'connectivity', connectivityVisualizer);
1356
+ // Or: Observer.observe(document.state, [ ['network', 'connectivity'] ], connectivityeVisualizer);
1247
1357
  ```
1248
1358
 
1249
1359
  ```js
@@ -1258,15 +1368,32 @@ Observer.observe(document.state.network, 'error', e => {
1258
1368
  });
1259
1369
  ```
1260
1370
 
1261
- ###### Form Actions
1371
+ ##### Form Actions
1262
1372
 
1263
- When navigation occurs [via form submissions](#scenario-4-single-page-navigation-requests-and-responses), the form element and the submit button are made to go on the *active* state while the request is processed. For both of these elements, the Webflo client simply sets the `element.state.active` to `true` on submission, then `false`, on completion.
1373
+ When navigation occurs [via form submissions](#scenario-4-single-page-navigation-requests-and-responses), the form element and the submit button are made to go on the *active* state while the request is being processed. For both of these elements, the Webflo client simply sets the `element.state.active` to `true` on submission, then `false`, on completion.
1264
1374
 
1265
- ##### Service Workers
1375
+ ```html
1376
+ <form method="post">
1377
+ <input name="username" placeholder="Your username..." />
1378
+ <script>
1379
+ $(this).css(this.state.active ? { pointerEvents: 'none', opacity: 'o.5' } : { pointerEvents: 'auto', opacity: '1' });
1380
+ </script>
1381
+ </form>
1382
+ ```
1266
1383
 
1267
- Webflo client-side applications are intended to provide an app-like-first experience. So unless disabled in [config](#enable_service_worker), a [Service Worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) is built as part of your application on running the `npm run generate` command. You may define [route handlers in the `/worker` directory](#handler-functions-and-layout) of your application, and these will be built into the service worker to handle Same-Origin requests of the application. Where there are no *worker* handlers, or where they forward these requests, the request is fetched, either from the cache, or from the network, depending on the fetching strategy built into the Service Worker.
1384
+ One more thing: HTML forms can only accept two HTTP methods on their `method` attribute: `GET`, `POST`! The same constraint exists on the equivalent `formmethod` attribue in submit buttons. You are able to overcome this in Webflo by using alternative `data-` attributes: `data-method`, `data-formmethod`, respectively.
1268
1385
 
1269
- ###### Fetching Strategy
1386
+ ```html
1387
+ <form data-method="patch">
1388
+ <input name="price" placeholder="Enter new price..." />
1389
+ </form>
1390
+ ```
1391
+
1392
+ #### Progressive Web Apps
1393
+
1394
+ Webflo client-side applications are intended to provide an app-like-first experience. So unless disabled in [config](#enable_service_worker), a [Service Worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) is built as part of your application on running the `npm run generate` command. You may define [route handlers in the `/worker` directory](#handler-functions-and-layout) of your application, and these will be built into the service worker to handle Same-Origin requests of the application. Where there are no *worker* handlers, or where these forward incoming requests, requests are fetched, either from the cache, or from the network, depending on the fetching strategy built into the Service Worker.
1395
+
1396
+ ##### Fetching Strategy
1270
1397
 
1271
1398
  + **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](#default_fetching_strategy), 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](#network_first_urls).
1272
1399
  + **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](#cache_first_urls).
@@ -1275,9 +1402,9 @@ Webflo client-side applications are intended to provide an app-like-first experi
1275
1402
 
1276
1403
  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.)
1277
1404
 
1278
- ###### Cross-Thread Communications
1405
+ ##### Cross-Thread Communications
1279
1406
 
1280
- 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:
1407
+ 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:
1281
1408
 
1282
1409
  + The `workport` API - an object with simple methods for working with *cross-thread* messages, UI and Push Notifications.
1283
1410
 
@@ -1417,7 +1544,7 @@ export default function(event, context, next) {
1417
1544
  }
1418
1545
  ```
1419
1546
 
1420
- You are always able to lay out your route handlers in the structure for a formalized REST API.
1547
+ You are always able to lay out your route handlers in the structure for a formal REST API.
1421
1548
 
1422
1549
  ```shell
1423
1550
  server
@@ -1426,15 +1553,15 @@ server
1426
1553
  └── api/v1/products/index.js
1427
1554
  ```
1428
1555
 
1429
- And if you will partition your backend for both page routes and a formalized REST API...
1556
+ And if you will partition your backend for both page routes and a formal REST API...
1430
1557
 
1431
1558
  ```shell
1432
1559
  server
1433
1560
  ├── index.js ──┐
1434
- ├── cart/index.js ├── Page Routes
1561
+ ├── cart/index.js ├─ Page Routes
1435
1562
  ├── products/index.js ──┘
1436
1563
  ├── api/v1/index.js ──┐
1437
- ├── api/v1/orders/index.js ├── REST API
1564
+ ├── api/v1/orders/index.js ├─ REST API
1438
1565
  └── api/v1/products/index.js ──┘
1439
1566
  ```
1440
1567
 
@@ -1500,12 +1627,6 @@ You could soon be taking all your ideas to Webflo! 😃
1500
1627
 
1501
1628
  All forms of contributions and PR are welcome! To report bugs or request features, please submit an [issue](https://github.com/webqit/webflo/issues). For general discussions, ideation or community help, please join our github [Discussions](https://github.com/webqit/webflo/discussions).
1502
1629
 
1503
- ## License
1504
-
1505
- MIT.
1506
-
1507
- ...
1508
-
1509
1630
  ## Getting Involved
1510
1631
 
1511
1632
  All forms of contributions and PR are welcome! To report bugs or request features, please submit an [issue](https://github.com/webqit/webflo/issues).
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.4",
15
+ "version": "0.11.7",
16
16
  "license": "MIT",
17
17
  "repository": {
18
18
  "type": "git",
@@ -118,7 +118,7 @@ export default class Runtime {
118
118
  return params;
119
119
  }, {});
120
120
  // We support method hacking
121
- submitParams.method = (submitter && submitter.dataset.method) || form.dataset.method || submitParams.method;
121
+ submitParams.method = (submitter && submitter.dataset.formmethod) || form.dataset.method || submitParams.method;
122
122
  submitParams.submitter = submitter;
123
123
  // ---------------
124
124
  var actionEl = window.document.createElement('a');
@@ -152,13 +152,13 @@ export default class Runtime {
152
152
  // -----------------------
153
153
  // Initialize network
154
154
  Observer.set(this, 'network', {});
155
- window.addEventListener('online', () => Observer.set(this.network, 'online', navigator.onLine));
156
- window.addEventListener('offline', () => Observer.set(this.network, 'online', navigator.onLine));
155
+ window.addEventListener('online', () => Observer.set(this.network, 'connectivity', 'online'));
156
+ window.addEventListener('offline', () => Observer.set(this.network, 'connectivity', 'offline'));
157
157
 
158
158
  // -----------------------
159
159
  // Service Worker && COMM
160
- if (this.cx.service_worker_support) {
161
- let workport = new Workport(this.cx.worker_filename, { scope: this.cx.worker_scope, startMessages: true });
160
+ if (this.cx.params.service_worker_support) {
161
+ let workport = new Workport(this.cx.params.worker_filename, { scope: this.cx.params.worker_scope, startMessages: true });
162
162
  Observer.set(this, 'workport', workport);
163
163
  workport.messaging.listen(async evt => {
164
164
  let responsePort = evt.ports[0];