@webqit/webflo 0.11.5 → 0.11.6
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 +112 -76
- package/package.json +1 -1
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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 [
|
|
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
|
|
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
|
|
|
@@ -1021,6 +1021,98 @@ export async function render(event, data, next) {
|
|
|
1021
1021
|
|
|
1022
1022
|
Custom render functions must return a value, and `window` objects are accepted. (Actually, any object that has a `toString()` method can be returned.)
|
|
1023
1023
|
|
|
1024
|
+
#### The Idea of State
|
|
1025
|
+
|
|
1026
|
+
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!
|
|
1027
|
+
|
|
1028
|
+
*State* and *Observability* in Webflo applications come down to this basic form: there is an object...
|
|
1029
|
+
|
|
1030
|
+
```js
|
|
1031
|
+
state = {}
|
|
1032
|
+
```
|
|
1033
|
+
|
|
1034
|
+
...and there is a way to observe property changes on it...
|
|
1035
|
+
|
|
1036
|
+
```js
|
|
1037
|
+
Observer.observe(state, changes => {
|
|
1038
|
+
changes.forEach(change => {
|
|
1039
|
+
console.log(change.name, change.value);
|
|
1040
|
+
});
|
|
1041
|
+
});
|
|
1042
|
+
```
|
|
1043
|
+
|
|
1044
|
+
```js
|
|
1045
|
+
Observer.observe(state, propertyName, change => {
|
|
1046
|
+
console.log(change.name, change.value);
|
|
1047
|
+
});
|
|
1048
|
+
```
|
|
1049
|
+
|
|
1050
|
+
...plus, all references to the object and its properties from within embedded Subscript code are reactive.
|
|
1051
|
+
|
|
1052
|
+
```html
|
|
1053
|
+
<script type="subscript">
|
|
1054
|
+
// Always log the value of this property in realtime
|
|
1055
|
+
console.log(state.propertyName);
|
|
1056
|
+
</script>
|
|
1057
|
+
```
|
|
1058
|
+
|
|
1059
|
+
This way, all the moving parts of your application remain coordinated, and can easily be rendered to reflect them on the UI!
|
|
1060
|
+
|
|
1061
|
+
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!
|
|
1062
|
+
|
|
1063
|
+
> **Note**
|
|
1064
|
+
> <br>The State API is not available when the OOHTML support level in config is switched away from `full` and `scripting`.
|
|
1065
|
+
|
|
1066
|
+
#### The `document.state.data` Object
|
|
1067
|
+
|
|
1068
|
+
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.
|
|
1069
|
+
|
|
1070
|
+
```js
|
|
1071
|
+
Observer.observe(document.state, 'data', e => {
|
|
1072
|
+
console.log('Current page data is: ', e.value);
|
|
1073
|
+
});
|
|
1074
|
+
```
|
|
1075
|
+
|
|
1076
|
+
#### The `document.state.url` Object
|
|
1077
|
+
|
|
1078
|
+
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).
|
|
1079
|
+
|
|
1080
|
+
```js
|
|
1081
|
+
console.log(document.state.url) // { hash, host, hostname, href, origin, password, pathname, port, protocol, search, searchParams, username }
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
```js
|
|
1085
|
+
Observer.observe(document.state.url, 'hash', e => {
|
|
1086
|
+
console.log(document.state.url.hash === e.value); // true
|
|
1087
|
+
});
|
|
1088
|
+
```
|
|
1089
|
+
|
|
1090
|
+
```js
|
|
1091
|
+
// Navigates to "/login#form" as if a link was clicked
|
|
1092
|
+
document.addEventListener('synthetic-navigation', e => {
|
|
1093
|
+
Observer.set(document.state.url, 'href', '/login#form');
|
|
1094
|
+
});
|
|
1095
|
+
|
|
1096
|
+
// Or...
|
|
1097
|
+
document.addEventListener('synthetic-navigation', e => {
|
|
1098
|
+
Observer.set(document.state.url, { pathname: '/login', hash: '#form' });
|
|
1099
|
+
});
|
|
1100
|
+
|
|
1101
|
+
console.log(document.state.url.hash); // #form
|
|
1102
|
+
```
|
|
1103
|
+
|
|
1104
|
+
There is also the *convenience* `query` property that offers the URL parameters as a *live* object.
|
|
1105
|
+
|
|
1106
|
+
```js
|
|
1107
|
+
// For URL: http://localhost:3000/login?as=student
|
|
1108
|
+
console.log(document.state.url.query.as) // student
|
|
1109
|
+
|
|
1110
|
+
// Re-rewrite the URL and initiate navigation by simply modifying a query parameter
|
|
1111
|
+
document.addEventListener('synthetic-navigation', e => {
|
|
1112
|
+
Observer.set(document.state.url.query, 'as', 'business');
|
|
1113
|
+
});
|
|
1114
|
+
```
|
|
1115
|
+
|
|
1024
1116
|
### Requests and Responses
|
|
1025
1117
|
|
|
1026
1118
|
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.
|
|
@@ -1030,7 +1122,7 @@ Now, routes in Webflo can be designed for different types of request/response sc
|
|
|
1030
1122
|
#### Scenario 1: Static File Requests and Responses
|
|
1031
1123
|
|
|
1032
1124
|
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
|
|
1125
|
+
+ 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
1126
|
+ On the client, Webflo serves static files from the network, or from the application cache, where available.
|
|
1035
1127
|
|
|
1036
1128
|
#### Scenario 2: API Requests and Responses
|
|
@@ -1120,74 +1212,16 @@ Webflo also offers a *convenience* method.
|
|
|
1120
1212
|
|
|
1121
1213
|
```js
|
|
1122
1214
|
console.log(event.request.headers.cookies); // { 'Cookie-1': 'cookie-val', 'Cookie-2': 'cookie2-val' };
|
|
1123
|
-
|
|
1215
|
+
```
|
|
1124
1216
|
|
|
1125
1217
|
### Webflo Applications
|
|
1126
1218
|
|
|
1127
|
-
In just a few concepts, Webflo comes ready for any type of application! Now,
|
|
1219
|
+
In just a few concepts, Webflo comes ready for any type of application! Now, here's how it all works.
|
|
1128
1220
|
|
|
1129
|
-
+ [Application State](#application-state)
|
|
1130
1221
|
+ [Client-Side Applications](#client-side-applications)
|
|
1131
1222
|
+ [API Backends](#api-backends)
|
|
1132
1223
|
+ [Static Sites](#static-sites)
|
|
1133
1224
|
|
|
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
1225
|
#### Client-Side Applications
|
|
1192
1226
|
|
|
1193
1227
|
Web pages that embed the Webflo client JS bundle deliver a great user experience.
|
|
@@ -1208,7 +1242,7 @@ Unless disabled in [config](#spa_navigation), it is factored-in at build time fo
|
|
|
1208
1242
|
|
|
1209
1243
|
##### SPA State
|
|
1210
1244
|
|
|
1211
|
-
|
|
1245
|
+
On the client side of a Webflo application, [the idea of state](#the-idea-of-state) also goes further to include the following aspects of the client-side lifecycle that can be used to provide visual cues on the UI.
|
|
1212
1246
|
|
|
1213
1247
|
###### The `document.state.network` Object
|
|
1214
1248
|
|
|
@@ -1262,6 +1296,8 @@ Observer.observe(document.state.network, 'error', e => {
|
|
|
1262
1296
|
|
|
1263
1297
|
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.
|
|
1264
1298
|
|
|
1299
|
+
<!-- TODO: method overrides -->
|
|
1300
|
+
|
|
1265
1301
|
##### Service Workers
|
|
1266
1302
|
|
|
1267
1303
|
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.
|
|
@@ -1417,7 +1453,7 @@ export default function(event, context, next) {
|
|
|
1417
1453
|
}
|
|
1418
1454
|
```
|
|
1419
1455
|
|
|
1420
|
-
You are always able to lay out your route handlers in the structure for a
|
|
1456
|
+
You are always able to lay out your route handlers in the structure for a formal REST API.
|
|
1421
1457
|
|
|
1422
1458
|
```shell
|
|
1423
1459
|
server
|
|
@@ -1426,7 +1462,7 @@ server
|
|
|
1426
1462
|
└── api/v1/products/index.js
|
|
1427
1463
|
```
|
|
1428
1464
|
|
|
1429
|
-
And if you will partition your backend for both page routes and a
|
|
1465
|
+
And if you will partition your backend for both page routes and a formal REST API...
|
|
1430
1466
|
|
|
1431
1467
|
```shell
|
|
1432
1468
|
server
|