@webqit/webflo 0.20.2-next.0 → 0.20.2-next.2
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 +8 -5
- package/package.json +1 -1
- package/site/.vitepress/cache/deps/_metadata.json +14 -14
- package/site/.vitepress/config.ts +8 -2
- package/site/.vitepress/dist/-/_.html +56 -0
- package/site/.vitepress/dist/-/docs.old.html +679 -0
- package/site/.vitepress/dist/404.html +23 -0
- package/site/.vitepress/dist/api/webflo-fetch/FormData.html +55 -0
- package/site/.vitepress/dist/api/webflo-fetch/Headers.html +55 -0
- package/site/.vitepress/dist/api/webflo-fetch/LiveResponse.html +55 -0
- package/site/.vitepress/dist/api/webflo-fetch/Request.html +55 -0
- package/site/.vitepress/dist/api/webflo-fetch/Response.html +55 -0
- package/site/.vitepress/dist/api/webflo-fetch/fetch.html +55 -0
- package/site/.vitepress/dist/api/webflo-routing/HttpCookies.html +55 -0
- package/site/.vitepress/dist/api/webflo-routing/HttpEvent/respondWith.html +55 -0
- package/site/.vitepress/dist/api/webflo-routing/HttpEvent/waitUntil.html +55 -0
- package/site/.vitepress/dist/api/webflo-routing/HttpEvent/waitUntilNavigate.html +55 -0
- package/site/.vitepress/dist/api/webflo-routing/HttpEvent.html +55 -0
- package/site/.vitepress/dist/api/webflo-routing/HttpSession.html +55 -0
- package/site/.vitepress/dist/api/webflo-routing/HttpState.html +55 -0
- package/site/.vitepress/dist/api/webflo-routing/HttpUser.html +55 -0
- package/site/.vitepress/dist/api/webflo-routing/handler/fetch.html +59 -0
- package/site/.vitepress/dist/api/webflo-routing/handler/next.html +61 -0
- package/site/.vitepress/dist/api/webflo-routing/handler.html +65 -0
- package/site/.vitepress/dist/api.html +55 -0
- package/site/.vitepress/dist/assets/-__.md.wD5kDRhS.js +1 -0
- package/site/.vitepress/dist/assets/-__.md.wD5kDRhS.lean.js +1 -0
- package/site/.vitepress/dist/assets/-_docs.old.md.CiqucE_1.js +625 -0
- package/site/.vitepress/dist/assets/-_docs.old.md.CiqucE_1.lean.js +1 -0
- package/site/.vitepress/dist/assets/api.md.C9KRNLi_.js +1 -0
- package/site/.vitepress/dist/assets/api.md.C9KRNLi_.lean.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-fetch_FormData.md.CDuexUTz.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-fetch_FormData.md.CDuexUTz.lean.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-fetch_Headers.md.Cl_4-FUP.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-fetch_Headers.md.Cl_4-FUP.lean.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-fetch_LiveResponse.md.BMidKMB1.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-fetch_LiveResponse.md.BMidKMB1.lean.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-fetch_Request.md.DPwZCPmi.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-fetch_Request.md.DPwZCPmi.lean.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-fetch_Response.md.BaSyoOLE.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-fetch_Response.md.BaSyoOLE.lean.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-fetch_fetch.md.Bc9r3Q9I.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-fetch_fetch.md.Bc9r3Q9I.lean.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_HttpCookies.md.B5ok3jrM.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_HttpCookies.md.B5ok3jrM.lean.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_HttpEvent.md.DBkSQRTa.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_HttpEvent.md.DBkSQRTa.lean.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_HttpEvent_respondWith.md.aAxq-5Ie.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_HttpEvent_respondWith.md.aAxq-5Ie.lean.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_HttpEvent_waitUntil.md.DyQZLhPR.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_HttpEvent_waitUntil.md.DyQZLhPR.lean.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_HttpEvent_waitUntilNavigate.md.DKLylwhl.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_HttpEvent_waitUntilNavigate.md.DKLylwhl.lean.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_HttpSession.md.bkeCy7_Q.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_HttpSession.md.bkeCy7_Q.lean.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_HttpState.md.DO53IAM1.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_HttpState.md.DO53IAM1.lean.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_HttpUser.md.CfsaBFdl.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_HttpUser.md.CfsaBFdl.lean.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_handler.md.B0eVFluL.js +11 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_handler.md.B0eVFluL.lean.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_handler_fetch.md.CpwUMFMz.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_handler_fetch.md.CpwUMFMz.lean.js +1 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_handler_next.md.CA4tDXtV.js +7 -0
- package/site/.vitepress/dist/assets/api_webflo-routing_handler_next.md.CA4tDXtV.lean.js +1 -0
- package/site/.vitepress/dist/assets/app.B989j-Lw.js +256 -0
- package/site/.vitepress/dist/assets/chunks/@localSearchIndexroot.IcaIAE_s.js +1 -0
- package/site/.vitepress/dist/assets/chunks/VPLocalSearchBox.CxstEVOs.js +8 -0
- package/site/.vitepress/dist/assets/chunks/arc.CNNknwAo.js +1 -0
- package/site/.vitepress/dist/assets/chunks/architectureDiagram-VXUJARFQ.DCLYhNHD.js +36 -0
- package/site/.vitepress/dist/assets/chunks/basePickBy.Cxi84nlK.js +1 -0
- package/site/.vitepress/dist/assets/chunks/baseUniq.DDTOgUAc.js +1 -0
- package/site/.vitepress/dist/assets/chunks/blockDiagram-VD42YOAC.BcpiarhA.js +122 -0
- package/site/.vitepress/dist/assets/chunks/c4Diagram-YG6GDRKO.BI_5dKaW.js +10 -0
- package/site/.vitepress/dist/assets/chunks/channel.CxIJmpCu.js +1 -0
- package/site/.vitepress/dist/assets/chunks/chunk-4BX2VUAB.C6goADHj.js +1 -0
- package/site/.vitepress/dist/assets/chunks/chunk-55IACEB6.BLGj4Pud.js +1 -0
- package/site/.vitepress/dist/assets/chunks/chunk-B4BG7PRW.DsAzxoJ_.js +165 -0
- package/site/.vitepress/dist/assets/chunks/chunk-DI55MBZ5.BnE2NeEC.js +220 -0
- package/site/.vitepress/dist/assets/chunks/chunk-FMBD7UC4.DGeT8zu4.js +15 -0
- package/site/.vitepress/dist/assets/chunks/chunk-QN33PNHL.Bc78W0xn.js +1 -0
- package/site/.vitepress/dist/assets/chunks/chunk-QZHKN3VN.BFSSbGEs.js +1 -0
- package/site/.vitepress/dist/assets/chunks/chunk-TZMSLE5B.DyZR9PPb.js +1 -0
- package/site/.vitepress/dist/assets/chunks/classDiagram-2ON5EDUG._0fUH5ha.js +1 -0
- package/site/.vitepress/dist/assets/chunks/classDiagram-v2-WZHVMYZB._0fUH5ha.js +1 -0
- package/site/.vitepress/dist/assets/chunks/clone.BYyOtvOD.js +1 -0
- package/site/.vitepress/dist/assets/chunks/cose-bilkent-S5V4N54A.TlhUFBAR.js +1 -0
- package/site/.vitepress/dist/assets/chunks/cytoscape.esm.CyJtwmzi.js +331 -0
- package/site/.vitepress/dist/assets/chunks/dagre-6UL2VRFP.D__9IqfC.js +4 -0
- package/site/.vitepress/dist/assets/chunks/defaultLocale.C4B-KCzX.js +1 -0
- package/site/.vitepress/dist/assets/chunks/diagram-PSM6KHXK.DYSDWndU.js +24 -0
- package/site/.vitepress/dist/assets/chunks/diagram-QEK2KX5R.FZi2c8WB.js +43 -0
- package/site/.vitepress/dist/assets/chunks/diagram-S2PKOQOG.D-Xxss9Z.js +24 -0
- package/site/.vitepress/dist/assets/chunks/erDiagram-Q2GNP2WA.CNdrcxqE.js +60 -0
- package/site/.vitepress/dist/assets/chunks/flowDiagram-NV44I4VS.C_6PWuGm.js +162 -0
- package/site/.vitepress/dist/assets/chunks/framework.9Uv4PgnO.js +18 -0
- package/site/.vitepress/dist/assets/chunks/ganttDiagram-LVOFAZNH._TBJejxF.js +267 -0
- package/site/.vitepress/dist/assets/chunks/gitGraphDiagram-NY62KEGX.BmQODSWS.js +65 -0
- package/site/.vitepress/dist/assets/chunks/graph.BHWe3GDJ.js +1 -0
- package/site/.vitepress/dist/assets/chunks/infoDiagram-ER5ION4S.CbdOaGtv.js +2 -0
- package/site/.vitepress/dist/assets/chunks/init.Gi6I4Gst.js +1 -0
- package/site/.vitepress/dist/assets/chunks/journeyDiagram-XKPGCS4Q.B-PjYmDZ.js +139 -0
- package/site/.vitepress/dist/assets/chunks/kanban-definition-3W4ZIXB7.Bc_8e9KR.js +89 -0
- package/site/.vitepress/dist/assets/chunks/katex.BbEIqZs1.js +261 -0
- package/site/.vitepress/dist/assets/chunks/layout.hswMW8l0.js +1 -0
- package/site/.vitepress/dist/assets/chunks/linear.CUHYPplj.js +1 -0
- package/site/.vitepress/dist/assets/chunks/mindmap-definition-VGOIOE7T.CvIaZI8e.js +68 -0
- package/site/.vitepress/dist/assets/chunks/ordinal.BYWQX77i.js +1 -0
- package/site/.vitepress/dist/assets/chunks/pieDiagram-ADFJNKIX.BSTQM2Ms.js +30 -0
- package/site/.vitepress/dist/assets/chunks/quadrantDiagram-AYHSOK5B.CkEJjNYC.js +7 -0
- package/site/.vitepress/dist/assets/chunks/requirementDiagram-UZGBJVZJ.QsTahBza.js +64 -0
- package/site/.vitepress/dist/assets/chunks/sankeyDiagram-TZEHDZUN.NRsUNMzF.js +10 -0
- package/site/.vitepress/dist/assets/chunks/sequenceDiagram-WL72ISMW.rq-YLOb-.js +145 -0
- package/site/.vitepress/dist/assets/chunks/stateDiagram-FKZM4ZOC.BseVQjcW.js +1 -0
- package/site/.vitepress/dist/assets/chunks/stateDiagram-v2-4FDKWEC3.CH4mdaj_.js +1 -0
- package/site/.vitepress/dist/assets/chunks/theme.Jp0RA4YE.js +2 -0
- package/site/.vitepress/dist/assets/chunks/timeline-definition-IT6M3QCI.N8DNMk16.js +61 -0
- package/site/.vitepress/dist/assets/chunks/treemap-KMMF4GRG.CAwkr_aP.js +128 -0
- package/site/.vitepress/dist/assets/chunks/virtual_mermaid-config.DDnGl6nM.js +1 -0
- package/site/.vitepress/dist/assets/chunks/xychartDiagram-PRI3JC2R.CF4KVQhG.js +7 -0
- package/site/.vitepress/dist/assets/contributing.md.gqIMCtVI.js +1 -0
- package/site/.vitepress/dist/assets/contributing.md.gqIMCtVI.lean.js +1 -0
- package/site/.vitepress/dist/assets/docs.md.hvbqYbFY.js +1 -0
- package/site/.vitepress/dist/assets/docs.md.hvbqYbFY.lean.js +1 -0
- package/site/.vitepress/dist/assets/docs_advanced.md.D048cxnq.js +1 -0
- package/site/.vitepress/dist/assets/docs_advanced.md.D048cxnq.lean.js +1 -0
- package/site/.vitepress/dist/assets/docs_advanced_lifecycles.md.BICPL-da.js +1 -0
- package/site/.vitepress/dist/assets/docs_advanced_lifecycles.md.BICPL-da.lean.js +1 -0
- package/site/.vitepress/dist/assets/docs_advanced_redirects.md.BMha6D3W.js +1 -0
- package/site/.vitepress/dist/assets/docs_advanced_redirects.md.BMha6D3W.lean.js +1 -0
- package/site/.vitepress/dist/assets/docs_advanced_routing.md.Cv63UDJF.js +1 -0
- package/site/.vitepress/dist/assets/docs_advanced_routing.md.Cv63UDJF.lean.js +1 -0
- package/site/.vitepress/dist/assets/docs_concepts.md.Clwx81Hz.js +3 -0
- package/site/.vitepress/dist/assets/docs_concepts.md.Clwx81Hz.lean.js +1 -0
- package/site/.vitepress/dist/assets/docs_concepts_realtime.md.CBrMq5Ln.js +218 -0
- package/site/.vitepress/dist/assets/docs_concepts_realtime.md.CBrMq5Ln.lean.js +1 -0
- package/site/.vitepress/dist/assets/docs_concepts_rendering.md.BWr5Lxgn.js +24 -0
- package/site/.vitepress/dist/assets/docs_concepts_rendering.md.BWr5Lxgn.lean.js +1 -0
- package/site/.vitepress/dist/assets/docs_concepts_request-response.md.DhplzNqt.js +4 -0
- package/site/.vitepress/dist/assets/docs_concepts_request-response.md.DhplzNqt.lean.js +1 -0
- package/site/.vitepress/dist/assets/docs_concepts_routing.md.C2KO1eAu.js +228 -0
- package/site/.vitepress/dist/assets/docs_concepts_routing.md.C2KO1eAu.lean.js +1 -0
- package/site/.vitepress/dist/assets/docs_concepts_state.md.CtbMVS_K.js +10 -0
- package/site/.vitepress/dist/assets/docs_concepts_state.md.CtbMVS_K.lean.js +1 -0
- package/site/.vitepress/dist/assets/docs_concepts_templates.md.4i6jQcYw.js +15 -0
- package/site/.vitepress/dist/assets/docs_concepts_templates.md.4i6jQcYw.lean.js +1 -0
- package/site/.vitepress/dist/assets/docs_getting-started.md.CNGtwB_L.js +75 -0
- package/site/.vitepress/dist/assets/docs_getting-started.md.CNGtwB_L.lean.js +1 -0
- package/site/.vitepress/dist/assets/docs_tech-stack.md.xiWGQstL.js +1 -0
- package/site/.vitepress/dist/assets/docs_tech-stack.md.xiWGQstL.lean.js +1 -0
- package/site/.vitepress/dist/assets/examples.md.BqDfJd4G.js +1 -0
- package/site/.vitepress/dist/assets/examples.md.BqDfJd4G.lean.js +1 -0
- package/site/.vitepress/dist/assets/examples_pwa.md.DREN7J2F.js +1 -0
- package/site/.vitepress/dist/assets/examples_pwa.md.DREN7J2F.lean.js +1 -0
- package/site/.vitepress/dist/assets/examples_web.md.DUhZ0IQL.js +1 -0
- package/site/.vitepress/dist/assets/examples_web.md.DUhZ0IQL.lean.js +1 -0
- package/site/.vitepress/dist/assets/faq.md.DtfXaXUI.js +1 -0
- package/site/.vitepress/dist/assets/faq.md.DtfXaXUI.lean.js +1 -0
- package/site/.vitepress/dist/assets/guides.md.BVdQyeU-.js +1 -0
- package/site/.vitepress/dist/assets/guides.md.BVdQyeU-.lean.js +1 -0
- package/site/.vitepress/dist/assets/guides_guide-auth.md.DNFuRudp.js +1 -0
- package/site/.vitepress/dist/assets/guides_guide-auth.md.DNFuRudp.lean.js +1 -0
- package/site/.vitepress/dist/assets/guides_guide-file-upload.md.DRbRLk7h.js +1 -0
- package/site/.vitepress/dist/assets/guides_guide-file-upload.md.DRbRLk7h.lean.js +1 -0
- package/site/.vitepress/dist/assets/guides_guide-service-worker.md.B0wEVcQw.js +1 -0
- package/site/.vitepress/dist/assets/guides_guide-service-worker.md.B0wEVcQw.lean.js +1 -0
- package/site/.vitepress/dist/assets/guides_tutorial-1-todo.md.D9ket3Re.js +3 -0
- package/site/.vitepress/dist/assets/guides_tutorial-1-todo.md.D9ket3Re.lean.js +1 -0
- package/site/.vitepress/dist/assets/index.md.DB-CsGEX.js +1 -0
- package/site/.vitepress/dist/assets/index.md.DB-CsGEX.lean.js +1 -0
- package/site/.vitepress/dist/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
- package/site/.vitepress/dist/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
- package/site/.vitepress/dist/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
- package/site/.vitepress/dist/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
- package/site/.vitepress/dist/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
- package/site/.vitepress/dist/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
- package/site/.vitepress/dist/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
- package/site/.vitepress/dist/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
- package/site/.vitepress/dist/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
- package/site/.vitepress/dist/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
- package/site/.vitepress/dist/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
- package/site/.vitepress/dist/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
- package/site/.vitepress/dist/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
- package/site/.vitepress/dist/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
- package/site/.vitepress/dist/assets/recipes_realtime.md.CX1Vs2FD.js +1 -0
- package/site/.vitepress/dist/assets/recipes_realtime.md.CX1Vs2FD.lean.js +1 -0
- package/site/.vitepress/dist/assets/recipes_streaming.md.C7GFShgF.js +5 -0
- package/site/.vitepress/dist/assets/recipes_streaming.md.C7GFShgF.lean.js +1 -0
- package/site/.vitepress/dist/assets/reference_cli.md.DERqaQJm.js +1 -0
- package/site/.vitepress/dist/assets/reference_cli.md.DERqaQJm.lean.js +1 -0
- package/site/.vitepress/dist/assets/reference_config.md.DI_yG-7N.js +1 -0
- package/site/.vitepress/dist/assets/reference_config.md.DI_yG-7N.lean.js +1 -0
- package/site/.vitepress/dist/assets/reference_tools.md.DZxjdVFX.js +1 -0
- package/site/.vitepress/dist/assets/reference_tools.md.DZxjdVFX.lean.js +1 -0
- package/site/.vitepress/dist/assets/style.BD0LzINo.css +1 -0
- package/site/.vitepress/dist/contributing.html +55 -0
- package/site/.vitepress/dist/docs/advanced/lifecycles.html +55 -0
- package/site/.vitepress/dist/docs/advanced/redirects.html +55 -0
- package/site/.vitepress/dist/docs/advanced/routing.html +55 -0
- package/site/.vitepress/dist/docs/advanced.html +55 -0
- package/site/.vitepress/dist/docs/concepts/realtime.html +272 -0
- package/site/.vitepress/dist/docs/concepts/rendering.html +78 -0
- package/site/.vitepress/dist/docs/concepts/request-response.html +58 -0
- package/site/.vitepress/dist/docs/concepts/routing.html +282 -0
- package/site/.vitepress/dist/docs/concepts/state.html +64 -0
- package/site/.vitepress/dist/docs/concepts/templates.html +69 -0
- package/site/.vitepress/dist/docs/concepts.html +57 -0
- package/site/.vitepress/dist/docs/getting-started.html +129 -0
- package/site/.vitepress/dist/docs/tech-stack.html +55 -0
- package/site/.vitepress/dist/docs.html +55 -0
- package/site/.vitepress/dist/examples/pwa.html +55 -0
- package/site/.vitepress/dist/examples/web.html +55 -0
- package/site/.vitepress/dist/examples.html +55 -0
- package/site/.vitepress/dist/faq.html +55 -0
- package/site/.vitepress/dist/guides/guide-auth.html +55 -0
- package/site/.vitepress/dist/guides/guide-file-upload.html +55 -0
- package/site/.vitepress/dist/guides/guide-service-worker.html +55 -0
- package/site/.vitepress/dist/guides/tutorial-1-todo.html +57 -0
- package/site/.vitepress/dist/guides.html +55 -0
- package/site/.vitepress/dist/hashmap.json +1 -0
- package/site/.vitepress/dist/img/brand/logo-670x670.png +0 -0
- package/site/.vitepress/dist/index.html +55 -0
- package/site/.vitepress/dist/recipes/realtime.html +55 -0
- package/site/.vitepress/dist/recipes/streaming.html +59 -0
- package/site/.vitepress/dist/reference/cli.html +55 -0
- package/site/.vitepress/dist/reference/config.html +55 -0
- package/site/.vitepress/dist/reference/tools.html +55 -0
- package/site/.vitepress/dist/vp-icons.css +1 -0
- package/site/docs/getting-started.md +12 -0
- package/site/docs.md +4 -99
- package/site/index.md +4 -1
- package/site/overview.md +101 -0
- package/site/.vitepress/cache/deps/@braintree_sanitize-url 2.js +0 -93
- package/site/.vitepress/cache/deps/@braintree_sanitize-url.js 2.map +0 -7
- package/site/.vitepress/cache/deps/_metadata 2.json +0 -85
- package/site/.vitepress/cache/deps/chunk-BUSYA2B4 2.js +0 -9
- package/site/.vitepress/cache/deps/chunk-BUSYA2B4.js 2.map +0 -7
- package/site/.vitepress/cache/deps/chunk-Q2AYPHVK 2.js +0 -9719
- package/site/.vitepress/cache/deps/chunk-Q2AYPHVK.js 2.map +0 -7
- package/site/.vitepress/cache/deps/chunk-QAXAIFA7 2.js +0 -12705
- package/site/.vitepress/cache/deps/chunk-QAXAIFA7.js 2.map +0 -7
- package/site/.vitepress/cache/deps/cytoscape 2.js +0 -30278
- package/site/.vitepress/cache/deps/cytoscape-cose-bilkent 2.js +0 -4710
- package/site/.vitepress/cache/deps/cytoscape-cose-bilkent.js 2.map +0 -7
- package/site/.vitepress/cache/deps/cytoscape.js 2.map +0 -7
- package/site/.vitepress/cache/deps/dayjs 2.js +0 -285
- package/site/.vitepress/cache/deps/dayjs.js 2.map +0 -7
- package/site/.vitepress/cache/deps/debug 2.js +0 -453
- package/site/.vitepress/cache/deps/debug.js 2.map +0 -7
- package/site/.vitepress/cache/deps/package 2.json +0 -3
- package/site/.vitepress/cache/deps/vitepress___@vue_devtools-api 2.js +0 -4507
- package/site/.vitepress/cache/deps/vitepress___@vue_devtools-api.js 2.map +0 -7
- package/site/.vitepress/cache/deps/vitepress___@vueuse_core 2.js +0 -584
- package/site/.vitepress/cache/deps/vitepress___@vueuse_core.js 2.map +0 -7
- package/site/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap 2.js +0 -1166
- package/site/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js 2.map +0 -7
- package/site/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js 2.js +0 -1667
- package/site/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js 2.map +0 -7
- package/site/.vitepress/cache/deps/vitepress___minisearch 2.js +0 -1815
- package/site/.vitepress/cache/deps/vitepress___minisearch.js 2.map +0 -7
- package/site/.vitepress/cache/deps/vue 2.js +0 -344
- package/site/.vitepress/cache/deps/vue.js 2.map +0 -7
|
@@ -0,0 +1,679 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en-US" dir="ltr">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>Webflo</title>
|
|
7
|
+
<meta name="description" content="A universal, standards-first web framework for building web-native apps.">
|
|
8
|
+
<meta name="generator" content="VitePress v1.6.4">
|
|
9
|
+
<link rel="preload stylesheet" href="/assets/style.BD0LzINo.css" as="style">
|
|
10
|
+
<link rel="preload stylesheet" href="/vp-icons.css" as="style">
|
|
11
|
+
|
|
12
|
+
<script type="module" src="/assets/app.B989j-Lw.js"></script>
|
|
13
|
+
<link rel="preload" href="/assets/inter-roman-latin.Di8DUHzh.woff2" as="font" type="font/woff2" crossorigin="">
|
|
14
|
+
<link rel="modulepreload" href="/assets/chunks/framework.9Uv4PgnO.js">
|
|
15
|
+
<link rel="modulepreload" href="/assets/chunks/theme.Jp0RA4YE.js">
|
|
16
|
+
<link rel="modulepreload" href="/assets/chunks/katex.BbEIqZs1.js">
|
|
17
|
+
<link rel="modulepreload" href="/assets/chunks/dagre-6UL2VRFP.D__9IqfC.js">
|
|
18
|
+
<link rel="modulepreload" href="/assets/chunks/cose-bilkent-S5V4N54A.TlhUFBAR.js">
|
|
19
|
+
<link rel="modulepreload" href="/assets/chunks/c4Diagram-YG6GDRKO.BI_5dKaW.js">
|
|
20
|
+
<link rel="modulepreload" href="/assets/chunks/flowDiagram-NV44I4VS.C_6PWuGm.js">
|
|
21
|
+
<link rel="modulepreload" href="/assets/chunks/erDiagram-Q2GNP2WA.CNdrcxqE.js">
|
|
22
|
+
<link rel="modulepreload" href="/assets/chunks/gitGraphDiagram-NY62KEGX.BmQODSWS.js">
|
|
23
|
+
<link rel="modulepreload" href="/assets/chunks/ganttDiagram-LVOFAZNH._TBJejxF.js">
|
|
24
|
+
<link rel="modulepreload" href="/assets/chunks/infoDiagram-ER5ION4S.CbdOaGtv.js">
|
|
25
|
+
<link rel="modulepreload" href="/assets/chunks/pieDiagram-ADFJNKIX.BSTQM2Ms.js">
|
|
26
|
+
<link rel="modulepreload" href="/assets/chunks/quadrantDiagram-AYHSOK5B.CkEJjNYC.js">
|
|
27
|
+
<link rel="modulepreload" href="/assets/chunks/xychartDiagram-PRI3JC2R.CF4KVQhG.js">
|
|
28
|
+
<link rel="modulepreload" href="/assets/chunks/requirementDiagram-UZGBJVZJ.QsTahBza.js">
|
|
29
|
+
<link rel="modulepreload" href="/assets/chunks/sequenceDiagram-WL72ISMW.rq-YLOb-.js">
|
|
30
|
+
<link rel="modulepreload" href="/assets/chunks/classDiagram-2ON5EDUG._0fUH5ha.js">
|
|
31
|
+
<link rel="modulepreload" href="/assets/chunks/classDiagram-v2-WZHVMYZB._0fUH5ha.js">
|
|
32
|
+
<link rel="modulepreload" href="/assets/chunks/stateDiagram-FKZM4ZOC.BseVQjcW.js">
|
|
33
|
+
<link rel="modulepreload" href="/assets/chunks/stateDiagram-v2-4FDKWEC3.CH4mdaj_.js">
|
|
34
|
+
<link rel="modulepreload" href="/assets/chunks/journeyDiagram-XKPGCS4Q.B-PjYmDZ.js">
|
|
35
|
+
<link rel="modulepreload" href="/assets/chunks/timeline-definition-IT6M3QCI.N8DNMk16.js">
|
|
36
|
+
<link rel="modulepreload" href="/assets/chunks/mindmap-definition-VGOIOE7T.CvIaZI8e.js">
|
|
37
|
+
<link rel="modulepreload" href="/assets/chunks/kanban-definition-3W4ZIXB7.Bc_8e9KR.js">
|
|
38
|
+
<link rel="modulepreload" href="/assets/chunks/sankeyDiagram-TZEHDZUN.NRsUNMzF.js">
|
|
39
|
+
<link rel="modulepreload" href="/assets/chunks/diagram-S2PKOQOG.D-Xxss9Z.js">
|
|
40
|
+
<link rel="modulepreload" href="/assets/chunks/diagram-QEK2KX5R.FZi2c8WB.js">
|
|
41
|
+
<link rel="modulepreload" href="/assets/chunks/blockDiagram-VD42YOAC.BcpiarhA.js">
|
|
42
|
+
<link rel="modulepreload" href="/assets/chunks/architectureDiagram-VXUJARFQ.DCLYhNHD.js">
|
|
43
|
+
<link rel="modulepreload" href="/assets/chunks/diagram-PSM6KHXK.DYSDWndU.js">
|
|
44
|
+
<link rel="modulepreload" href="/assets/chunks/virtual_mermaid-config.DDnGl6nM.js">
|
|
45
|
+
<link rel="modulepreload" href="/assets/-_docs.old.md.CiqucE_1.lean.js">
|
|
46
|
+
<meta name="theme-color" content="#0f172a">
|
|
47
|
+
<script id="check-dark-mode">document.documentElement.classList.add("dark");</script>
|
|
48
|
+
<script id="check-mac-os">document.documentElement.classList.toggle("mac",/Mac|iPhone|iPod|iPad/i.test(navigator.platform));</script>
|
|
49
|
+
</head>
|
|
50
|
+
<body>
|
|
51
|
+
<div id="app"><div class="Layout" data-v-5d98c3a5><!--[--><!--]--><!--[--><span tabindex="-1" data-v-0b0ada53></span><a href="#VPContent" class="VPSkipLink visually-hidden" data-v-0b0ada53>Skip to content</a><!--]--><!----><header class="VPNav" data-v-5d98c3a5 data-v-ae24b3ad><div class="VPNavBar" data-v-ae24b3ad data-v-6aa21345><div class="wrapper" data-v-6aa21345><div class="container" data-v-6aa21345><div class="title" data-v-6aa21345><div class="VPNavBarTitle has-sidebar" data-v-6aa21345 data-v-1168a8e4><a class="title" href="/" data-v-1168a8e4><!--[--><!--]--><!----><span data-v-1168a8e4>Webflo</span><!--[--><!--]--></a></div></div><div class="content" data-v-6aa21345><div class="content-body" data-v-6aa21345><!--[--><!--]--><div class="VPNavBarSearch search" data-v-6aa21345><!--[--><!----><div id="local-search"><button type="button" class="DocSearch DocSearch-Button" aria-label="Search"><span class="DocSearch-Button-Container"><span class="vp-icon DocSearch-Search-Icon"></span><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"><kbd class="DocSearch-Button-Key"></kbd><kbd class="DocSearch-Button-Key">K</kbd></span></button></div><!--]--></div><nav aria-labelledby="main-nav-aria-label" class="VPNavBarMenu menu" data-v-6aa21345 data-v-dc692963><span id="main-nav-aria-label" class="visually-hidden" data-v-dc692963> Main Navigation </span><!--[--><!--[--><a class="VPLink link VPNavBarMenuLink active" href="/docs" tabindex="0" data-v-dc692963 data-v-e56f3d57><!--[--><span data-v-e56f3d57>Docs</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/api" tabindex="0" data-v-dc692963 data-v-e56f3d57><!--[--><span data-v-e56f3d57>API</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/guides" tabindex="0" data-v-dc692963 data-v-e56f3d57><!--[--><span data-v-e56f3d57>Guides</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/examples" tabindex="0" data-v-dc692963 data-v-e56f3d57><!--[--><span data-v-e56f3d57>Examples</span><!--]--></a><!--]--><!--]--></nav><!----><!----><div class="VPSocialLinks VPNavBarSocialLinks social-links" data-v-6aa21345 data-v-0394ad82 data-v-7bc22406><!--[--><a class="VPSocialLink no-icon" href="https://github.com/webqit/webflo" aria-label="github" target="_blank" rel="noopener" data-v-7bc22406 data-v-bd121fe5><span class="vpi-social-github"></span></a><!--]--></div><div class="VPFlyout VPNavBarExtra extra" data-v-6aa21345 data-v-bb2aa2f0 data-v-cf11d7a2><button type="button" class="button" aria-haspopup="true" aria-expanded="false" aria-label="extra navigation" data-v-cf11d7a2><span class="vpi-more-horizontal icon" data-v-cf11d7a2></span></button><div class="menu" data-v-cf11d7a2><div class="VPMenu" data-v-cf11d7a2 data-v-b98bc113><!----><!--[--><!--[--><!----><!----><div class="group" data-v-bb2aa2f0><div class="item social-links" data-v-bb2aa2f0><div class="VPSocialLinks social-links-list" data-v-bb2aa2f0 data-v-7bc22406><!--[--><a class="VPSocialLink no-icon" href="https://github.com/webqit/webflo" aria-label="github" target="_blank" rel="noopener" data-v-7bc22406 data-v-bd121fe5><span class="vpi-social-github"></span></a><!--]--></div></div></div><!--]--><!--]--></div></div></div><!--[--><!--]--><button type="button" class="VPNavBarHamburger hamburger" aria-label="mobile navigation" aria-expanded="false" aria-controls="VPNavScreen" data-v-6aa21345 data-v-e5dd9c1c><span class="container" data-v-e5dd9c1c><span class="top" data-v-e5dd9c1c></span><span class="middle" data-v-e5dd9c1c></span><span class="bottom" data-v-e5dd9c1c></span></span></button></div></div></div></div><div class="divider" data-v-6aa21345><div class="divider-line" data-v-6aa21345></div></div></div><!----></header><div class="VPLocalNav has-sidebar empty" data-v-5d98c3a5 data-v-a6f0e41e><div class="container" data-v-a6f0e41e><button class="menu" aria-expanded="false" aria-controls="VPSidebarNav" data-v-a6f0e41e><span class="vpi-align-left menu-icon" data-v-a6f0e41e></span><span class="menu-text" data-v-a6f0e41e>Menu</span></button><div class="VPLocalNavOutlineDropdown" style="--vp-vh:0px;" data-v-a6f0e41e data-v-8a42e2b4><button data-v-8a42e2b4>Return to top</button><!----></div></div></div><aside class="VPSidebar" data-v-5d98c3a5 data-v-319d5ca6><div class="curtain" data-v-319d5ca6></div><nav class="nav" id="VPSidebarNav" aria-labelledby="sidebar-aria-label" tabindex="-1" data-v-319d5ca6><span class="visually-hidden" id="sidebar-aria-label" data-v-319d5ca6> Sidebar Navigation </span><!--[--><!--]--><!--[--><div class="no-transition group" data-v-c40bc020><section class="VPSidebarItem level-0" data-v-c40bc020 data-v-b3fd67f8><div class="item" role="button" tabindex="0" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><h2 class="text" data-v-b3fd67f8>Getting Started</h2><!----></div><div class="items" data-v-b3fd67f8><!--[--><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Welcome</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/getting-started" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Quickstart</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-c40bc020><section class="VPSidebarItem level-0" data-v-c40bc020 data-v-b3fd67f8><div class="item" role="button" tabindex="0" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><h2 class="text" data-v-b3fd67f8>Concepts</h2><!----></div><div class="items" data-v-b3fd67f8><!--[--><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/concepts" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Concepts Overview</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/concepts/routing" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Webflo Routing</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/concepts/rendering" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Rendering</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/concepts/templates" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Templates</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/concepts/state" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>State Management</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/concepts/request-response" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Request/Response Lifecycle</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/concepts/realtime" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Webflo Realtime</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-c40bc020><section class="VPSidebarItem level-0" data-v-c40bc020 data-v-b3fd67f8><div class="item" role="button" tabindex="0" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><h2 class="text" data-v-b3fd67f8>Advanced</h2><!----></div><div class="items" data-v-b3fd67f8><!--[--><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/advanced" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Advanced Overview</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/docs/advanced/redirects" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Redirects</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-c40bc020><section class="VPSidebarItem level-0" data-v-c40bc020 data-v-b3fd67f8><div class="item" role="button" tabindex="0" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><h2 class="text" data-v-b3fd67f8>API Reference</h2><!----></div><div class="items" data-v-b3fd67f8><!--[--><section class="VPSidebarItem level-1 collapsible collapsed" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" role="button" tabindex="0" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><h3 class="text" data-v-b3fd67f8>Webflo Routing</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-b3fd67f8><span class="vpi-chevron-right caret-icon" data-v-b3fd67f8></span></div></div><div class="items" data-v-b3fd67f8><!--[--><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/api/webflo-routing/handler" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Handler</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/api/webflo-routing/HttpEvent" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>HttpEvent</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/api/webflo-routing/handler/next" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>next</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/api/webflo-routing/handler/fetch" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>fetch</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" role="button" tabindex="0" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><h3 class="text" data-v-b3fd67f8>Webflo Fetch</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-b3fd67f8><span class="vpi-chevron-right caret-icon" data-v-b3fd67f8></span></div></div><div class="items" data-v-b3fd67f8><!--[--><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/api/webflo-fetch/fetch" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>fetch</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/api/webflo-fetch/Request" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Request</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/api/webflo-fetch/Response" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Response</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/api/webflo-fetch/LiveResponse" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>LiveResponse</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/api/webflo-fetch/FormData" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>FormData</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/api/webflo-fetch/Headers" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Headers</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" role="button" tabindex="0" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><h3 class="text" data-v-b3fd67f8>Webflo Messaging</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-b3fd67f8><span class="vpi-chevron-right caret-icon" data-v-b3fd67f8></span></div></div><div class="items" data-v-b3fd67f8><!--[--><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/api/webflo-messaging/MessageChannel" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>MessageChannel</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/api/webflo-messaging/MessagePort" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>MessagePort</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/api/webflo-messaging/MessageEvent" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>MessageEvent</p><!--]--></a><!----></div><!----></div><!--]--></div></section><!--]--></div></section></div><div class="no-transition group" data-v-c40bc020><section class="VPSidebarItem level-0" data-v-c40bc020 data-v-b3fd67f8><div class="item" role="button" tabindex="0" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><h2 class="text" data-v-b3fd67f8>Guides & Recipes</h2><!----></div><div class="items" data-v-b3fd67f8><!--[--><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/guides/tutorial-1-todo" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Tutorial: Todo App</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/guides/guide-auth" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Auth</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/guides/guide-file-upload" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>File Upload</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/guides/guide-service-worker" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Service Worker</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/recipes/streaming" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Streaming</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/recipes/realtime" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Realtime Patterns</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-c40bc020><section class="VPSidebarItem level-0" data-v-c40bc020 data-v-b3fd67f8><div class="item" role="button" tabindex="0" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><h2 class="text" data-v-b3fd67f8>Examples</h2><!----></div><div class="items" data-v-b3fd67f8><!--[--><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/examples/web" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Web Example</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/examples/pwa" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>PWA Example</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-c40bc020><section class="VPSidebarItem level-0" data-v-c40bc020 data-v-b3fd67f8><div class="item" role="button" tabindex="0" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><h2 class="text" data-v-b3fd67f8>Reference</h2><!----></div><div class="items" data-v-b3fd67f8><!--[--><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/reference/cli" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>CLI</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/reference/config" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Config</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/reference/tools" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Tools</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/faq" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>FAQ</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b3fd67f8 data-v-b3fd67f8><div class="item" data-v-b3fd67f8><div class="indicator" data-v-b3fd67f8></div><a class="VPLink link link" href="/contributing" data-v-b3fd67f8><!--[--><p class="text" data-v-b3fd67f8>Contributing</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><!--]--><!--[--><!--]--></nav></aside><div class="VPContent has-sidebar" id="VPContent" data-v-5d98c3a5 data-v-1428d186><div class="VPDoc has-sidebar has-aside" data-v-1428d186 data-v-39a288b8><!--[--><!--]--><div class="container" data-v-39a288b8><div class="aside" data-v-39a288b8><div class="aside-curtain" data-v-39a288b8></div><div class="aside-container" data-v-39a288b8><div class="aside-content" data-v-39a288b8><div class="VPDocAside" data-v-39a288b8 data-v-3f215769><!--[--><!--]--><!--[--><!--]--><nav aria-labelledby="doc-outline-aria-label" class="VPDocAsideOutline" data-v-3f215769 data-v-a5bbad30><div class="content" data-v-a5bbad30><div class="outline-marker" data-v-a5bbad30></div><div aria-level="2" class="outline-title" id="doc-outline-aria-label" role="heading" data-v-a5bbad30>On this page</div><ul class="VPDocOutlineItem root" data-v-a5bbad30 data-v-b933a997><!--[--><!--]--></ul></div></nav><!--[--><!--]--><div class="spacer" data-v-3f215769></div><!--[--><!--]--><!----><!--[--><!--]--><!--[--><!--]--></div></div></div></div><div class="content" data-v-39a288b8><div class="content-container" data-v-39a288b8><!--[--><!--]--><main class="main" data-v-39a288b8><div style="position:relative;" class="vp-doc _-_docs_old" data-v-39a288b8><div><h1 id="webflo" tabindex="-1">Webflo <a class="header-anchor" href="#webflo" aria-label="Permalink to "Webflo""></a></h1><p><span class="badge-npmversion"><a href="https://npmjs.org/package/@webqit/webflo" title="View this project on NPM"><img src="https://img.shields.io/npm/v/@webqit/webflo.svg" alt="NPM version"></a></span></p><p>Webflo is a universal <em>web</em>, <em>mobile</em>, and <em>API backend</em> framework based on vanilla HTML, CSS, and JavaScript! It's a powerful little thing written to facilitate building more authentic, web-native applications!</p><p>Here, we've put all of that up for a 20mins straight read!</p><h2 id="documentation" tabindex="-1">Documentation <a class="header-anchor" href="#documentation" aria-label="Permalink to "Documentation""></a></h2><p>(A bit of a long read!)</p><ul><li><a href="#overview">Overview</a></li><li><a href="#installation">Installation</a></li><li><a href="#concepts">Concepts</a><ul><li><a href="#handler-functions-and-layout">Handler Functions and Layout</a></li><li><a href="#step-functions-and-pipelines">Step Functions and Routing Pipelines</a></li><li><a href="#pages-layout-and-templating">Pages, Layout and Templating</a></li><li><a href="#client-and-server-side-rendering">Client and Server-Side Rendering</a></li><li><a href="#requests-and-responses">Requests and Responses</a></li></ul></li><li><a href="#webflo-applications">Webflo Applications</a><ul><li><a href="#client-side-applications">Client-Side Applications</a></li><li><a href="#progressive-web-apps">Progressive Web Apps</a></li><li><a href="#api-backends">API Backends</a></li><li><a href="#static-sites">Static Sites</a></li></ul></li><li><a href="#webflo-config">Webflo Config</a></li><li><a href="#webflo-tooling">Webflo Tooling</a><ul><li><a href="#oohtml">OOHTML</a></li><li><a href="#oohtml-ssr">OOHTML SSR</a></li><li><a href="#oohtml-cli">OOHTML CLI</a></li><li><a href="#the-observer-api">The Observer API</a></li></ul></li><li><a href="#getting-started">Getting Started</a></li><li><a href="#getting-involved">Getting Involved</a></li></ul><h2 id="overview" tabindex="-1">Overview <a class="header-anchor" href="#overview" aria-label="Permalink to "Overview""></a></h2><details><summary><b>Build <i>anything</i></b> - from as basic as a static <code>index.html</code> page to as rich as a <i>Multi Page Application (MPA)</i>, or <i>Single Page Application (SPA)</i>, or a hybrid of both, implementing <i>Server Side Rendering (SSR)</i>, <i>Client Side Rendering (CSR)</i>, or a hybrid of both, having offline and <i>PWA</i> capabilities, etc. - this time, <i>without loosing the vanilla advantage</i>! </summary><p>Here's a glimpse of your Webflo app.</p><p>For when your application is a static site, or has static files to serve:</p><ul><li><p>The <code>public</code> directory for static files.</p><div class="language-shell"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#FFCB6B;">my-app</span></span>
|
|
52
|
+
<span class="line"><span style="color:#FFCB6B;"> ├──</span><span style="color:#C3E88D;"> public/index.html</span><span style="color:#C3E88D;"> -----------------</span><span style="color:#C3E88D;"> http://localhost:3000/index.html</span><span style="color:#89DDFF;"> |</span><span style="color:#FFCB6B;"> http://localhost:3000</span></span>
|
|
53
|
+
<span class="line"><span style="color:#FFCB6B;"> └──</span><span style="color:#C3E88D;"> public/logo.png</span><span style="color:#C3E88D;"> -------------------</span><span style="color:#C3E88D;"> http://localhost:3000/logo.png</span></span></code></pre></div></li></ul><p>For when your application requires dynamic request handling on the server:</p><ul><li><p>The <code>server</code> directory for server-side routing.</p><div class="language-shell"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#FFCB6B;">my-app</span></span>
|
|
54
|
+
<span class="line"><span style="color:#FFCB6B;"> └──</span><span style="color:#C3E88D;"> server/index.js</span></span></code></pre></div><p>In which case a typical <code>index.js</code> route handler has the following anatomy:</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
55
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">server</span></span>
|
|
56
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.js</span></span>
|
|
57
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
|
|
58
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#89DDFF;font-style:italic;"> default</span><span style="color:#C792EA;"> function</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
59
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">next</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">pathname</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
|
|
60
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#82AAFF;"> next</span><span style="color:#F07178;">()</span><span style="color:#89DDFF;">;</span><span style="color:#676E95;font-style:italic;"> // <--------------------------------- http://localhost:3000/logo.png (or other non-root URLs)</span></span>
|
|
61
|
+
<span class="line"><span style="color:#89DDFF;"> }</span></span>
|
|
62
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> title</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">Hello from Server</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> };</span><span style="color:#676E95;font-style:italic;"> // <------------- http://localhost:3000/ (root URL)</span></span>
|
|
63
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><p>In which case data is returned either as a JSON (API) response, or as a rendered page response where there is an <code>index.html</code> file in the <code>public</code> directory that pairs with the route.</p><div class="language-shell"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#FFCB6B;">my-app</span></span>
|
|
64
|
+
<span class="line"><span style="color:#FFCB6B;"> ├──</span><span style="color:#C3E88D;"> server/index.js</span></span>
|
|
65
|
+
<span class="line"><span style="color:#FFCB6B;"> └──</span><span style="color:#C3E88D;"> public/index.html</span></span></code></pre></div><p>In which case a typical <code>index.html</code> page has the following anatomy:</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;"><!--</span></span>
|
|
66
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">public</span></span>
|
|
67
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.html</span></span>
|
|
68
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">--></span></span>
|
|
69
|
+
<span class="line"><span style="color:#89DDFF;"><!</span><span style="color:#F07178;">DOCTYPE</span><span style="color:#C792EA;"> html</span><span style="color:#89DDFF;">></span></span>
|
|
70
|
+
<span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span>
|
|
71
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
72
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">link</span><span style="color:#C792EA;"> rel</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">stylesheet</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> href</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/app.css</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;"> /></span><span style="color:#676E95;font-style:italic;"> <!-- ---------------------- Application CSS --></span></span>
|
|
73
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#C792EA;"> type</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">module</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/app.js</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span><span style="color:#676E95;font-style:italic;"> <!-- ----------------- Application JS bundle --></span></span>
|
|
74
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">template</span><span style="color:#C792EA;"> def</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">app</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/app.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">></span><span style="color:#676E95;font-style:italic;"> <!-- ------------- Reusable HTML Templates and partials (Details ahead) --></span></span>
|
|
75
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
76
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span><span style="color:#BABED8;">...</span><span style="color:#89DDFF;"></</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span>
|
|
77
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span></code></pre></div></li></ul><p>For when your application requires dynamic request handling on the client (the browser):</p><ul><li><p>The <code>client</code> directory for client-side routing,</p></li><li><p>And, optionally, the <code>worker</code> directory for an all-new Service Worker based routing! (As detailed ahead.)</p><div class="language-shell"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#FFCB6B;">my-app</span></span>
|
|
78
|
+
<span class="line"><span style="color:#FFCB6B;"> ├──</span><span style="color:#C3E88D;"> client/index.js</span></span>
|
|
79
|
+
<span class="line"><span style="color:#FFCB6B;"> └──</span><span style="color:#C3E88D;"> worker/index.js</span></span></code></pre></div><p>Where in both cases, a typical <code>index.js</code> route handler has the same familiar anatomy:</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
80
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">[client|worker]</span></span>
|
|
81
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.js</span></span>
|
|
82
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
|
|
83
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#89DDFF;font-style:italic;"> default</span><span style="color:#C792EA;"> function</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
84
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">next</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">pathname</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
|
|
85
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#82AAFF;"> next</span><span style="color:#F07178;">()</span><span style="color:#89DDFF;">;</span><span style="color:#676E95;font-style:italic;"> // <--------------------------------- http://localhost:3000/logo.png (or other non-root URLs)</span></span>
|
|
86
|
+
<span class="line"><span style="color:#89DDFF;"> }</span></span>
|
|
87
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> title</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">Hello from [Browser|Worker]</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> };</span><span style="color:#676E95;font-style:italic;"> // <--- http://localhost:3000/ (root URL)</span></span>
|
|
88
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div></li></ul><p>This and much more - ahead!</p></details><details><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><p>Here's a glimpse of the standards-based stack you get of Webflo!</p><p>For when your application involves routing:</p><ul><li><p><a href="https://fetch.spec.whatwg.org/" target="_blank" rel="noreferrer">The Fetch Standard</a>, comprising of the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Request" target="_blank" rel="noreferrer">Request</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/API/Response" target="_blank" rel="noreferrer">Response</a>, and <a href="https://developer.mozilla.org/en-US/docs/Web/API/Headers" target="_blank" rel="noreferrer">Headers</a> interfaces - used for all things <em>requests and responses</em> - across client, server, and Service Worker environments. (<a href="#requests-and-responses">Details ahead</a>)</p><blockquote><p>This paves the way to using other native APIs as-is, when handling requests and responses. For example, if you sent an instance of the native <a href="https://developer.mozilla.org/en-US/docs/Web/API/FormData" target="_blank" rel="noreferrer">FormData</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob" target="_blank" rel="noreferrer">Blob</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/API/File" target="_blank" rel="noreferrer">File</a>, or <a href="https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream" target="_blank" rel="noreferrer">ReadableStream</a> object from the browser side of your application, you'd be getting the equivalent instance on the server side!</p></blockquote></li><li><p><a href="https://url.spec.whatwg.org/" target="_blank" rel="noreferrer">WHATWG URL</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/URLPattern" target="_blank" rel="noreferrer">WHATWG URLPattern</a> - used for all things <em>URL</em> and <em>URL pattern matching</em>, respectively - across client, server, and Service Worker environments. (<a href="#">Details ahead</a>)</p></li></ul><p>For when your application involves pages and a UI:</p><ul><li><p><a href="https://html.spec.whatwg.org/" target="_blank" rel="noreferrer">The HTML Standard</a> - held for all things <em>markup</em> - across client, server, and Service Worker environments! You'll absolutely ❤ love it that your pages and page components can live as plain <code>.html</code> files! The browser already does!</p><blockquote><p>This HTML-first approach is new! And you can get away with a "zero-JavaScript" proposition, or something more of a <em>Progressive Enhancement</em> proposition that makes do with "just-enough JavaScript"!</p></blockquote></li><li><p><a href="https://dom.spec.whatwg.org/" target="_blank" rel="noreferrer">WHATWG DOM</a> - available universally - not only on the client-side, but also on the server-side via <a href="#oohtml-ssr">OOHTML-SSR</a> - all of which lets us have <em>dynamic pages</em>.</p><blockquote><p>And you get to have <a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements" target="_blank" rel="noreferrer">Custom Elements</a> as powerful building blocks.</p></blockquote></li></ul><p>Same web standards everwhere you look! You come off with a web-native app!</p></details><h2 id="installation" tabindex="-1">Installation <a class="header-anchor" href="#installation" aria-label="Permalink to "Installation""></a></h2><p>Every Webflo project starts on an empty directory that you can create on your machine. The command below makes a new directory <code>my-app</code> from the terminal and navigates into it.</p><div class="language-shell"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#FFCB6B;">mkdir</span><span style="color:#C3E88D;"> my-app</span></span>
|
|
89
|
+
<span class="line"><span style="color:#82AAFF;">cd</span><span style="color:#C3E88D;"> my-app</span></span></code></pre></div><p>With <a href="https://docs.npmjs.com/downloading-and-installing-node-js-and-npm" target="_blank" rel="noreferrer">npm available on your terminal</a>, run the following command to install Webflo to your project:</p><blockquote><p>System Requirements: Node.js 18.0 (having stable Fetch API support) or later</p></blockquote><div class="language-shell"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#FFCB6B;">npm</span><span style="color:#C3E88D;"> i</span><span style="color:#C3E88D;"> @webqit/webflo</span></span></code></pre></div><p>The installation automatically creates a <code>package.json</code> file at project root, containing <code>@webqit/webflo</code> as a project dependency.</p><div class="language-json"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">{</span></span>
|
|
90
|
+
<span class="line"><span style="color:#89DDFF;"> "</span><span style="color:#C792EA;">dependencies</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> {</span></span>
|
|
91
|
+
<span class="line"><span style="color:#89DDFF;"> "</span><span style="color:#FFCB6B;">@webqit/webflo</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> "</span><span style="color:#C3E88D;">...</span><span style="color:#89DDFF;">"</span></span>
|
|
92
|
+
<span class="line"><span style="color:#89DDFF;"> }</span></span>
|
|
93
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><p>Other important definitions like project <code>name</code>, package <code>type</code>, and <em>aliases</em> for common Webflo commands will now also belong here.</p><div class="language-json"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">{</span></span>
|
|
94
|
+
<span class="line"><span style="color:#89DDFF;"> "</span><span style="color:#C792EA;">name</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> "</span><span style="color:#C3E88D;">my-app</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">,</span></span>
|
|
95
|
+
<span class="line"><span style="color:#89DDFF;"> "</span><span style="color:#C792EA;">type</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> "</span><span style="color:#C3E88D;">module</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">,</span></span>
|
|
96
|
+
<span class="line"><span style="color:#89DDFF;"> "</span><span style="color:#C792EA;">scripts</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> {</span></span>
|
|
97
|
+
<span class="line"><span style="color:#89DDFF;"> "</span><span style="color:#FFCB6B;">start</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> "</span><span style="color:#C3E88D;">webflo start::server --mode=dev</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">,</span></span>
|
|
98
|
+
<span class="line"><span style="color:#89DDFF;"> "</span><span style="color:#FFCB6B;">generate</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> "</span><span style="color:#C3E88D;">webflo generate::client --compression=gz --auto-embed</span><span style="color:#89DDFF;">"</span></span>
|
|
99
|
+
<span class="line"><span style="color:#89DDFF;"> },</span></span>
|
|
100
|
+
<span class="line"><span style="color:#89DDFF;"> "</span><span style="color:#C792EA;">dependencies</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> {</span></span>
|
|
101
|
+
<span class="line"><span style="color:#89DDFF;"> "</span><span style="color:#FFCB6B;">@webqit/webflo</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> "</span><span style="color:#C3E88D;">...</span><span style="color:#89DDFF;">"</span></span>
|
|
102
|
+
<span class="line"><span style="color:#89DDFF;"> }</span></span>
|
|
103
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><p>And that gets it all ready! The commands <code>npm start</code> and <code>npm run generate</code> will be coming in often during development.</p><h3 id="hello-world" tabindex="-1">"Hello World!" <a class="header-anchor" href="#hello-world" aria-label="Permalink to ""Hello World!"""></a></h3><p>To be sure that Webflo is listening, run <code>npx webflo help</code> on the terminal. An overview of available commands should be shown.</p><p>If you can't wait to say <em>Hello World!</em> 😅, you can have an HTML page say that right away!</p><ul><li><p>Create an <code>index.html</code> file in a new subdirectory <code>public</code>.</p><div class="language-shell"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#FFCB6B;">public</span></span>
|
|
104
|
+
<span class="line"><span style="color:#FFCB6B;"> └──</span><span style="color:#C3E88D;"> index.html</span></span></code></pre></div><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;"><!</span><span style="color:#F07178;">DOCTYPE</span><span style="color:#C792EA;"> html</span><span style="color:#89DDFF;">></span></span>
|
|
105
|
+
<span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span>
|
|
106
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
107
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">title</span><span style="color:#89DDFF;">></span><span style="color:#BABED8;">My App</span><span style="color:#89DDFF;"></</span><span style="color:#F07178;">title</span><span style="color:#89DDFF;">></span></span>
|
|
108
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
109
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span>
|
|
110
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">h1</span><span style="color:#89DDFF;">></span><span style="color:#BABED8;">Hello World!</span><span style="color:#89DDFF;"></</span><span style="color:#F07178;">h1</span><span style="color:#89DDFF;">></span></span>
|
|
111
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">p</span><span style="color:#89DDFF;">></span><span style="color:#BABED8;">This is </span><span style="color:#89DDFF;"><</span><span style="color:#F07178;">b</span><span style="color:#89DDFF;">></span><span style="color:#BABED8;">My App</span><span style="color:#89DDFF;"></</span><span style="color:#F07178;">b</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">p</span><span style="color:#89DDFF;">></span></span>
|
|
112
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span>
|
|
113
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span></code></pre></div></li><li><p>Start the Webflo server and visit <code>http://localhost:3000</code> on your browser to see your page. 😃</p><div class="language-bash"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#FFCB6B;">npm</span><span style="color:#C3E88D;"> start</span></span></code></pre></div></li><li><p>Welcome to Webflo!</p></li></ul><blockquote><p><strong>Note</strong><br>This is documentation for Webflo@0.11.x which is compatible with OOHTML@1.x. Webflo@next which is compatible with OOHTML@2.x is coming soon!</p></blockquote><h2 id="concepts" tabindex="-1">Concepts <a class="header-anchor" href="#concepts" aria-label="Permalink to "Concepts""></a></h2><ul><li><a href="#handler-functions-and-layout">Handler Functions and Layout</a></li><li><a href="#step-functions-and-pipelines">Step Functions and Routing Pipelines</a></li><li><a href="#pages-layout-and-templating">Pages, Layout and Templating</a></li><li><a href="#client-and-server-side-rendering">Client and Server-Side Rendering</a></li><li><a href="#requests-and-responses">Requests and Responses</a></li></ul><h3 id="handler-functions-and-layout" tabindex="-1">Handler Functions and Layout <a class="header-anchor" href="#handler-functions-and-layout" aria-label="Permalink to "Handler Functions and Layout""></a></h3><p>Functions come in in Webflo when you need to dynamically handle requests.</p><p>Whether building a <em>server-based</em>, <em>browser-based</em>, or <em>universal</em> application, Webflo gives you one consistent way to handle requests and navigation: using <em>handler functions</em>!</p><p>You just define an <code>index.js</code> file with a function that gets called to handle a request!</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
114
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">[server|client|worker]</span></span>
|
|
115
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.js</span></span>
|
|
116
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
|
|
117
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#89DDFF;font-style:italic;"> default</span><span style="color:#C792EA;"> function</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> fetch</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
118
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><details><summary>Functions may have a name...</summary><blockquote><p>The following function handles only <code>GET</code> requests:</p></blockquote><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#C792EA;"> function</span><span style="color:#82AAFF;"> GET</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> fetch</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
119
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><blockquote><p>Function names take after <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods" target="_blank" rel="noreferrer"><em>HTTP methods</em></a>: <code>GET</code>, <code>POST</code>, <code>PUT</code>, <code>PATCH</code>, <code>DELETE</code>, <code>OPTIONS</code>, <code>HEAD</code>, etc.</p></blockquote><blockquote><p>Function names are lower case for Webflo version <= <code>0.11.23</code>, in which case <code>delete</code> is <code>del</code>.</p></blockquote></details><details><summary>Functions may also may be <code>async</code>...</summary><blockquote><p>The following function can simply <code>await</code> asynchronous stuff:</p></blockquote><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#C792EA;"> async</span><span style="color:#C792EA;"> function</span><span style="color:#82AAFF;"> GET</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> fetch</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
120
|
+
<span class="line"><span style="color:#C792EA;"> let</span><span style="color:#BABED8;"> a</span><span style="color:#89DDFF;"> =</span><span style="color:#89DDFF;font-style:italic;"> await</span><span style="color:#82AAFF;"> b</span><span style="color:#F07178;">()</span><span style="color:#89DDFF;">;</span></span>
|
|
121
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div></details><p>Each function receives an <code>event</code> object representing details about the request - e.g. <code>event.request</code>, <code>event.url</code>, <code>event.session</code>. (<a href="#pipeline-api">Details ahead</a>.)</p><p>While the <code>context</code> and <code>next</code> parameters are discussed below, <code>fetch</code> is a <a href="https://fetch.spec.whatwg.org/" target="_blank" rel="noreferrer">fetch</a>-equivalent function passed in for <em>convenience</em> - for initiating remote requests.</p><p><strong>Functions that will respond to requests on the server-side</strong> go into a directory named <code>server</code>.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
122
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">server</span></span>
|
|
123
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.js</span></span>
|
|
124
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
|
|
125
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#89DDFF;font-style:italic;"> default</span><span style="color:#C792EA;"> function</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
126
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">next</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">pathname</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#82AAFF;"> next</span><span style="color:#F07178;">()</span><span style="color:#89DDFF;">;</span></span>
|
|
127
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span></span>
|
|
128
|
+
<span class="line"><span style="color:#F07178;"> title</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">Home | FluffyPets</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span></span>
|
|
129
|
+
<span class="line"><span style="color:#F07178;"> source</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">server</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span></span>
|
|
130
|
+
<span class="line"><span style="color:#89DDFF;"> };</span></span>
|
|
131
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><details><summary>How it works...</summary><blockquote><p>The above function will respond on starting the server - <code>npm start</code> on your terminal - and visiting <a href="http://localhost:3000" target="_blank" rel="noreferrer">http://localhost:3000</a>.</p></blockquote></details><p><strong>Funtions that will respond to requests on the client-side (from right within the browser)</strong> go into a directory named <code>client</code>.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
132
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">client</span></span>
|
|
133
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.js</span></span>
|
|
134
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
|
|
135
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#89DDFF;font-style:italic;"> default</span><span style="color:#C792EA;"> function</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
136
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">next</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">pathname</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#82AAFF;"> next</span><span style="color:#F07178;">()</span><span style="color:#89DDFF;">;</span></span>
|
|
137
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span></span>
|
|
138
|
+
<span class="line"><span style="color:#F07178;"> title</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">Home | FluffyPets</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span></span>
|
|
139
|
+
<span class="line"><span style="color:#F07178;"> source</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">in-browser</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span></span>
|
|
140
|
+
<span class="line"><span style="color:#89DDFF;"> };</span></span>
|
|
141
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><details><summary>How it works...</summary><blockquote><p>The above function is built as part of your application's client-side script from the <code>npm run generate</code> command. It is typically bundled to the file <code>./public/bundle.js</code>. And the <code>--auto-embed</code> flag in that command gets it automatically embedded on your <code>./public/index.html</code> page as <code><script type="module" src="/bundle.js"></script></code>. Then it responds from right in the browser on visiting <a href="http://localhost:3000" target="_blank" rel="noreferrer">http://localhost:3000</a>.</p></blockquote></details><p>For <em>browser-based</em> applications that want to employ Service-Workers (typically, Progressive Web Apps), Webflo allows for equivalent request handlers as part of the Service Worker. These <strong>worker-based</strong> functions go into a directory named <code>worker</code>.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
142
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">worker</span></span>
|
|
143
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.js</span></span>
|
|
144
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
|
|
145
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#89DDFF;font-style:italic;"> default</span><span style="color:#C792EA;"> function</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
146
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">next</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">pathname</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#82AAFF;"> next</span><span style="color:#F07178;">()</span><span style="color:#89DDFF;">;</span></span>
|
|
147
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span></span>
|
|
148
|
+
<span class="line"><span style="color:#F07178;"> title</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">Home | FluffyPets</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span></span>
|
|
149
|
+
<span class="line"><span style="color:#F07178;"> source</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">service-worker</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span></span>
|
|
150
|
+
<span class="line"><span style="color:#89DDFF;"> };</span></span>
|
|
151
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><details><summary>How it works...</summary><blockquote><p>The above function is built as part of your application's Service Worker script from the <code>npm run generate</code> command. It is typically bundled to the file <code>./public/worker.js</code>, and the main application bundle automatically registers this as the application's Service Worker. Now, our function responds from within the Service Worker on visiting <a href="http://localhost:3000" target="_blank" rel="noreferrer">http://localhost:3000</a>. (More details <a href="#service-workers">ahead</a>.)</p></blockquote></details><p>So, depending on where requests are best handled for your type of application, handler functions may be placed as below:</p><div class="language-shell"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#FFCB6B;">client</span></span>
|
|
152
|
+
<span class="line"><span style="color:#FFCB6B;"> ├──</span><span style="color:#C3E88D;"> index.js</span></span></code></pre></div><div class="language-shell"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#FFCB6B;">worker</span></span>
|
|
153
|
+
<span class="line"><span style="color:#FFCB6B;"> ├──</span><span style="color:#C3E88D;"> index.js</span></span></code></pre></div><div class="language-shell"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#FFCB6B;">server</span></span>
|
|
154
|
+
<span class="line"><span style="color:#FFCB6B;"> ├──</span><span style="color:#C3E88D;"> index.js</span></span></code></pre></div><p>Static files, e.g. images, stylesheets, etc, have their place in a files directory named <code>public</code>.</p><div class="language-shell"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#FFCB6B;">public</span></span>
|
|
155
|
+
<span class="line"><span style="color:#FFCB6B;"> └──</span><span style="color:#C3E88D;"> logo.png</span></span></code></pre></div><h3 id="step-functions-and-routing-pipelines" tabindex="-1">Step Functions and Routing Pipelines <a class="header-anchor" href="#step-functions-and-routing-pipelines" aria-label="Permalink to "Step Functions and Routing Pipelines""></a></h3><p>Whether routing in the <code>/client</code>, <code>/worker</code>, or <code>/server</code> directory above, nested URLs follow the concept of Step Functions! These are parent-child layout of handlers that model your application's URL structure.</p><div class="language-shell"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#FFCB6B;">server</span></span>
|
|
156
|
+
<span class="line"><span style="color:#FFCB6B;"> ├──</span><span style="color:#C3E88D;"> index.js</span><span style="color:#C3E88D;"> ---------------------------------</span><span style="color:#C3E88D;"> http://localhost:3000</span></span>
|
|
157
|
+
<span class="line"><span style="color:#FFCB6B;"> └──</span><span style="color:#C3E88D;"> products/index.js</span><span style="color:#C3E88D;"> ------------------------</span><span style="color:#C3E88D;"> http://localhost:3000/products</span></span>
|
|
158
|
+
<span class="line"><span style="color:#FFCB6B;"> └──</span><span style="color:#C3E88D;"> stickers/index.js</span><span style="color:#C3E88D;"> ------------------</span><span style="color:#C3E88D;"> http://localhost:3000/products/stickers</span></span></code></pre></div><p>Each step calls a <code>next()</code> function to forward the current request to the next step of the given URL.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
159
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">server</span></span>
|
|
160
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.js</span></span>
|
|
161
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
|
|
162
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#89DDFF;font-style:italic;"> default</span><span style="color:#C792EA;"> async</span><span style="color:#C792EA;"> function</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
163
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">next</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">stepname</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#82AAFF;"> next</span><span style="color:#F07178;">()</span><span style="color:#89DDFF;">;</span></span>
|
|
164
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> title</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">Home | FluffyPets</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> };</span></span>
|
|
165
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
166
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">server</span></span>
|
|
167
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── products/index.js</span></span>
|
|
168
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
|
|
169
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#89DDFF;font-style:italic;"> default</span><span style="color:#C792EA;"> function</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
170
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">next</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">stepname</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#82AAFF;"> next</span><span style="color:#F07178;">()</span><span style="color:#89DDFF;">;</span></span>
|
|
171
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> title</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">Products</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> };</span></span>
|
|
172
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><p>We get a step-based pipeline that helps to decomplicate routing and lets us build out each route horizontally!</p><details><summary>Each step can entirely control the next...</summary><p>Here, a parent step can pass a <code>context</code> object to a child step, and can <em>recompose</em> its return value.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
173
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">server</span></span>
|
|
174
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.js</span></span>
|
|
175
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
|
|
176
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#89DDFF;font-style:italic;"> default</span><span style="color:#C792EA;"> async</span><span style="color:#C792EA;"> function</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
177
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">next</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">stepname</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
|
|
178
|
+
<span class="line"><span style="color:#C792EA;"> let</span><span style="color:#BABED8;"> childContext</span><span style="color:#89DDFF;"> =</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> user</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> id</span><span style="color:#89DDFF;">:</span><span style="color:#F78C6C;"> 2</span><span style="color:#89DDFF;"> },</span><span style="color:#89DDFF;"> };</span></span>
|
|
179
|
+
<span class="line"><span style="color:#C792EA;"> let</span><span style="color:#BABED8;"> childResponse</span><span style="color:#89DDFF;"> =</span><span style="color:#89DDFF;font-style:italic;"> await</span><span style="color:#82AAFF;"> next</span><span style="color:#F07178;">(</span><span style="color:#BABED8;">childContext</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
180
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span><span style="color:#89DDFF;"> ...</span><span style="color:#BABED8;">childResponse</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> title</span><span style="color:#89DDFF;">:</span><span style="color:#BABED8;"> childResponse</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">title</span><span style="color:#89DDFF;"> +</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;"> | FluffyPets</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> };</span></span>
|
|
181
|
+
<span class="line"><span style="color:#89DDFF;"> }</span></span>
|
|
182
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> title</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">Home | FluffyPets</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> };</span></span>
|
|
183
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><details><summary>But a function can just talk to any other function...</summary><p>It's versatile, so the <code>next()</code> function can be used to re-route the current request to a different handler - using a relative or absolute URL.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
184
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">server</span></span>
|
|
185
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.js</span></span>
|
|
186
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
|
|
187
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#89DDFF;font-style:italic;"> default</span><span style="color:#C792EA;"> async</span><span style="color:#C792EA;"> function</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
188
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">next</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">stepname</span><span style="color:#89DDFF;"> ===</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">products</span><span style="color:#89DDFF;">'</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
|
|
189
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#82AAFF;"> next</span><span style="color:#F07178;">(</span><span style="color:#BABED8;">context</span><span style="color:#89DDFF;">,</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">/api/products?params=allowed</span><span style="color:#89DDFF;">'</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span><span style="color:#676E95;font-style:italic;"> // With an absolute URL</span></span>
|
|
190
|
+
<span class="line"><span style="color:#89DDFF;"> }</span></span>
|
|
191
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> title</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">Home | FluffyPets</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> };</span></span>
|
|
192
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
193
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">server</span></span>
|
|
194
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── products/index.js</span></span>
|
|
195
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
|
|
196
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#89DDFF;font-style:italic;"> default</span><span style="color:#C792EA;"> async</span><span style="color:#C792EA;"> function</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
197
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">next</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">stepname</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#82AAFF;"> next</span><span style="color:#F07178;">()</span><span style="color:#89DDFF;">;</span></span>
|
|
198
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#82AAFF;"> next</span><span style="color:#F07178;">(</span><span style="color:#BABED8;">context</span><span style="color:#89DDFF;">,</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">../api/products?params=allowed</span><span style="color:#89DDFF;">'</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span><span style="color:#676E95;font-style:italic;"> // With a relative URL</span></span>
|
|
199
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><p>And you can make that a full-fledged <em>in-app</em> request - passing in <code>fetch</code>-equivalent parameters.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
200
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">server</span></span>
|
|
201
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.js</span></span>
|
|
202
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
|
|
203
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#89DDFF;font-style:italic;"> default</span><span style="color:#C792EA;"> async</span><span style="color:#C792EA;"> function</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
204
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">next</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">stepname</span><span style="color:#89DDFF;"> ===</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">products</span><span style="color:#89DDFF;">'</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
|
|
205
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#82AAFF;"> next</span><span style="color:#F07178;">(</span><span style="color:#BABED8;">context</span><span style="color:#89DDFF;">,</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">/api/products?params=allowed</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#89DDFF;"> {</span></span>
|
|
206
|
+
<span class="line"><span style="color:#F07178;"> method</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">get</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#89DDFF;"> {</span><span style="color:#FFCB6B;"> headers</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> {</span><span style="color:#FFCB6B;"> Authorization</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">djjdd</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> }</span><span style="color:#89DDFF;"> }</span><span style="color:#F07178;"> </span></span>
|
|
207
|
+
<span class="line"><span style="color:#89DDFF;"> }</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
208
|
+
<span class="line"><span style="color:#89DDFF;"> }</span></span>
|
|
209
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> title</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">Home | FluffyPets</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> };</span></span>
|
|
210
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><p>These requests are received at destination route - <code>/api/products</code> above - as regular HTTP requests!</p></details></details><p>For even more flexibility, pipelines may be designed with <em>wildcard</em> steps using a hyphen <code>-</code> as step name. At runtime, a wildcard step matches any URL segment at its level in the hierarchy! A <code>this.stepname</code> property is always available to tell which URL segment has been matched.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
211
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">server</span></span>
|
|
212
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── -/index.js</span></span>
|
|
213
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
|
|
214
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#89DDFF;font-style:italic;"> default</span><span style="color:#C792EA;"> function</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
215
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#89DDFF;">this.</span><span style="color:#BABED8;">stepname</span><span style="color:#89DDFF;"> ===</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">products</span><span style="color:#89DDFF;">'</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
|
|
216
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> title</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">Products</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> };</span></span>
|
|
217
|
+
<span class="line"><span style="color:#89DDFF;"> }</span></span>
|
|
218
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> title</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">Untitled</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> };</span></span>
|
|
219
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><details><summary>More details...</summary><blockquote><p>Every handler function has the following contextual properties:</p><ul><li><code>this.stepname</code> - The name of the current step.</li><li><code>this.pathname</code> - The URL pathname of the current step.</li><li><code>next.stepname</code> - The name of the next step.</li><li><code>next.pathname</code> - The remaining URL pathname after the current step. Server-side handlers have the following in addition:</li><li><code>this.dirname</code> - The filesystem pathname of the current step.</li></ul></blockquote></details><p>Additionally, pipelines may be designed with as many or as few step functions as necessary; the flow control parameters <code>next.stepname</code> and <code>next.pathname</code> are always available at any point to help with the remaining part of the given URL.</p><p>This means that it is even possible to handle all URLs from the root handler alone.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
220
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">server</span></span>
|
|
221
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.js</span></span>
|
|
222
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
|
|
223
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#89DDFF;font-style:italic;"> default</span><span style="color:#C792EA;"> function</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
224
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> // For http://localhost:3000/products</span></span>
|
|
225
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">next</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">pathname</span><span style="color:#89DDFF;"> ===</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">products</span><span style="color:#89DDFF;">'</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
|
|
226
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> title</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">Products</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> };</span></span>
|
|
227
|
+
<span class="line"><span style="color:#89DDFF;"> }</span></span>
|
|
228
|
+
<span class="line"></span>
|
|
229
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> // For http://localhost:3000/products/stickers</span></span>
|
|
230
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">next</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">pathname</span><span style="color:#89DDFF;"> ===</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">products/stickers</span><span style="color:#89DDFF;">'</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
|
|
231
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> title</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">Stickers</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> };</span></span>
|
|
232
|
+
<span class="line"><span style="color:#89DDFF;"> }</span></span>
|
|
233
|
+
<span class="line"><span style="color:#F07178;"> </span></span>
|
|
234
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> // Should we later support other URLs like static assets http://localhost:3000/logo.png</span></span>
|
|
235
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">next</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">pathname</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
|
|
236
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#82AAFF;"> next</span><span style="color:#F07178;">()</span><span style="color:#89DDFF;">;</span></span>
|
|
237
|
+
<span class="line"><span style="color:#89DDFF;"> }</span></span>
|
|
238
|
+
<span class="line"><span style="color:#F07178;"> </span></span>
|
|
239
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> // For the root URL http://localhost:3000</span></span>
|
|
240
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> title</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">Home</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> };</span></span>
|
|
241
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><p>The <code>next()</code> function still plays an important role above because Webflo takes a <em>default action</em> when <code>next()</code> is called at the <em>edge</em> of the pipeline - this point where there are no more step functions as there are URL segments.</p><p><strong>For pipelines in the <code>/server</code> directory</strong>, the <em>default action</em> of <code>next()</code>ing at the edge is to go match and return a static file in the <code>public</code> directory.</p><p>So, above, should our handler receive static file requests like <code>http://localhost:3000/logo.png</code>, the statement <code>return next()</code> would get Webflo to match and return the logo at <code>public/logo.png</code>, if any; a <code>404</code> response otherwise.</p><div class="language-shell"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#FFCB6B;">my-app</span></span>
|
|
242
|
+
<span class="line"><span style="color:#FFCB6B;"> ├──</span><span style="color:#C3E88D;"> server/index.js</span><span style="color:#C3E88D;"> -------------------------</span><span style="color:#C3E88D;"> http://localhost:3000,</span><span style="color:#C3E88D;"> http://localhost:3000/products,</span><span style="color:#C3E88D;"> http://localhost:3000/products/stickers,</span><span style="color:#C3E88D;"> etc</span></span>
|
|
243
|
+
<span class="line"><span style="color:#FFCB6B;"> └──</span><span style="color:#C3E88D;"> public/logo.png</span><span style="color:#C3E88D;"> -------------------------</span><span style="color:#C3E88D;"> http://localhost:3000/logo.png</span></span></code></pre></div><blockquote><p><strong>Note</strong><br>Obviously, the root handler effectively becomes the single point of entry to the application - being that it sees even requests for static files!</p></blockquote><p><strong>For pipelines in the <code>/worker</code> directory</strong>, the <em>default action</em> of <code>next()</code>ing at the edge is to send the request through the network to the server. (But Webflo will check to see whether to (and how to) resolve the request from the application cache.)</p><p>So, above, if we defined handler functions in the <code>/worker</code> directory, we could selectively handle specific requests while <code>next()</code>ing others to the server.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
244
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">worker</span></span>
|
|
245
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.js</span></span>
|
|
246
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
|
|
247
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#89DDFF;font-style:italic;"> default</span><span style="color:#C792EA;"> async</span><span style="color:#C792EA;"> function</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
248
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> // For http://localhost:3000/about</span></span>
|
|
249
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">next</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">pathname</span><span style="color:#89DDFF;"> ===</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">about</span><span style="color:#89DDFF;">'</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
|
|
250
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span></span>
|
|
251
|
+
<span class="line"><span style="color:#F07178;"> name</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">FluffyPets</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span></span>
|
|
252
|
+
<span class="line"><span style="color:#F07178;"> version</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">1.0</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span></span>
|
|
253
|
+
<span class="line"><span style="color:#89DDFF;"> };</span></span>
|
|
254
|
+
<span class="line"><span style="color:#89DDFF;"> }</span></span>
|
|
255
|
+
<span class="line"><span style="color:#F07178;"> </span></span>
|
|
256
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> // For http://localhost:3000/logo.png</span></span>
|
|
257
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">next</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">pathname</span><span style="color:#89DDFF;"> ===</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">logo.png</span><span style="color:#89DDFF;">'</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
|
|
258
|
+
<span class="line"><span style="color:#C792EA;"> let</span><span style="color:#BABED8;"> response</span><span style="color:#89DDFF;"> =</span><span style="color:#89DDFF;font-style:italic;"> await</span><span style="color:#82AAFF;"> next</span><span style="color:#F07178;">()</span><span style="color:#89DDFF;">;</span></span>
|
|
259
|
+
<span class="line"><span style="color:#BABED8;"> console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">( </span><span style="color:#89DDFF;">'</span><span style="color:#C3E88D;">Logo file size:</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;"> response</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">headers</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">get</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">'</span><span style="color:#C3E88D;">Content-Length</span><span style="color:#89DDFF;">'</span><span style="color:#F07178;">) )</span><span style="color:#89DDFF;">;</span></span>
|
|
260
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#BABED8;"> response</span><span style="color:#89DDFF;">;</span></span>
|
|
261
|
+
<span class="line"><span style="color:#89DDFF;"> }</span></span>
|
|
262
|
+
<span class="line"><span style="color:#F07178;"> </span></span>
|
|
263
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> // For every other URL</span></span>
|
|
264
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#82AAFF;"> next</span><span style="color:#F07178;">()</span><span style="color:#89DDFF;">;</span></span>
|
|
265
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><p>Now we get the following handler-to-URL mapping for our application:</p><div class="language-shell"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#FFCB6B;">my-app</span></span>
|
|
266
|
+
<span class="line"><span style="color:#FFCB6B;"> ├──</span><span style="color:#C3E88D;"> worker/index.js</span><span style="color:#C3E88D;"> -------------------------</span><span style="color:#C3E88D;"> http://localhost:3000/about,</span><span style="color:#C3E88D;"> http://localhost:3000/logo.png</span></span>
|
|
267
|
+
<span class="line"><span style="color:#FFCB6B;"> ├──</span><span style="color:#C3E88D;"> server/index.js</span><span style="color:#C3E88D;"> -------------------------</span><span style="color:#C3E88D;"> http://localhost:3000,</span><span style="color:#C3E88D;"> http://localhost:3000/products,</span><span style="color:#C3E88D;"> http://localhost:3000/products/stickers,</span><span style="color:#C3E88D;"> etc</span></span>
|
|
268
|
+
<span class="line"><span style="color:#FFCB6B;"> └──</span><span style="color:#C3E88D;"> public/logo.png</span><span style="color:#C3E88D;"> -------------------------</span><span style="color:#C3E88D;"> http://localhost:3000/logo.png</span></span></code></pre></div><details><summary>More details...</summary><blockquote><p>Handlers in the <code>/worker</code> directory see only Same-Origin requests, being that Cross-Origin URLs like <code>https://auth.example.com/oauth</code> do not belong in the application's layout! But as detailed later, these external URLs may be may configured for strategic caching by the Service Worker.</p></blockquote></details><p><strong>For pipelines in the <code>/client</code> directory</strong>, the <em>default action</em> of <code>next()</code>ing at the edge is to send the request through the network to the server. But where there is a Service Worker layer, then that becomes the next destination.</p><p>So, above, if we defined handler functions in the <code>/client</code> directory, we could selectively handle specific navigation requests in-browser while <code>next()</code>ing others down to the server, or first, the Service Worker layer.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
269
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">client</span></span>
|
|
270
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.js</span></span>
|
|
271
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
|
|
272
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#89DDFF;font-style:italic;"> default</span><span style="color:#C792EA;"> async</span><span style="color:#C792EA;"> function</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
273
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> // For http://localhost:3000/login</span></span>
|
|
274
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">next</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">pathname</span><span style="color:#89DDFF;"> ===</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">login</span><span style="color:#89DDFF;">'</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
|
|
275
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span></span>
|
|
276
|
+
<span class="line"><span style="color:#F07178;"> name</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">John Doe</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span></span>
|
|
277
|
+
<span class="line"><span style="color:#F07178;"> role</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">owner</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span></span>
|
|
278
|
+
<span class="line"><span style="color:#89DDFF;"> };</span></span>
|
|
279
|
+
<span class="line"><span style="color:#89DDFF;"> }</span></span>
|
|
280
|
+
<span class="line"><span style="color:#F07178;"> </span></span>
|
|
281
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> // For every other URL</span></span>
|
|
282
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#82AAFF;"> next</span><span style="color:#F07178;">()</span><span style="color:#89DDFF;">;</span></span>
|
|
283
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><p>Our overall handler-to-URL mapping for the hypothetical application in context now becomes:</p><div class="language-shell"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#FFCB6B;">my-app</span></span>
|
|
284
|
+
<span class="line"><span style="color:#FFCB6B;"> ├──</span><span style="color:#C3E88D;"> client/index.js</span><span style="color:#C3E88D;"> -------------------------</span><span style="color:#C3E88D;"> http://localhost:3000/login</span></span>
|
|
285
|
+
<span class="line"><span style="color:#FFCB6B;"> ├──</span><span style="color:#C3E88D;"> worker/index.js</span><span style="color:#C3E88D;"> -------------------------</span><span style="color:#C3E88D;"> http://localhost:3000/about,</span><span style="color:#C3E88D;"> http://localhost:3000/logo.png</span></span>
|
|
286
|
+
<span class="line"><span style="color:#FFCB6B;"> ├──</span><span style="color:#C3E88D;"> server/index.js</span><span style="color:#C3E88D;"> -------------------------</span><span style="color:#C3E88D;"> http://localhost:3000,</span><span style="color:#C3E88D;"> http://localhost:3000/products,</span><span style="color:#C3E88D;"> http://localhost:3000/products/stickers,</span><span style="color:#C3E88D;"> etc</span></span>
|
|
287
|
+
<span class="line"><span style="color:#FFCB6B;"> └──</span><span style="color:#C3E88D;"> public/logo.png</span><span style="color:#C3E88D;"> -------------------------</span><span style="color:#C3E88D;"> http://localhost:3000/logo.png</span></span></code></pre></div><p>If there's anything we have now, it is the ability to break work down<a href="https://en.wikipedia.org/wiki/Divide-and-conquer_algorithm"><small><sup>[i]</sup></small></a>, optionally across step functions, optionally between layers!</p><h3 id="pages-layout-and-templating" tabindex="-1">Pages, Layout and Templating <a class="header-anchor" href="#pages-layout-and-templating" aria-label="Permalink to "Pages, Layout and Templating""></a></h3><p>HTML files in the <code>public</code> directory, just like every other <em>public</em> file, are served statically when accessed directly - e.g. <code>http://localhost:3000/index.html</code>. But <code>index.html</code> files, specifically, are treated as <em>pages</em> by Webflo. They are, therefore, also accessible with path URLs like <code>http://localhost:3000</code>.</p><div class="language-shell"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#FFCB6B;">my-app</span></span>
|
|
288
|
+
<span class="line"><span style="color:#FFCB6B;"> └──</span><span style="color:#C3E88D;"> public/index.html</span><span style="color:#C3E88D;"> -----------------------</span><span style="color:#C3E88D;"> http://localhost:3000/index.html,</span><span style="color:#C3E88D;"> http://localhost:3000</span></span></code></pre></div><p>But, where an <code>index.html</code> file pairs with a route...</p><div class="language-shell"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#FFCB6B;">my-app</span></span>
|
|
289
|
+
<span class="line"><span style="color:#FFCB6B;"> ├──</span><span style="color:#C3E88D;"> server/index.js</span></span>
|
|
290
|
+
<span class="line"><span style="color:#FFCB6B;"> └──</span><span style="color:#C3E88D;"> public/index.html</span></span></code></pre></div><p>...the route handler determines what happens.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
291
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">server</span></span>
|
|
292
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.js</span></span>
|
|
293
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
|
|
294
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#89DDFF;font-style:italic;"> default</span><span style="color:#C792EA;"> async</span><span style="color:#C792EA;"> function</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
295
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> // For http://localhost:3000/index.html, etc</span></span>
|
|
296
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">next</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">pathname</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
|
|
297
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#82AAFF;"> next</span><span style="color:#F07178;">()</span><span style="color:#89DDFF;">;</span></span>
|
|
298
|
+
<span class="line"><span style="color:#89DDFF;"> }</span></span>
|
|
299
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> // For http://localhost:3000 specifically</span></span>
|
|
300
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span><span style="color:#89DDFF;"> ...</span><span style="color:#89DDFF;"> };</span></span>
|
|
301
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><p>Now, we are able to access the data component of a route differently from its HTML component!</p><div class="language-shell"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#FFCB6B;">my-app</span></span>
|
|
302
|
+
<span class="line"><span style="color:#FFCB6B;"> └──</span><span style="color:#C3E88D;"> server/index.js</span><span style="color:#C3E88D;"> -------------------------</span><span style="color:#C3E88D;"> http://localhost:3000</span><span style="color:#C3E88D;"> --------------------</span><span style="color:#C3E88D;"> application/json</span></span>
|
|
303
|
+
<span class="line"><span style="color:#FFCB6B;"> └──</span><span style="color:#C3E88D;"> public/index.html</span><span style="color:#C3E88D;"> -----------------------</span><span style="color:#C3E88D;"> http://localhost:3000/index.html</span><span style="color:#C3E88D;"> ---------</span><span style="color:#C3E88D;"> text/html</span></span></code></pre></div><p>But, we can also access the route in a way that gets the data rendered into the automatically-paired <code>index.html</code> file for a dynamic page response. We'd simply set the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept" target="_blank" rel="noreferrer"><code>Accept</code></a> header of the request to a value of <code>text/html</code>, and Webflo will automatically perform <a href="#client-and-server-side-rendering">Server-Side Rendering</a> to give a page response.</p><blockquote><p><strong>Note</strong><br>The <code>Accept</code> header hint is already how browsers make requests on every page load. Here, it just works!</p></blockquote><p>This automatic pairing of an <code>index.html</code> file with a route works the same for nested routes! But top-level <code>index.html</code> files are implicitly inherited down the hierarchy. That means that subroutes do not need to have their own <code>index.html</code> document, unless necessary.</p><h4 id="layout-and-templating-overview" tabindex="-1">Layout and Templating Overview <a class="header-anchor" href="#layout-and-templating-overview" aria-label="Permalink to "Layout and Templating Overview""></a></h4><details><summary>Some disambiguation...</summary><blockquote><p>In a Single Page Application, all pages are based off a single <code>index.html</code> document. In a Multi Page Application, pages are individual <code>index.html</code> documents - ideally. But, Server-Side Rendering makes it possible to serve the same, but dynamically-rendered, <code>index.html</code> document across page loads - essentially an SPA architecture hiding on the server. But, here, lets take Multi Page Applications for an individual-page architecture.</p></blockquote></details><p>In a Multi Page Application (with an individual-page architecture), each page is its own <code>index.html</code> document, and it is often necessary to have certain page sections - e.g. site header, footer, and sidebar, etc. - stay consistent across pages. These sections can be defined once and <em>imported</em> on every page.</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#BABED8;">my-app</span></span>
|
|
304
|
+
<span class="line"><span style="color:#BABED8;"> └── public</span></span>
|
|
305
|
+
<span class="line"><span style="color:#BABED8;"> ├── about/index.html ------------------------- </span><span style="color:#89DDFF;"><!</span><span style="color:#F07178;">DOCTYPE</span><span style="color:#C792EA;"> html</span><span style="color:#89DDFF;">></span></span>
|
|
306
|
+
<span class="line"><span style="color:#BABED8;"> ├── products/index.html ---------------------- </span><span style="color:#89DDFF;"><!</span><span style="color:#F07178;">DOCTYPE</span><span style="color:#C792EA;"> html</span><span style="color:#89DDFF;">></span></span>
|
|
307
|
+
<span class="line"><span style="color:#BABED8;"> ├── index.html ------------------------------- </span><span style="color:#89DDFF;"><!</span><span style="color:#F07178;">DOCTYPE</span><span style="color:#C792EA;"> html</span><span style="color:#89DDFF;">></span></span>
|
|
308
|
+
<span class="line"><span style="color:#BABED8;"> ├── header.html ------------------------------ </span><span style="color:#89DDFF;"><</span><span style="color:#F07178;">header</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">header</span><span style="color:#89DDFF;">></span><span style="color:#676E95;font-style:italic;"> <!-- To appear at top of each index.html page --></span></span>
|
|
309
|
+
<span class="line"><span style="color:#BABED8;"> └── footer.html ------------------------------ </span><span style="color:#89DDFF;"><</span><span style="color:#F07178;">footer</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">footer</span><span style="color:#89DDFF;">></span><span style="color:#676E95;font-style:italic;"> <!-- To appear at bottom of each index.html page --></span></span></code></pre></div><p>In a Single Page Application, each page is the same <code>index.html</code> document, and it is often necessary to have the main page sections change on each route. These sections can be defined per-route and <em>imported</em> to the document on navigating to their respective route.</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#BABED8;">my-app</span></span>
|
|
310
|
+
<span class="line"><span style="color:#BABED8;"> └── public</span></span>
|
|
311
|
+
<span class="line"><span style="color:#BABED8;"> ├── about/main.html -------------------------- </span><span style="color:#89DDFF;"><</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></span><span style="color:#676E95;font-style:italic;"> <!-- To appear at main area of index.html --></span></span>
|
|
312
|
+
<span class="line"><span style="color:#BABED8;"> ├── products/main.html ----------------------- </span><span style="color:#89DDFF;"><</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></span><span style="color:#676E95;font-style:italic;"> <!-- To appear at main area of index.html --></span></span>
|
|
313
|
+
<span class="line"><span style="color:#BABED8;"> ├── main.html -------------------------------- </span><span style="color:#89DDFF;"><</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></span><span style="color:#676E95;font-style:italic;"> <!-- To appear at main area of index.html --></span></span>
|
|
314
|
+
<span class="line"><span style="color:#BABED8;"> └── index.html ------------------------------- </span><span style="color:#89DDFF;"><!</span><span style="color:#F07178;">DOCTYPE</span><span style="color:#C792EA;"> html</span><span style="color:#89DDFF;">></span></span></code></pre></div><p>This, in both cases, is templating - the ability to define HTML <em>partials</em> 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 <a href="#in-a-multi-spa-layout">Multi SPA</a>! It's all a <em>layout</em> thing!</p><p>Now, with pages in Webflo being <a href="#overview">DOM-based</a> (both client-side and <a href="#oohtml-ssr">server-side</a>), 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 <a href="https://github.com/webqit/oohtml#html-modules" target="_blank" rel="noreferrer">HTML Modules</a> and <a href="https://github.com/webqit/oohtml#html-imports" target="_blank" rel="noreferrer">HTML Imports</a> features in <a href="#oohtml">OOHTML</a> - unless disabled in config. These features provide a powerful declarative templating system on top of the standard <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template" target="_blank" rel="noreferrer">HTML <code><template></code></a> element - all in a <em>module</em>, <em>export</em> and <em>import</em> paradigm.</p><p>Here, you are able to define reusable contents in a <code><template></code> element...</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
315
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">template</span><span style="color:#C792EA;"> def</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">routes</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></span></span>
|
|
316
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">header</span><span style="color:#C792EA;"> def</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">header.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></span><span style="color:#BABED8;">Header Area</span><span style="color:#89DDFF;"></</span><span style="color:#F07178;">header</span><span style="color:#89DDFF;">></span></span>
|
|
317
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">main</span><span style="color:#C792EA;"> def</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">main.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></span><span style="color:#BABED8;">Main Area</span><span style="color:#89DDFF;"></</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></span></span>
|
|
318
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">></span></span>
|
|
319
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span></code></pre></div><p>...and have them imported anywhere on the root document using an <code><import></code> element:</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span>
|
|
320
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">import</span><span style="color:#C792EA;"> ref</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">app#header.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">import</span><span style="color:#89DDFF;">></span></span>
|
|
321
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">import</span><span style="color:#C792EA;"> ref</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">app#main.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">import</span><span style="color:#89DDFF;">></span></span>
|
|
322
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span></code></pre></div><p>The <em>module</em> element - <code><template></code> - is also able to load its contents from a remote <code>.html</code> file that serves as a bundle:</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;"><!--</span></span>
|
|
323
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">public</span></span>
|
|
324
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── bundle.html</span></span>
|
|
325
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">--></span></span>
|
|
326
|
+
<span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">header</span><span style="color:#C792EA;"> def</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">header.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></span><span style="color:#BABED8;">Header Area</span><span style="color:#89DDFF;"></</span><span style="color:#F07178;">header</span><span style="color:#89DDFF;">></span></span>
|
|
327
|
+
<span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">main</span><span style="color:#C792EA;"> def</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">main.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></span><span style="color:#BABED8;">Main Area</span><span style="color:#89DDFF;"></</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></span></span></code></pre></div><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;"><!--</span></span>
|
|
328
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">public</span></span>
|
|
329
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.html</span></span>
|
|
330
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">--></span></span>
|
|
331
|
+
<span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
332
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">template</span><span style="color:#C792EA;"> def</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">routes</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/bundle.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">></span></span>
|
|
333
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span></code></pre></div><p>What <a href="#bundling">we'll see shortly</a> is how multiple standalone <code>.html</code> files - e.g. those <code>header.html</code>, <code>footer.html</code>, <code>main.html</code> files above - come together into one <code>bundle.html</code> file for an application.</p><h4 id="in-a-multi-page-layout" tabindex="-1">In a Multi Page Layout <a class="header-anchor" href="#in-a-multi-page-layout" aria-label="Permalink to "In a Multi Page Layout""></a></h4><p>In a Multi Page layout (as seen <a href="#layout-and-templating-overview">earlier</a>), generic contents - e.g. header and footer sections, etc. - are typically bundled into one <code>bundle.html</code> file that can be embedded on each page of the application.</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;"><!--</span></span>
|
|
334
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">public</span></span>
|
|
335
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.html</span></span>
|
|
336
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">--></span></span>
|
|
337
|
+
<span class="line"><span style="color:#89DDFF;"><!</span><span style="color:#F07178;">DOCTYPE</span><span style="color:#C792EA;"> html</span><span style="color:#89DDFF;">></span></span>
|
|
338
|
+
<span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span>
|
|
339
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
340
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#C792EA;"> type</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">module</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/bundle.js</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
341
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">template</span><span style="color:#C792EA;"> def</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">app</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/bundle.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">></span></span>
|
|
342
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
343
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span>
|
|
344
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">import</span><span style="color:#C792EA;"> ref</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">app#header.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">import</span><span style="color:#89DDFF;">></span></span>
|
|
345
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></span><span style="color:#BABED8;">Welcome to our Home Page</span><span style="color:#89DDFF;"></</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></span></span>
|
|
346
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">import</span><span style="color:#C792EA;"> ref</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">app#footer.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">import</span><span style="color:#89DDFF;">></span></span>
|
|
347
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span>
|
|
348
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span></code></pre></div><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;"><!--</span></span>
|
|
349
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">public/about</span></span>
|
|
350
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.html</span></span>
|
|
351
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">--></span></span>
|
|
352
|
+
<span class="line"><span style="color:#89DDFF;"><!</span><span style="color:#F07178;">DOCTYPE</span><span style="color:#C792EA;"> html</span><span style="color:#89DDFF;">></span></span>
|
|
353
|
+
<span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span>
|
|
354
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
355
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#C792EA;"> type</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">module</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/bundle.js</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
356
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">template</span><span style="color:#C792EA;"> def</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">routes</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/bundle.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">></span></span>
|
|
357
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
358
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span>
|
|
359
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">import</span><span style="color:#C792EA;"> ref</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">app#header.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">import</span><span style="color:#89DDFF;">></span></span>
|
|
360
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></span><span style="color:#BABED8;">Welcome to our About Page</span><span style="color:#89DDFF;"></</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></span></span>
|
|
361
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">import</span><span style="color:#C792EA;"> ref</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">app#footer.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">import</span><span style="color:#89DDFF;">></span></span>
|
|
362
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span>
|
|
363
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span></code></pre></div><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;"><!--</span></span>
|
|
364
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">public/products</span></span>
|
|
365
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.html</span></span>
|
|
366
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">--></span></span>
|
|
367
|
+
<span class="line"><span style="color:#89DDFF;"><!</span><span style="color:#F07178;">DOCTYPE</span><span style="color:#C792EA;"> html</span><span style="color:#89DDFF;">></span></span>
|
|
368
|
+
<span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span>
|
|
369
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
370
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#C792EA;"> type</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">module</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/bundle.js</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
371
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">template</span><span style="color:#C792EA;"> def</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">routes</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/bundle.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">></span></span>
|
|
372
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
373
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span>
|
|
374
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">import</span><span style="color:#C792EA;"> ref</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">app#header.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">import</span><span style="color:#89DDFF;">></span></span>
|
|
375
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></span><span style="color:#BABED8;">Welcome to our Products Page</span><span style="color:#89DDFF;"></</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></span></span>
|
|
376
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">import</span><span style="color:#C792EA;"> ref</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">app#footer.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">import</span><span style="color:#89DDFF;">></span></span>
|
|
377
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span>
|
|
378
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span></code></pre></div><details><summary>How it works...</summary><blockquote><p>In this architecture, navigation is traditional - a new page loads each time. The <code>bundle.js</code> script comes with the appropriate OOHTML support level required for the imports to function.</p></blockquote></details><h4 id="in-a-single-page-layout" tabindex="-1">In a Single Page Layout <a class="header-anchor" href="#in-a-single-page-layout" aria-label="Permalink to "In a Single Page Layout""></a></h4><p>In a Single Page layout (as seen <a href="#layout-and-templating-overview">earlier</a>), page-specific contents - e.g. main sections - are typically bundled together into one <code>bundle.html</code> file that can be embedded on the document root. Notice how nested routes end up as nested <code><template></code> elements that form the equivalent of the application's URL structure.</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;"><!--</span></span>
|
|
379
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">public</span></span>
|
|
380
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── bundle.html</span></span>
|
|
381
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">--></span></span>
|
|
382
|
+
<span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">template</span><span style="color:#C792EA;"> def</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">about</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></span></span>
|
|
383
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">main</span><span style="color:#C792EA;"> def</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">main.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></span><span style="color:#BABED8;">Welcome to our About Page</span><span style="color:#89DDFF;"></</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></span></span>
|
|
384
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">></span></span>
|
|
385
|
+
<span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">template</span><span style="color:#C792EA;"> def</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">products</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></span></span>
|
|
386
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">main</span><span style="color:#C792EA;"> def</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">main.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></span><span style="color:#BABED8;">Welcome to our Products Page</span><span style="color:#89DDFF;"></</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></span></span>
|
|
387
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">></span></span>
|
|
388
|
+
<span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">main</span><span style="color:#C792EA;"> def</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">main.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></span><span style="color:#BABED8;">Welcome to our Home Page</span><span style="color:#89DDFF;"></</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></span></span></code></pre></div><p>Now, the <code><main></code> elements are each imported on navigating to their respective routes. This time, Webflo takes care of setting the URL path as a global <code>template</code> attribute on the <code><body></code> element such that <code><import></code> elements that inherit this global attribute are resolved from its current value.</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;"><!--</span></span>
|
|
389
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">public</span></span>
|
|
390
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.html</span></span>
|
|
391
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">--></span></span>
|
|
392
|
+
<span class="line"><span style="color:#89DDFF;"><!</span><span style="color:#F07178;">DOCTYPE</span><span style="color:#C792EA;"> html</span><span style="color:#89DDFF;">></span></span>
|
|
393
|
+
<span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span>
|
|
394
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
395
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#C792EA;"> type</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">module</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/bundle.js</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
396
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">template</span><span style="color:#C792EA;"> def</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">routes</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/bundle.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">></span></span>
|
|
397
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
398
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">body</span><span style="color:#C792EA;"> importscontext</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">app/</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></span><span style="color:#676E95;font-style:italic;"> <!-- This "template" attribute automatically changes to routes/about or routes/products as we navigate to http://localhost:3000/about and http://localhost:3000/products respectively --></span></span>
|
|
399
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">header</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">header</span><span style="color:#89DDFF;">></span></span>
|
|
400
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">import</span><span style="color:#C792EA;"> ref</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">#main.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">import</span><span style="color:#89DDFF;">></span><span style="color:#676E95;font-style:italic;"> <!-- This import element omits its "template" attribute so as to inherit the global one --></span></span>
|
|
401
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">footer</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">footer</span><span style="color:#89DDFF;">></span></span>
|
|
402
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span>
|
|
403
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span></code></pre></div><details><summary>How it works...</summary><blockquote><p>In this architecture, navigation is instant and sleek - Webflo prevents a full page reload, obtains and sets data at <code>document.bindings.data</code> for the new URL, then sets the <code>template</code> attribute on the <code><body></code> element to the new URL path. The <code>bundle.js</code> script comes with the appropriate OOHTML support level required for the imports to function.</p></blockquote></details><h4 id="in-a-multi-spa-layout" tabindex="-1">In a Multi SPA Layout <a class="header-anchor" href="#in-a-multi-spa-layout" aria-label="Permalink to "In a Multi SPA Layout""></a></h4><p>It's all a <em>layout</em> thing, so a hybrid of the two architectures above is possible in one application, to take advantage of the unique benefits of each! Here, you are able to have routes that are standalone <code>index.html</code> documents (MPA), which in turn, are able to act as a single document root for their subroutes (SPA).</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#BABED8;">my-app</span></span>
|
|
404
|
+
<span class="line"><span style="color:#BABED8;"> └── public</span></span>
|
|
405
|
+
<span class="line"><span style="color:#BABED8;"> ├── about/index.html ------------------------- </span><span style="color:#89DDFF;"><!</span><span style="color:#F07178;">DOCTYPE</span><span style="color:#C792EA;"> html</span><span style="color:#89DDFF;">></span><span style="color:#676E95;font-style:italic;"> <!-- Document root 1 --></span></span>
|
|
406
|
+
<span class="line"><span style="color:#BABED8;"> ├── products</span></span>
|
|
407
|
+
<span class="line"><span style="color:#BABED8;"> │ ├── free/main.html --------------------------- </span><span style="color:#89DDFF;"><</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></span><span style="color:#676E95;font-style:italic;"> <!-- To appear at main area of document root 2 --></span></span>
|
|
408
|
+
<span class="line"><span style="color:#BABED8;"> │ ├── paid/main.html --------------------------- </span><span style="color:#89DDFF;"><</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></span><span style="color:#676E95;font-style:italic;"> <!-- To appear at main area of document root 2 --></span></span>
|
|
409
|
+
<span class="line"><span style="color:#BABED8;"> │ ├── main.html -------------------------------- </span><span style="color:#89DDFF;"><</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">main</span><span style="color:#89DDFF;">></span><span style="color:#676E95;font-style:italic;"> <!-- To appear at main area of document root 2 --></span></span>
|
|
410
|
+
<span class="line"><span style="color:#BABED8;"> │ └── index.html ------------------------------- </span><span style="color:#89DDFF;"><!</span><span style="color:#F07178;">DOCTYPE</span><span style="color:#C792EA;"> html</span><span style="color:#89DDFF;">></span><span style="color:#676E95;font-style:italic;"> <!-- Document root 2, (doubles as an SPA) --></span></span>
|
|
411
|
+
<span class="line"><span style="color:#BABED8;"> ├── index.html ------------------------------- </span><span style="color:#89DDFF;"><!</span><span style="color:#F07178;">DOCTYPE</span><span style="color:#C792EA;"> html</span><span style="color:#89DDFF;">></span><span style="color:#676E95;font-style:italic;"> <!-- Document root 0 --></span></span>
|
|
412
|
+
<span class="line"><span style="color:#BABED8;"> ├── header.html ------------------------------ </span><span style="color:#89DDFF;"><</span><span style="color:#F07178;">header</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">header</span><span style="color:#89DDFF;">></span><span style="color:#676E95;font-style:italic;"> <!-- To appear at top of each document root --></span></span>
|
|
413
|
+
<span class="line"><span style="color:#BABED8;"> └── footer.html ------------------------------ </span><span style="color:#89DDFF;"><</span><span style="color:#F07178;">footer</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">footer</span><span style="color:#89DDFF;">></span><span style="color:#676E95;font-style:italic;"> <!-- To appear at bottom of each document root --></span></span></code></pre></div><p>The above gives us three document roots: <code>/index.html</code>, <code>/about/index.html</code>, <code>/products/index.html</code>. The <code>/products</code> route doubles as a Single Page Application such that visiting the <code>/products</code> route loads the document root <code>/products/index.html</code> and lets Webflo SPA routing determine which of <code>/products/main.html</code>, <code>/products/free/main.html</code>, <code>/products/paid/main.html</code> is imported on a given URL.</p><p>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.</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;"><!--</span></span>
|
|
414
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">public</span></span>
|
|
415
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── products/index.html</span></span>
|
|
416
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">--></span></span>
|
|
417
|
+
<span class="line"><span style="color:#89DDFF;"><!</span><span style="color:#F07178;">DOCTYPE</span><span style="color:#C792EA;"> html</span><span style="color:#89DDFF;">></span></span>
|
|
418
|
+
<span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span>
|
|
419
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
420
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#C792EA;"> type</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">module</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/webflo.bundle.js</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
421
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#C792EA;"> type</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">module</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/products/bundle.js</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
422
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">template</span><span style="color:#C792EA;"> def</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">pages</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/bundle.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">></span></span>
|
|
423
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
424
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span><span style="color:#BABED8;">...</span><span style="color:#89DDFF;"></</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span>
|
|
425
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span></code></pre></div><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;"><!--</span></span>
|
|
426
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">public</span></span>
|
|
427
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── about/index.html</span></span>
|
|
428
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">--></span></span>
|
|
429
|
+
<span class="line"><span style="color:#89DDFF;"><!</span><span style="color:#F07178;">DOCTYPE</span><span style="color:#C792EA;"> html</span><span style="color:#89DDFF;">></span></span>
|
|
430
|
+
<span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span>
|
|
431
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
432
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#C792EA;"> type</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">module</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/webflo.bundle.js</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
433
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#C792EA;"> type</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">module</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/about/bundle.js</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
434
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">template</span><span style="color:#C792EA;"> def</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">pages</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/bundle.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">></span></span>
|
|
435
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
436
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span><span style="color:#BABED8;">...</span><span style="color:#89DDFF;"></</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span>
|
|
437
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span></code></pre></div><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;"><!--</span></span>
|
|
438
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">public</span></span>
|
|
439
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.html</span></span>
|
|
440
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">--></span></span>
|
|
441
|
+
<span class="line"><span style="color:#89DDFF;"><!</span><span style="color:#F07178;">DOCTYPE</span><span style="color:#C792EA;"> html</span><span style="color:#89DDFF;">></span></span>
|
|
442
|
+
<span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span>
|
|
443
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
444
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#C792EA;"> type</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">module</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/webflo.bundle.js</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
445
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#C792EA;"> type</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">module</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/bundle.js</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
446
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">template</span><span style="color:#C792EA;"> def</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">pages</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/bundle.html</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">></span></span>
|
|
447
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
448
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span><span style="color:#BABED8;">...</span><span style="color:#89DDFF;"></</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span>
|
|
449
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span></code></pre></div><details><summary>How it works...</summary><blockquote><p>The Webflo <code>generate</code> command automatically figures out a given architecture and generates the appropriate scripts for the application! It also factors into the generated scripts the location of each document root so that <a href="#spa-navigation">all navigations to these roots are handled as a regular page load</a>.</p></blockquote></details><h4 id="bundling" tabindex="-1">Bundling <a class="header-anchor" href="#bundling" aria-label="Permalink to "Bundling""></a></h4><p>Template <code>.html</code> files are bundled from the filesystem into a single file using the <a href="#oohtml-cli">OOHTML CLI</a> utility. On installing this utility, you may want to add the following to your npm scripts in <code>package.json</code>.</p><div class="language-json"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">scripts</span><span style="color:#89DDFF;">"</span><span style="color:#BABED8;">: </span><span style="color:#89DDFF;">{</span></span>
|
|
450
|
+
<span class="line"><span style="color:#89DDFF;"> "</span><span style="color:#C792EA;">generate:templates</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> "</span><span style="color:#C3E88D;">oohtml bundle --recursive --auto-embed=routes</span><span style="color:#89DDFF;">"</span></span>
|
|
451
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><p>The <code>--recursive</code> flag gets the bundler to recursively bundle <em>subroots</em> in a <a href="#in-a-multi-spa-layout">Multi SPA</a> layout - where certain subdirectories have their own <code>index.html</code> document. (These subroots would be ignored otherwise.)</p><p>The <code>--auto-embed</code> flag gets the bundler to automatically embed the generated <code>bundle.html</code> file on the matched <code>index.html</code> document. A value of <code>routes</code> for the flag ends up as the name of the <em>embed</em> template: <code><template def="routes" src="/bundle.html"></template></code>.</p><blockquote><p><strong>Note</strong><br>If your HTML files are actually based off the <code>public</code> directory, you'll need to tell the above command to run in the <code>public</code> directory, either by <a href="https://github.com/webqit/oohtml-cli#other-options" target="_blank" rel="noreferrer">configuring the bundler</a>, or by rewriting the command with a prefix: <code>cd public && oohtml bundle --recursive --auto-embed=routes</code>.</p></blockquote><h3 id="client-and-server-side-rendering" tabindex="-1">Client and Server-Side Rendering <a class="header-anchor" href="#client-and-server-side-rendering" aria-label="Permalink to "Client and Server-Side Rendering""></a></h3><p>With pages in Webflo being <a href="#overview">DOM-based</a> (both client-side and <a href="#oohtml-ssr">server-side</a>), 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!</p><p>Here, Webflo simply makes sure that the data obtained from each route is available as part of the <code>document</code> object as <a href="#the-documentstatedata-object"><code>document.bindings.data</code></a>, making it accessible to our rendering logic.</p><p>We are able embed a script on our page and render this data on the relevant parts of the document.</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;"><!--</span></span>
|
|
452
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">public</span></span>
|
|
453
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.html</span></span>
|
|
454
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">--></span></span>
|
|
455
|
+
<span class="line"><span style="color:#89DDFF;"><!</span><span style="color:#F07178;">DOCTYPE</span><span style="color:#C792EA;"> html</span><span style="color:#89DDFF;">></span></span>
|
|
456
|
+
<span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span>
|
|
457
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
458
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">title</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">title</span><span style="color:#89DDFF;">></span></span>
|
|
459
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
460
|
+
<span class="line"><span style="color:#82AAFF;"> setTimeout</span><span style="color:#BABED8;">(</span><span style="color:#89DDFF;">()</span><span style="color:#C792EA;"> =></span><span style="color:#89DDFF;"> {</span></span>
|
|
461
|
+
<span class="line"><span style="color:#BABED8;"> console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">( </span><span style="color:#BABED8;">document</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">bindings</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">data</span><span style="color:#F07178;"> )</span><span style="color:#89DDFF;">;</span><span style="color:#676E95;font-style:italic;"> // { title: 'Home | FluffyPets' }</span></span>
|
|
462
|
+
<span class="line"><span style="color:#C792EA;"> let</span><span style="color:#89DDFF;"> {</span><span style="color:#BABED8;"> title</span><span style="color:#89DDFF;"> }</span><span style="color:#89DDFF;"> =</span><span style="color:#BABED8;"> document</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">bindings</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">data</span><span style="color:#89DDFF;">;</span></span>
|
|
463
|
+
<span class="line"><span style="color:#BABED8;"> document</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">title</span><span style="color:#89DDFF;"> =</span><span style="color:#BABED8;"> title</span><span style="color:#89DDFF;">;</span></span>
|
|
464
|
+
<span class="line"><span style="color:#89DDFF;"> },</span><span style="color:#F78C6C;"> 0</span><span style="color:#BABED8;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
465
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
466
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
467
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span>
|
|
468
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span></code></pre></div><p>Where your rendering logic is an external script, your <code><script></code> element would need to have an <code>ssr</code> Boolean attribute to get the rendering engine to fetch and run your script on the server.</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;"><!--</span></span>
|
|
469
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">public</span></span>
|
|
470
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.html</span></span>
|
|
471
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">--></span></span>
|
|
472
|
+
<span class="line"><span style="color:#89DDFF;"><!</span><span style="color:#F07178;">DOCTYPE</span><span style="color:#C792EA;"> html</span><span style="color:#89DDFF;">></span></span>
|
|
473
|
+
<span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span>
|
|
474
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
475
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">title</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">title</span><span style="color:#89DDFF;">></span></span>
|
|
476
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">app.js</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> ssr</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
477
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
478
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span>
|
|
479
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span></code></pre></div><p>From here, even the most-rudimentary form of rendering (using vanilla HTML and native DOM methods) becomes possible, and this is a good thing: you get away with less tooling until you absolutely need to add up on tooling!</p><p>However, since the <code>document</code> objects in Webflo natively support <a href="#oohtml">OOHTML</a> - unless disabled in config, we are able to write reactive UI logic! Here, OOHTML makes it possible to embed reactive <code><script></code> elements (called <a href="https://github.com/webqit/oohtml#subscript" target="_blank" rel="noreferrer">Subscript</a>) right within HTML elements - where each expression automatically self-updates whenever references to data, or its properties, get an update!</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;"> <!--</span></span>
|
|
480
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> public</span></span>
|
|
481
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.html</span></span>
|
|
482
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> --></span></span>
|
|
483
|
+
<span class="line"><span style="color:#89DDFF;"> <!</span><span style="color:#F07178;">DOCTYPE</span><span style="color:#C792EA;"> html</span><span style="color:#89DDFF;">></span></span>
|
|
484
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span>
|
|
485
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
486
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">title</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">title</span><span style="color:#89DDFF;">></span></span>
|
|
487
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
488
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span>
|
|
489
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">h1</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">h1</span><span style="color:#89DDFF;">></span></span>
|
|
490
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#C792EA;"> quantum</span><span style="color:#89DDFF;">></span></span>
|
|
491
|
+
<span class="line"><span style="color:#C792EA;"> let</span><span style="color:#89DDFF;"> {</span><span style="color:#BABED8;"> title </span><span style="color:#89DDFF;">}</span><span style="color:#89DDFF;"> =</span><span style="color:#BABED8;"> document</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">bindings</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">data</span><span style="color:#89DDFF;">;</span></span>
|
|
492
|
+
<span class="line"><span style="color:#BABED8;"> document</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">title </span><span style="color:#89DDFF;">=</span><span style="color:#BABED8;"> title</span><span style="color:#89DDFF;">;</span></span>
|
|
493
|
+
<span class="line"><span style="color:#C792EA;"> let</span><span style="color:#BABED8;"> h1Element </span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;"> this.</span><span style="color:#82AAFF;">querySelector</span><span style="color:#BABED8;">(</span><span style="color:#89DDFF;">'</span><span style="color:#C3E88D;">h1</span><span style="color:#89DDFF;">'</span><span style="color:#BABED8;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
494
|
+
<span class="line"><span style="color:#BABED8;"> h1Element</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">innerHTML </span><span style="color:#89DDFF;">=</span><span style="color:#BABED8;"> title</span><span style="color:#89DDFF;">;</span></span>
|
|
495
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
496
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span>
|
|
497
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span></code></pre></div><details><summary>Re-introducing UI logic in the actual language for logic - JavaScript...</summary><blockquote><p>So, this is simple to think about: HTML already just let's us embed <code><script></code> elements for UI logic, and so be it! What OOHTML does further is simply to extend the plain old <code><script></code> element with the <code>subscript</code> type which gets any JavaScript code to be <em>reactive</em>! Compared with other syntax alternatives, this uniquely enables us to do all things logic in the actual language for logic - JavaScript.</p></blockquote></details><p>Note that because these scripts are naturally reactive, we do not require any <code>setTimeout()</code> construct like we required earlier in the case of the classic <code><script></code> element. These expressions self-update as the values they depend on become available, removed, or updated - i.e. as <code>document.bindings</code> gets updated.</p><p>From here, we are also able to write more succinct code! Using the <a href="https://github.com/webqit/oohtml#namespaced-html" target="_blank" rel="noreferrer">Namespaced HTML</a> feature in OOHTML, we could do without those <code>querySelector()</code> calls up there. Also, we could go on to use any DOM manipulation library of our choice; e.g jQuery, or even better, the jQuery-like <a href="https://github.com/webqit/play-ui" target="_blank" rel="noreferrer">Play UI</a> library.</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;"> <!--</span></span>
|
|
498
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> public</span></span>
|
|
499
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.html</span></span>
|
|
500
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> --></span></span>
|
|
501
|
+
<span class="line"><span style="color:#89DDFF;"> <!</span><span style="color:#F07178;">DOCTYPE</span><span style="color:#C792EA;"> html</span><span style="color:#89DDFF;">></span></span>
|
|
502
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span>
|
|
503
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
504
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">title</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">title</span><span style="color:#89DDFF;">></span></span>
|
|
505
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#C792EA;"> src</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">/jquery.js</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
506
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">head</span><span style="color:#89DDFF;">></span></span>
|
|
507
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">body</span><span style="color:#C792EA;"> namespace</span><span style="color:#89DDFF;">></span></span>
|
|
508
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">h1</span><span style="color:#C792EA;"> id</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">headline1</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></</span><span style="color:#F07178;">h1</span><span style="color:#89DDFF;">></span></span>
|
|
509
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#C792EA;"> quantum</span><span style="color:#89DDFF;">></span></span>
|
|
510
|
+
<span class="line"><span style="color:#C792EA;"> let</span><span style="color:#89DDFF;"> {</span><span style="color:#BABED8;"> title </span><span style="color:#89DDFF;">}</span><span style="color:#89DDFF;"> =</span><span style="color:#BABED8;"> document</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">bindings</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">data</span><span style="color:#89DDFF;">;</span></span>
|
|
511
|
+
<span class="line"><span style="color:#BABED8;"> document</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">title </span><span style="color:#89DDFF;">=</span><span style="color:#BABED8;"> title</span><span style="color:#89DDFF;">;</span></span>
|
|
512
|
+
<span class="line"><span style="color:#C792EA;"> let</span><span style="color:#89DDFF;"> {</span><span style="color:#BABED8;"> headline1</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;"> headline2 </span><span style="color:#89DDFF;">}</span><span style="color:#89DDFF;"> =</span><span style="color:#89DDFF;"> this.</span><span style="color:#BABED8;">namespace</span><span style="color:#89DDFF;">;</span></span>
|
|
513
|
+
<span class="line"><span style="color:#82AAFF;"> $</span><span style="color:#BABED8;">(headline1)</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">html</span><span style="color:#BABED8;">(title)</span><span style="color:#89DDFF;">;</span></span>
|
|
514
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#BABED8;"> (headline2) </span><span style="color:#89DDFF;">{</span></span>
|
|
515
|
+
<span class="line"><span style="color:#82AAFF;"> $</span><span style="color:#F07178;">(</span><span style="color:#BABED8;">headline2</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">html</span><span style="color:#F07178;">(</span><span style="color:#BABED8;">title</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
516
|
+
<span class="line"><span style="color:#89DDFF;"> }</span></span>
|
|
517
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
518
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span>
|
|
519
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">html</span><span style="color:#89DDFF;">></span></span></code></pre></div><p>Above, we've also referenced some currently non-existent element <code>headline2</code> - ahead of when it becomes added in the DOM! This should give a glimpse of the powerful reactivity we get with having OOHTML around on our document!</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#82AAFF;">setTimeout</span><span style="color:#BABED8;">(</span><span style="color:#89DDFF;">()</span><span style="color:#C792EA;"> =></span><span style="color:#89DDFF;"> {</span></span>
|
|
520
|
+
<span class="line"><span style="color:#C792EA;"> let</span><span style="color:#BABED8;"> headline2</span><span style="color:#89DDFF;"> =</span><span style="color:#BABED8;"> document</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">createElement</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">'</span><span style="color:#C3E88D;">h2</span><span style="color:#89DDFF;">'</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
521
|
+
<span class="line"><span style="color:#BABED8;"> headline2</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">id</span><span style="color:#89DDFF;"> =</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">headline2</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">;</span></span>
|
|
522
|
+
<span class="line"><span style="color:#BABED8;"> document</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">body</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">append</span><span style="color:#F07178;">(</span><span style="color:#BABED8;">headline2</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
523
|
+
<span class="line"><span style="color:#89DDFF;">},</span><span style="color:#F78C6C;"> 1000</span><span style="color:#BABED8;">)</span><span style="color:#89DDFF;">;</span></span></code></pre></div><p>Taking things further, it is possible to write class-based components that abstract away all logic! You can find a friend in <a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements" target="_blank" rel="noreferrer">Custom Elements</a>! Plus, your Custom Elements can function reactively using <a href="https://github.com/webqit/oohtml#subscript" target="_blank" rel="noreferrer">SubscriptElement</a> as base class!</p><h4 id="the-document-bindings-url-object" tabindex="-1">The <code>document.bindings.url</code> Object <a class="header-anchor" href="#the-document-bindings-url-object" aria-label="Permalink to "The `document.bindings.url` Object""></a></h4><p>This is a <em>live</em> object that reperesents the properties of the application URL at any point in time. The object exposes the same URL properties as of a standard <a href="https://developer.mozilla.org/en-US/docs/Web/API/URL" target="_blank" rel="noreferrer"><code>URL</code></a> object, but, here, as <em>live</em> properties that can be observed as navigation happens, and modified to initiate navigation - all using the <a href="#the-observer-api">Observer API</a>.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#C792EA;">let</span><span style="color:#89DDFF;"> {</span><span style="color:#BABED8;"> url </span><span style="color:#89DDFF;">}</span><span style="color:#89DDFF;"> =</span><span style="color:#BABED8;"> document</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">bindings</span><span style="color:#89DDFF;">;</span></span>
|
|
524
|
+
<span class="line"><span style="color:#BABED8;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#BABED8;">(url) </span><span style="color:#676E95;font-style:italic;">// { hash, host, hostname, href, origin, password, pathname, port, protocol, search, searchParams, username }</span></span></code></pre></div><details><summary>More examples...</summary><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#BABED8;">Observer</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">observe</span><span style="color:#BABED8;">(url</span><span style="color:#89DDFF;">,</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">hash</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> e</span><span style="color:#C792EA;"> =></span><span style="color:#89DDFF;"> {</span></span>
|
|
525
|
+
<span class="line"><span style="color:#BABED8;"> console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#BABED8;">url</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">hash</span><span style="color:#89DDFF;"> ===</span><span style="color:#BABED8;"> e</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">value</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span><span style="color:#676E95;font-style:italic;"> // true</span></span>
|
|
526
|
+
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#BABED8;">)</span><span style="color:#89DDFF;">;</span></span></code></pre></div><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">// Navigates to "/login#form" as if a link was clicked</span></span>
|
|
527
|
+
<span class="line"><span style="color:#BABED8;">document</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">addEventListener</span><span style="color:#BABED8;">(</span><span style="color:#89DDFF;">'</span><span style="color:#C3E88D;">synthetic-navigation</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> e</span><span style="color:#C792EA;"> =></span><span style="color:#89DDFF;"> {</span></span>
|
|
528
|
+
<span class="line"><span style="color:#BABED8;"> Observer</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">set</span><span style="color:#F07178;">(</span><span style="color:#BABED8;">url</span><span style="color:#89DDFF;">,</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">href</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">/login#form</span><span style="color:#89DDFF;">'</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
529
|
+
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#BABED8;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
530
|
+
<span class="line"></span>
|
|
531
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">// Or...</span></span>
|
|
532
|
+
<span class="line"><span style="color:#BABED8;">document</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">addEventListener</span><span style="color:#BABED8;">(</span><span style="color:#89DDFF;">'</span><span style="color:#C3E88D;">synthetic-navigation</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> e</span><span style="color:#C792EA;"> =></span><span style="color:#89DDFF;"> {</span></span>
|
|
533
|
+
<span class="line"><span style="color:#BABED8;"> Observer</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">set</span><span style="color:#F07178;">(</span><span style="color:#BABED8;">url</span><span style="color:#89DDFF;">,</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> pathname</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">/login</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> hash</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">#form</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> }</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
534
|
+
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#BABED8;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
535
|
+
<span class="line"></span>
|
|
536
|
+
<span class="line"><span style="color:#BABED8;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#BABED8;">(url</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">hash)</span><span style="color:#89DDFF;">;</span><span style="color:#676E95;font-style:italic;"> // #form</span></span></code></pre></div><p>There is also the <em>convenience</em> <code>query</code> property that offers the URL parameters as a <em>live</em> object.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">// For URL: http://localhost:3000/login?as=student</span></span>
|
|
537
|
+
<span class="line"><span style="color:#BABED8;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#BABED8;">(url</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">query</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">as) </span><span style="color:#676E95;font-style:italic;">// student</span></span>
|
|
538
|
+
<span class="line"></span>
|
|
539
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">// Re-rewrite the URL and initiate navigation by simply modifying a query parameter</span></span>
|
|
540
|
+
<span class="line"><span style="color:#BABED8;">document</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">addEventListener</span><span style="color:#BABED8;">(</span><span style="color:#89DDFF;">'</span><span style="color:#C3E88D;">synthetic-navigation</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> e</span><span style="color:#C792EA;"> =></span><span style="color:#89DDFF;"> {</span></span>
|
|
541
|
+
<span class="line"><span style="color:#BABED8;"> Observer</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">set</span><span style="color:#F07178;">(</span><span style="color:#BABED8;">url</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">query</span><span style="color:#89DDFF;">,</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">as</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">business</span><span style="color:#89DDFF;">'</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
542
|
+
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#BABED8;">)</span><span style="color:#89DDFF;">;</span></span></code></pre></div><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">script</span><span style="color:#C792EA;"> quantum</span><span style="color:#89DDFF;">></span></span>
|
|
543
|
+
<span class="line"><span style="color:#C792EA;"> let</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> query</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> as</span><span style="color:#89DDFF;">:</span><span style="color:#BABED8;"> role </span><span style="color:#89DDFF;">}</span><span style="color:#89DDFF;"> }</span><span style="color:#89DDFF;"> =</span><span style="color:#BABED8;"> url</span><span style="color:#89DDFF;">;</span></span>
|
|
544
|
+
<span class="line"><span style="color:#BABED8;"> document</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">title </span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">Login as </span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> +</span><span style="color:#BABED8;"> role</span><span style="color:#89DDFF;">;</span></span>
|
|
545
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span></code></pre></div></details><h3 id="requests-and-responses" tabindex="-1">Requests and Responses <a class="header-anchor" href="#requests-and-responses" aria-label="Permalink to "Requests and Responses""></a></h3><p>On each request, the event object passed to route handlers exposes the incoming request as <code>event.request</code>. This is an instance of <code>event.Request</code> - an extension of the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Request" target="_blank" rel="noreferrer">WHATWG Request</a> class. The event object also exposes <code>Response</code> - an extension of the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Response" target="_blank" rel="noreferrer">WHATWG Response</a> class, for returning instance-based responses. You enjoy routing that is based on standard interfaces!</p><p>Routes in Webflo can be designed for different types of request/response scenarios. Here are some important ones:</p><h4 id="scenario-1-static-file-requests-and-responses" tabindex="-1">Scenario 1: Static File Requests and Responses <a class="header-anchor" href="#scenario-1-static-file-requests-and-responses" aria-label="Permalink to "Scenario 1: Static File Requests and Responses""></a></h4><p>Static file requests like <code>http://localhost:3000/logo.png</code> are automatically responded to by Webflo when <code>next()</code>ed forward by route handlers, or where there are no route handlers.</p><ul><li>On the server, Webflo serves files from the <code>public</code> directory. File contents along with the appropriate headers like <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type" target="_blank" rel="noreferrer"><code>Content-Type</code></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Length" target="_blank" rel="noreferrer"><code>Content-Length</code></a>, etc. are returned as an instance of <code>Response</code>. Where a request has an <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding" target="_blank" rel="noreferrer"><code>Accept-Encoding</code></a> header set (e.g. <code>gzip</code>, <code>br</code>) and there exists a matching <em>compressed version</em> of the said file on the file system (e.g. <code>./public/logo.png.gz</code>, <code>./public/logo.png.br</code>), the compressed version is served and the appropriate <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding" target="_blank" rel="noreferrer"><code>Content-Encoding</code></a> response header is set.</li></ul><h4 id="scenario-2-api-requests-and-responses" tabindex="-1">Scenario 2: API Requests and Responses <a class="header-anchor" href="#scenario-2-api-requests-and-responses" aria-label="Permalink to "Scenario 2: API Requests and Responses""></a></h4><p>JSON (API) requests - requests that expect to get a JSON response (i.e. <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type" target="_blank" rel="noreferrer"><code>Content-Type</code></a>: <code>application/json</code>) - are automatically satisfied by Webflo with a valid JSON response. Here, Webflo simply jsonfies pipeline return values - which are usually plain objects, or other jsonfyable types - <code>string</code>, <code>number</code>, <code>boolean</code>, <code>array</code>.</p><ul><li>These requests need not have an <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept" target="_blank" rel="noreferrer"><code>Accept</code></a> header; but if they should, it must be the value of <code>application/json</code>.</li><li>Routes intended to be accessed this way are expected to return a jsonfyable value (or an instance of <code>Response</code> containing same) from the pipeline.</li><li>Pipeline responses that are an instance of <code>Response</code> with a <code>Content-Type</code> header already set are sent as-is.</li></ul><h4 id="scenario-3-page-requests-and-responses" tabindex="-1">Scenario 3: Page Requests and Responses <a class="header-anchor" href="#scenario-3-page-requests-and-responses" aria-label="Permalink to "Scenario 3: Page Requests and Responses""></a></h4><p>HTML page requests - requests that expect to get an HTML response (i.e. <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type" target="_blank" rel="noreferrer"><code>Content-Type</code></a>: <code>text/html</code>) - are automatically satisfied by Webflo with a valid HTML response. Pipeline return values that are objects are automatically used for <a href="#client-and-server-side-rendering">Server-Side Rendering</a>.</p><ul><li>These requests need to have an <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept" target="_blank" rel="noreferrer"><code>Accept</code></a> header of <code>text/html</code>, or something that resolves to <code>text/html</code> - e.g. <code>text/*</code>, <code>*/html</code>, <code>*/*</code>.</li><li>Routes intended to be accessed this way are expected to return a plain object (or an instance of <code>Response</code> containing same) from the pipeline in order to be renderable.</li><li>Pipeline responses that are an instance of <code>Response</code> with a <code>Content-Type</code> header already set are sent as-is, and not rendered.</li></ul><h4 id="scenario-4-single-page-navigation-requests-and-responses" tabindex="-1">Scenario 4: Single Page Navigation Requests and Responses <a class="header-anchor" href="#scenario-4-single-page-navigation-requests-and-responses" aria-label="Permalink to "Scenario 4: Single Page Navigation Requests and Responses""></a></h4><p>In a Single Page Application layout, every navigation event (page-to-page navigation, history back and forward navigation, and form submissions) is expected to initiate a request/response flow without a full page reload, since the destination URLs are often based off the already loaded document. The Webflo client JS intercepts these navigation events and generates the equivalent request object with an <code>Accept</code> header of <code>application/json</code>, so that data can be obtained as a JSON object (<a href="#scenario-2-api-requests-and-responses">scenerio 2 above</a>) for <a href="#client-and-server-side-rendering">Client-Side Rendering</a>.</p><p>The generated request also <a href="#custom-redirect-responses">hints the server</a> on how to return cross-SPA redirects (redirects that will point to another origin, or to another SPA root (in a <a href="#in-a-multi-spa-layout">Multi SPA</a> layout)) so that it can be handled manually by the client. The following headers are set: <code>X-Redirect-Policy: manual-when-cross-spa</code>, <code>X-Redirect-Code: 200</code>.</p><ul><li>Same-SPA redirects are sent as-is, and the Webflo client JS receives and renders the final data and updates the address bar with the final URL.</li><li>Cross-SPA/cross-origin redirects are communicated back, as hinted, and the destination URL is opened as a fresh page load.</li></ul><h4 id="scenario-5-range-requests-and-responses" tabindex="-1">Scenario 5: Range Requests and Responses <a class="header-anchor" href="#scenario-5-range-requests-and-responses" aria-label="Permalink to "Scenario 5: Range Requests and Responses""></a></h4><p>In all cases, where a request specifies a <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range" target="_blank" rel="noreferrer"><code>Range</code></a> header, Webflo automatically slices the response body to satisfy the range, and the appropriate <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Range" target="_blank" rel="noreferrer"><code>Content-Range</code></a> response header is set.</p><ul><li>Pipeline responses that are an instance of <code>Response</code> with a <code>Content-Range</code> header already set are sent as-is.</li></ul><h4 id="other-requests-and-responses" tabindex="-1">Other Requests and Responses <a class="header-anchor" href="#other-requests-and-responses" aria-label="Permalink to "Other Requests and Responses""></a></h4><p>Routing Pipelines may return any other data type, e.g. an instance of the native <a href="https://developer.mozilla.org/en-US/docs/Web/API/FormData" target="_blank" rel="noreferrer">FormData</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob" target="_blank" rel="noreferrer">Blob</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/API/File" target="_blank" rel="noreferrer">File</a>, or <a href="https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream" target="_blank" rel="noreferrer">ReadableStream</a>, etc., or an instance of <code>Response</code> containing same - usually on routes that do not double as a page route. Webflo tries to send these along with the appropriate response headers.</p><blockquote><p><strong>Note</strong><br>The fact that all requests, even static file requests, are seen by route handlers, where defined, means that they get a chance to dynamically generate the responses that the client sees!</p></blockquote><h4 id="custom-redirect-responses" tabindex="-1">Custom Redirect Responses <a class="header-anchor" href="#custom-redirect-responses" aria-label="Permalink to "Custom Redirect Responses""></a></h4><p>It is possible to hint the server on how to serve redirect responses. The idea is to substitute the standard <code>302</code>, <code>301</code> response code for these redirects with a non-rediret status code so that it can be recieved as a normal response and handled manually. The following pair of headers make this possible: <code>X-Redirect-Code</code>, <code>X-Redirect-Policy</code>.</p><ul><li><p>The <code>X-Redirect-Code</code> can be any valid HTTP status code (often preferably, in the 2xx). This is the response code that you want Webflo to substitute the actual redirect code with.</p></li><li><p>The <code>X-Redirect-Policy</code> header can be any of:</p><ul><li><code>manual</code> - which means "treat all redirects as manual"</li><li><code>manual-if-cross-origin</code> - which means "treat cross-origin redirects as manual"</li><li><code>manual-if-cross-spa</code> - which means "treat cross-SPA redirects (including cross-origin redirects) as manual"</li></ul><p>In each case, the substituted, original redirect code is returned back in the response in a special <code>X-Redirect-Code</code> response header, alongside the standard <code>Location</code> header.</p></li></ul><h4 id="failure-responses" tabindex="-1">Failure Responses <a class="header-anchor" href="#failure-responses" aria-label="Permalink to "Failure Responses""></a></h4><p>Where pipelines return <code>undefined</code>, a <code>Not Found</code> status is implied.</p><ul><li>On the server side, a <code>404</code> HTTP response is returned.</li><li>On the client-side, the initiating document in the browser has its <code>document.bindings.data</code> emptied. The error is also exposed on the <a href="#the-documentstatenetwork-object"><code>document.bindings.network.error</code></a> property.</li></ul><p>Where pipelines throw an exception, an <em>error</em> status is implied.</p><ul><li>On the server side, the error is logged and a <code>500</code> HTTP response is returned.</li><li>On the client-side, the initiating document in the browser has its <code>document.bindings.data</code> emptied. The error is also exposed on the <a href="#the-documentstatenetwork-object"><code>document.bindings.network.error</code></a> property.</li></ul><h4 id="cookie-responses" tabindex="-1">Cookie Responses <a class="header-anchor" href="#cookie-responses" aria-label="Permalink to "Cookie Responses""></a></h4><p>Handlers can set <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie" target="_blank" rel="noreferrer">response cookies</a> via the standard <code>Response</code> constructor, or using the standard <code>Headers.set()</code> method.</p><h3 id="webflo-applications" tabindex="-1">Webflo Applications <a class="header-anchor" href="#webflo-applications" aria-label="Permalink to "Webflo Applications""></a></h3><p>Webflo comes ready for any type of application!</p><ul><li><a href="#client-side-applications">Client-Side Applications</a></li><li><a href="#progressive-web-apps">Progressive Web Apps</a></li><li><a href="#api-backends">API Backends</a></li><li><a href="#static-sites">Static Sites</a></li></ul><p><em>And you can go hybrid!</em></p><h4 id="client-side-applications" tabindex="-1">Client-Side Applications <a class="header-anchor" href="#client-side-applications" aria-label="Permalink to "Client-Side Applications""></a></h4><p>Web pages that embed the Webflo client JS deliver a great user experience. It's simple: the <code>npm run generate</code> command does both the building and embedding of the script (or scripts), for the document root (or document roots - in a <a href="#in-a-multi-page-layout">Multi Page</a> / <a href="#in-a-multi-spa-layout">Multi SPA</a> layout)!</p><p>On being loaded, the state of the application is initialized, or is restored through hydration - where <a href="#client-and-server-side-rendering">Server-Side Rendering</a> was involved to optimize for first paint, and an app-like experience kicks in! For <a href="#in-a-single-page-layout">Single-Page Applications</a>, <a href="#client-and-server-side-rendering">Client-Side Rendering</a> is performed on each navigation.</p><h5 id="spa-navigation" tabindex="-1">SPA Navigation <a class="header-anchor" href="#spa-navigation" aria-label="Permalink to "SPA Navigation""></a></h5><p>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.</p><details><summary>How it works...</summary><p>SPA Navigation follows the following rules:</p><ul><li>When it ascertains that the destination URL is based on the current running <code>index.html</code> document in the browser (an SPA architecture), a full page reload is prevented for <em>soft</em> navigation. But where the destination URL points out of the current document root (a <a href="#in-a-multi-spa-layout">Multi SPA</a> architecture), navigation is allowed as a normal page load, and a new page root is loaded.</li><li>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.</li><li>If navigation is initiated from a link element that has the <code>target</code> attribute, or the <code>download</code> attribute, navigation is allowed to work the default way - regardless of the first rule above.</li><li>If navigation is initiated from a form element that has the <code>target</code> attribute, navigation is allowed to work the default way - regardless of the first rule above.</li></ul></details><details><summary>Config (Default)</summary><div class="language-json"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">{</span><span style="color:#89DDFF;"> "</span><span style="color:#C792EA;">spa_navigation</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> true</span><span style="color:#89DDFF;"> }</span></span></code></pre></div><blockquote><p><strong>File: <code>.webqit/webflo/client.json</code></strong></p></blockquote><blockquote><p><strong>Command: <code>webflo config client spa_navigation=TRUE</code></strong></p></blockquote></details><h5 id="spa-state" tabindex="-1">SPA State <a class="header-anchor" href="#spa-state" aria-label="Permalink to "SPA State""></a></h5><p>On the client side of a Webflo application, <a href="#the-idea-of-state">the idea of state</a> also includes the following aspects of the client-side lifecycle that can be used to provide visual cues on the UI.</p><h6 id="the-document-bindings-network-object" tabindex="-1">The <code>document.bindings.network</code> Object <a class="header-anchor" href="#the-document-bindings-network-object" aria-label="Permalink to "The `document.bindings.network` Object""></a></h6><p>This is a <em>live</em> object that exposes the network activity and network state of the application.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#BABED8;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#BABED8;">(document</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">bindings</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">network) </span><span style="color:#676E95;font-style:italic;">// { requesting, remote, error, redirecting, connectivity, }</span></span></code></pre></div><details><summary>Property: <code>.network.requesting: null|Object</code></summary><p>This property tells when a request is ongoing, in which case it exposes the <code>params</code> object used to initiate the request.</p><p>On the UI, this could be used to hide a menu drawer that may have been open.</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">menu-drawer</span><span style="color:#89DDFF;">></span></span>
|
|
546
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#C792EA;"> quantum</span><span style="color:#89DDFF;">></span></span>
|
|
547
|
+
<span class="line"><span style="color:#C792EA;"> let</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> network</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> {</span><span style="color:#BABED8;"> requesting </span><span style="color:#89DDFF;">}</span><span style="color:#89DDFF;"> }</span><span style="color:#89DDFF;"> =</span><span style="color:#BABED8;"> document</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">bindings</span><span style="color:#89DDFF;">;</span></span>
|
|
548
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#BABED8;"> (requesting) </span><span style="color:#89DDFF;">{</span></span>
|
|
549
|
+
<span class="line"><span style="color:#82AAFF;"> $</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">this</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">attr</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">'</span><span style="color:#C3E88D;">open</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#FF9CAC;"> false</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
550
|
+
<span class="line"><span style="color:#89DDFF;"> }</span></span>
|
|
551
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
552
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">menu-drawer</span><span style="color:#89DDFF;">></span></span></code></pre></div></details><details><summary>Property: <code>.network.remote: null|String</code></summary><p>This property tells when a remote request is ongoing - usually the same navigation requests as at <code>network.requesting</code>, but when not handled by any client-side route handlers, or when <code>next()</code>ed to this point by route handlers. The <code>remote</code> property also goes live when a route handler calls the special <code>fetch()</code> function that they recieve on their fourth parameter.</p><p>On the UI, this could be used to show/hide a spinner, or progress bar, to provide a visual cue.</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">progress-bar</span><span style="color:#89DDFF;">></span></span>
|
|
553
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#C792EA;"> quantum</span><span style="color:#89DDFF;">></span></span>
|
|
554
|
+
<span class="line"><span style="color:#C792EA;"> let</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> network</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> {</span><span style="color:#BABED8;"> remote </span><span style="color:#89DDFF;">}</span><span style="color:#89DDFF;"> }</span><span style="color:#89DDFF;"> =</span><span style="color:#BABED8;"> document</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">bindings</span><span style="color:#89DDFF;">;</span></span>
|
|
555
|
+
<span class="line"><span style="color:#82AAFF;"> $</span><span style="color:#BABED8;">(</span><span style="color:#89DDFF;">this</span><span style="color:#BABED8;">)</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">attr</span><span style="color:#BABED8;">(</span><span style="color:#89DDFF;">'</span><span style="color:#C3E88D;">hidden</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#89DDFF;"> !</span><span style="color:#BABED8;">remote)</span><span style="color:#89DDFF;">;</span></span>
|
|
556
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
557
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">progress-bar</span><span style="color:#89DDFF;">></span></span></code></pre></div></details><details><summary>Property: <code>.network.error: null|Error</code></summary><p>This property tells when a request is <em>errored</em> in which case it contains an <code>Error</code> instance of the error. For requests that can be retried, the <code>Error</code> instance also has a custom <code>retry()</code> method.</p><p>On the UI, this could be used to show/hide cute error elements.</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">nice-error</span><span style="color:#89DDFF;">></span></span>
|
|
558
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#C792EA;"> quantum</span><span style="color:#89DDFF;">></span></span>
|
|
559
|
+
<span class="line"><span style="color:#C792EA;"> let</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> network</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> {</span><span style="color:#BABED8;"> error </span><span style="color:#89DDFF;">}</span><span style="color:#89DDFF;"> }</span><span style="color:#89DDFF;"> =</span><span style="color:#BABED8;"> document</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">bindings</span><span style="color:#89DDFF;">;</span></span>
|
|
560
|
+
<span class="line"><span style="color:#82AAFF;"> $</span><span style="color:#BABED8;">(</span><span style="color:#89DDFF;">this</span><span style="color:#BABED8;">)</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">attr</span><span style="color:#BABED8;">(</span><span style="color:#89DDFF;">'</span><span style="color:#C3E88D;">hidden</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#89DDFF;"> !</span><span style="color:#BABED8;">error)</span><span style="color:#89DDFF;">;</span></span>
|
|
561
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
562
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">nice-error</span><span style="color:#89DDFF;">></span></span></code></pre></div></details><details><summary>Property: <code>.network.redirecting: null|String</code></summary><p>This property tells when a client-side redirect is ongoing - see <a href="#scenario-4-single-page-navigation-requests-and-responses">Scenario 4: Single Page Navigation Requests and Responses</a> - in which case it exposes the destination URL.</p><p>On the UI, this could be used to mark the current page as stale and prevent further interactions.</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span>
|
|
563
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#C792EA;"> quantum</span><span style="color:#89DDFF;">></span></span>
|
|
564
|
+
<span class="line"><span style="color:#C792EA;"> let</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> network</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> {</span><span style="color:#BABED8;"> redirecting </span><span style="color:#89DDFF;">}</span><span style="color:#89DDFF;"> }</span><span style="color:#89DDFF;"> =</span><span style="color:#BABED8;"> document</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">bindings</span><span style="color:#89DDFF;">;</span></span>
|
|
565
|
+
<span class="line"><span style="color:#82AAFF;"> $</span><span style="color:#BABED8;">(</span><span style="color:#89DDFF;">this</span><span style="color:#BABED8;">)</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">css</span><span style="color:#BABED8;">(redirecting </span><span style="color:#89DDFF;">?</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> pointerEvents</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">none</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> filter</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">blur(2)</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> }</span><span style="color:#89DDFF;"> :</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> pointerEvents</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">auto</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> filter</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">blur(0)</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> }</span><span style="color:#BABED8;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
566
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
567
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span></code></pre></div></details><details><summary>Property: <code>.network.connectivity: String</code></summary><p>This property tells of <a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine" target="_blank" rel="noreferrer">the browser's ability to connect to the network</a>: <code>online</code>, <code>offline</code>.</p><p>On the UI, this could be used to show/hide a connectivity status.</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span>
|
|
568
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#C792EA;"> quantum</span><span style="color:#89DDFF;">></span></span>
|
|
569
|
+
<span class="line"><span style="color:#C792EA;"> let</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> network</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> {</span><span style="color:#BABED8;"> connectivity </span><span style="color:#89DDFF;">}</span><span style="color:#89DDFF;"> }</span><span style="color:#89DDFF;"> =</span><span style="color:#BABED8;"> document</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">bindings</span><span style="color:#89DDFF;">;</span></span>
|
|
570
|
+
<span class="line"><span style="color:#82AAFF;"> $</span><span style="color:#BABED8;">(</span><span style="color:#89DDFF;">this</span><span style="color:#BABED8;">)</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">attr</span><span style="color:#BABED8;">( </span><span style="color:#89DDFF;">'</span><span style="color:#C3E88D;">connectivity</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;"> connectivity })</span><span style="color:#89DDFF;">;</span></span>
|
|
571
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
572
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">></span></span></code></pre></div></details><p>Here are some additional examples with the <a href="#the-observer-api">Observer API</a>.</p><details><summary>Visualize the network state...</summary><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">// Visualize the network state</span></span>
|
|
573
|
+
<span class="line"><span style="color:#C792EA;">let</span><span style="color:#BABED8;"> networkVisualizer </span><span style="color:#89DDFF;">=</span><span style="color:#BABED8;font-style:italic;"> changes</span><span style="color:#C792EA;"> =></span><span style="color:#89DDFF;"> {</span></span>
|
|
574
|
+
<span class="line"><span style="color:#BABED8;"> changes</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">forEach</span><span style="color:#F07178;">(</span><span style="color:#BABED8;font-style:italic;">e</span><span style="color:#C792EA;"> =></span><span style="color:#89DDFF;"> {</span></span>
|
|
575
|
+
<span class="line"><span style="color:#BABED8;"> console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#BABED8;">e</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">name</span><span style="color:#89DDFF;">,</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">:</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;"> e</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">value</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
576
|
+
<span class="line"><span style="color:#89DDFF;"> }</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
577
|
+
<span class="line"><span style="color:#89DDFF;">};</span></span>
|
|
578
|
+
<span class="line"><span style="color:#BABED8;">Observer</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">observe</span><span style="color:#BABED8;">(document</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">bindings</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">network</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;"> networkVisualizer)</span><span style="color:#89DDFF;">;</span></span>
|
|
579
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">// Or: Observer.observe(document, [ ['state', 'network'] ], networkVisualizer, { subtree: true });</span></span></code></pre></div></details><details><summary>Visualize "connectivity"...</summary><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">// Visualize the 'connectivity' property</span></span>
|
|
580
|
+
<span class="line"><span style="color:#C792EA;">let</span><span style="color:#BABED8;"> connectivityVisualizer </span><span style="color:#89DDFF;">=</span><span style="color:#BABED8;font-style:italic;"> e</span><span style="color:#C792EA;"> =></span><span style="color:#89DDFF;"> {</span></span>
|
|
581
|
+
<span class="line"><span style="color:#BABED8;"> console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">'</span><span style="color:#C3E88D;">You are </span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;"> e</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">value</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
582
|
+
<span class="line"><span style="color:#89DDFF;">};</span></span>
|
|
583
|
+
<span class="line"><span style="color:#BABED8;">Observer</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">observe</span><span style="color:#BABED8;">(document</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">bindings</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">network</span><span style="color:#89DDFF;">,</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">connectivity</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;"> connectivityVisualizer)</span><span style="color:#89DDFF;">;</span></span>
|
|
584
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">// Or: Observer.observe(document.bindings, [ ['network', 'connectivity'] ], connectivityeVisualizer);</span></span></code></pre></div></details><details><summary>Catch request errors; attempt a retry...</summary><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">// Catch request errors; attempt a retry</span></span>
|
|
585
|
+
<span class="line"><span style="color:#BABED8;">Observer</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">observe</span><span style="color:#BABED8;">(document</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">bindings</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">network</span><span style="color:#89DDFF;">,</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">error</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> e</span><span style="color:#C792EA;"> =></span><span style="color:#89DDFF;"> {</span></span>
|
|
586
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#89DDFF;">!</span><span style="color:#BABED8;">e</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">value</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#89DDFF;">;</span></span>
|
|
587
|
+
<span class="line"><span style="color:#BABED8;"> console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">error</span><span style="color:#F07178;">(</span><span style="color:#BABED8;">e</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">value</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">message</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
588
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">e</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">value</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">retry</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
|
|
589
|
+
<span class="line"><span style="color:#BABED8;"> console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">error</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">'</span><span style="color:#C3E88D;">Retrying...</span><span style="color:#89DDFF;">'</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
590
|
+
<span class="line"><span style="color:#BABED8;"> e</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">value</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">retry</span><span style="color:#F07178;">()</span><span style="color:#89DDFF;">;</span></span>
|
|
591
|
+
<span class="line"><span style="color:#89DDFF;"> }</span></span>
|
|
592
|
+
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#BABED8;">)</span><span style="color:#89DDFF;">;</span></span></code></pre></div></details><h5 id="form-actions" tabindex="-1">Form Actions <a class="header-anchor" href="#form-actions" aria-label="Permalink to "Form Actions""></a></h5><p>When navigation occurs <a href="#scenario-4-single-page-navigation-requests-and-responses">via form submissions</a>, the form element and the submit button are made to go on the <em>active</em> state while the request is being processed. For both of these elements, the Webflo client simply sets the <code>element.state.active</code> to <code>true</code> on submission, then <code>false</code>, on completion.</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">form</span><span style="color:#C792EA;"> method</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">post</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></span></span>
|
|
593
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">input</span><span style="color:#C792EA;"> name</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">username</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> placeholder</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">Your username...</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;"> /></span></span>
|
|
594
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
595
|
+
<span class="line"><span style="color:#82AAFF;"> $</span><span style="color:#BABED8;">(</span><span style="color:#89DDFF;">this</span><span style="color:#BABED8;">)</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">css</span><span style="color:#BABED8;">(</span><span style="color:#89DDFF;">this.</span><span style="color:#BABED8;">state</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">active </span><span style="color:#89DDFF;">?</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> pointerEvents</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">none</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> opacity</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">o.5</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> }</span><span style="color:#89DDFF;"> :</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> pointerEvents</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">auto</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> opacity</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">1</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> }</span><span style="color:#BABED8;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
596
|
+
<span class="line"><span style="color:#89DDFF;"> </</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">></span></span>
|
|
597
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">form</span><span style="color:#89DDFF;">></span></span></code></pre></div><p>Also, you'd remember that HTML forms can only accept two HTTP methods on their <code>method</code> attribute: <code>GET</code>, <code>POST</code>! And the same constraint exists on the equivalent <code>formmethod</code> attribue in submit buttons. You are able to overcome this in Webflo by using alternative <code>data-</code> attributes: <code>data-method</code>, <code>data-formmethod</code>, respectively.</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;"><</span><span style="color:#F07178;">form</span><span style="color:#C792EA;"> data-method</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">patch</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">></span></span>
|
|
598
|
+
<span class="line"><span style="color:#89DDFF;"> <</span><span style="color:#F07178;">input</span><span style="color:#C792EA;"> name</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">price</span><span style="color:#89DDFF;">"</span><span style="color:#C792EA;"> placeholder</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">Enter new price...</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;"> /></span></span>
|
|
599
|
+
<span class="line"><span style="color:#89DDFF;"></</span><span style="color:#F07178;">form</span><span style="color:#89DDFF;">></span></span></code></pre></div><h4 id="progressive-web-apps" tabindex="-1">Progressive Web Apps <a class="header-anchor" href="#progressive-web-apps" aria-label="Permalink to "Progressive Web Apps""></a></h4><p>Webflo client-side applications are intended to provide an app-like-first experience. So unless disabled in config, a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API" target="_blank" rel="noreferrer">Service Worker</a> is built as part of your application on running the <code>npm run generate</code> command. And as covered above, you may define <a href="#handler-functions-and-layout">route handlers in the <code>/worker</code> directory</a> of your application, and these will be built into the service worker to handle Same-Origin requests of the application. Where there are no <em>worker</em> 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.</p><details><summary>Config (Default)</summary><div class="language-json"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">{</span><span style="color:#89DDFF;"> "</span><span style="color:#C792EA;">service_worker_support</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> true</span><span style="color:#89DDFF;"> }</span></span></code></pre></div><blockquote><p><strong>File: <code>.webqit/webflo/client.json</code></strong></p></blockquote><blockquote><p><strong>Command: <code>webflo config client service_worker_support=TRUE</code></strong></p></blockquote></details><h5 id="fetching-strategy" tabindex="-1">Fetching Strategy <a class="header-anchor" href="#fetching-strategy" aria-label="Permalink to "Fetching Strategy""></a></h5><details><summary>Network First</summary><p>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.</p><details><summary>Config (Default)</summary><div class="language-json"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">{</span><span style="color:#89DDFF;"> "</span><span style="color:#C792EA;">default_fetching_strategy</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> "</span><span style="color:#C3E88D;">network-first</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;"> }</span></span></code></pre></div><p><em>To list specific URLs...</em></p><div class="language-json"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">{</span><span style="color:#89DDFF;"> "</span><span style="color:#C792EA;">network_first_urls</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> [</span><span style="color:#89DDFF;"> "</span><span style="color:#C3E88D;">/logo.png</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;"> ]</span><span style="color:#89DDFF;"> }</span></span></code></pre></div><blockquote><p><strong>File: <code>.webqit/webflo/worker.json</code></strong></p></blockquote><blockquote><p><strong>Command: <code>webflo config worker default_fetching_strategy=network-first</code></strong></p></blockquote></details></details><details><summary>Cache First</summary><p>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.</p><details><summary>Config</summary><div class="language-json"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">{</span><span style="color:#89DDFF;"> "</span><span style="color:#C792EA;">default_fetching_strategy</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> "</span><span style="color:#C3E88D;">cache-first</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;"> }</span></span></code></pre></div><p><em>To list specific URLs...</em></p><div class="language-json"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">{</span><span style="color:#89DDFF;"> "</span><span style="color:#C792EA;">cache_first_urls</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> [</span><span style="color:#89DDFF;"> "</span><span style="color:#C3E88D;">/logo.png</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;"> ]</span><span style="color:#89DDFF;"> }</span></span></code></pre></div><blockquote><p><strong>File: <code>.webqit/webflo/worker.json</code></strong></p></blockquote><blockquote><p><strong>Command: <code>webflo config worker default_fetching_strategy=cache-first</code></strong></p></blockquote></details></details><details><summary>Network Only</summary><p>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.</p><details><summary>Config</summary><div class="language-json"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">{</span><span style="color:#89DDFF;"> "</span><span style="color:#C792EA;">default_fetching_strategy</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> "</span><span style="color:#C3E88D;">network-only</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;"> }</span></span></code></pre></div><p><em>To list specific URLs...</em></p><div class="language-json"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">{</span><span style="color:#89DDFF;"> "</span><span style="color:#C792EA;">network_only_urls</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> [</span><span style="color:#89DDFF;"> "</span><span style="color:#C3E88D;">/logo.png</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;"> ]</span><span style="color:#89DDFF;"> }</span></span></code></pre></div><blockquote><p><strong>File: <code>.webqit/webflo/worker.json</code></strong></p></blockquote><blockquote><p><strong>Command: <code>webflo config worker default_fetching_strategy=network-only</code></strong></p></blockquote></details></details><details><summary>Cache Only</summary><p>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 <code>install</code> event of the Service Worker.)</p><details><summary>Config</summary><div class="language-json"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">{</span><span style="color:#89DDFF;"> "</span><span style="color:#C792EA;">default_fetching_strategy</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> "</span><span style="color:#C3E88D;">cache-only</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;"> }</span></span></code></pre></div><p><em>To list specific URLs...</em></p><div class="language-json"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">{</span><span style="color:#89DDFF;"> "</span><span style="color:#C792EA;">cache_only_urls</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> [</span><span style="color:#89DDFF;"> "</span><span style="color:#C3E88D;">/logo.png</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;"> ]</span><span style="color:#89DDFF;"> }</span></span></code></pre></div><blockquote><p><strong>File: <code>.webqit/webflo/worker.json</code></strong></p></blockquote><blockquote><p><strong>Command: <code>webflo config worker default_fetching_strategy=cache-only</code></strong></p></blockquote></details></details><p>In all cases above, the convention for specifying URLs for a strategy accepts an <a href="https://developer.mozilla.org/en-US/docs/Web/API/URLPattern" target="_blank" rel="noreferrer">URL pattern</a> - against which URLs can be matched on the fly. For example, to place all files in an <code>/image</code> directory (and subdirectories) on the <em>Cache First</em> strategy, the pattern <code>/image/*</code> can be used. To place all <code>.svg</code> files in an <code>/icons</code> directory (including subdirectories) on the <em>Cache Only</em> strategy, the pattern <code>/icons/*.svg</code> can be used. (Specifically for the <em>Cache Only</em> strategy, patterns are resolved at Service Worker build-time, and each pattern must match, at least, a file.)</p><details><summary>Example...</summary><div class="language-json"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">{</span><span style="color:#89DDFF;"> "</span><span style="color:#C792EA;">cache_only_urls</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> [</span><span style="color:#89DDFF;"> "</span><span style="color:#C3E88D;">/icons/*.svg</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;"> ]</span><span style="color:#89DDFF;"> }</span></span></code></pre></div></details><h5 id="cross-thread-communications" tabindex="-1">Cross-Thread Communications <a class="header-anchor" href="#cross-thread-communications" aria-label="Permalink to "Cross-Thread Communications""></a></h5><p>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:</p><h6 id="the-workport-api" tabindex="-1">The <code>workport</code> API <a class="header-anchor" href="#the-workport-api" aria-label="Permalink to "The `workport` API""></a></h6><p>This is an object with simple methods for working with <em>cross-thread</em> messages, UI Notifications, and Push Notifications.</p><p>On both the client and worker side of your application, the <code>workport</code> object is accessible from route handlers as <code>this.runtime.workport</code>.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
600
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> [client|worker]</span></span>
|
|
601
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">├── index.js</span></span>
|
|
602
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">*/</span></span>
|
|
603
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#89DDFF;font-style:italic;"> default</span><span style="color:#C792EA;"> async</span><span style="color:#C792EA;"> function</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
604
|
+
<span class="line"><span style="color:#C792EA;"> let</span><span style="color:#89DDFF;"> {</span><span style="color:#BABED8;"> workport</span><span style="color:#89DDFF;"> }</span><span style="color:#89DDFF;"> =</span><span style="color:#89DDFF;"> this.</span><span style="color:#BABED8;">runtime</span><span style="color:#89DDFF;">;</span></span>
|
|
605
|
+
<span class="line"><span style="color:#BABED8;"> workport</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">messaging</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">post</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">{</span><span style="color:#89DDFF;"> ...</span><span style="color:#89DDFF;"> }</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
606
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span><span style="color:#89DDFF;"> ...</span><span style="color:#89DDFF;"> };</span></span>
|
|
607
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><p>For cross-thread messaging, both sides of the API exposes the following methods:</p><details><summary>Method: <code>.messaging.post()</code></summary><p>The <code>.messaging.post()</code> method is used for sending any arbitrary data to the other side. E.g. <code>workport.messaging.post({ type: 'TEST' })</code>.</p></details><details><summary>Method: <code>.messaging.listen()</code></summary><p>The <code>.messaging.listen()</code> method is used for registering a listener to the <code>message</code> event from the other side. E.g. <code>workport.messaging.listen(event => console.log(event.data.type))</code>. (See <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/message_event" target="_blank" rel="noreferrer"><code>window: onmessage</code></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/message_event" target="_blank" rel="noreferrer"><code>worker: onmessage</code></a>.)</p></details><details><summary>Method: <code>.messaging.request()</code></summary><p>The <code>.messaging.request()</code> method is used for sending a message to the other side and obtaing a response, using the <a href="https://developer.mozilla.org/docs/Web/API/MessageChannel/MessageChannel" target="_blank" rel="noreferrer">MessageChannel</a> API.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">// On the worker side</span></span>
|
|
608
|
+
<span class="line"><span style="color:#BABED8;">workport</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">messaging</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">listen</span><span style="color:#BABED8;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#C792EA;"> =></span><span style="color:#89DDFF;"> {</span></span>
|
|
609
|
+
<span class="line"><span style="color:#BABED8;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#BABED8;">event</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">data</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
610
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">event</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">ports</span><span style="color:#F07178;">[</span><span style="color:#F78C6C;">0</span><span style="color:#F07178;">]) </span><span style="color:#89DDFF;">{</span></span>
|
|
611
|
+
<span class="line"><span style="color:#BABED8;"> event</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">ports</span><span style="color:#F07178;">[</span><span style="color:#F78C6C;">0</span><span style="color:#F07178;">]</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">postMessage</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">{</span><span style="color:#F07178;"> type</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">WORKS</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> }</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
612
|
+
<span class="line"><span style="color:#89DDFF;"> }</span></span>
|
|
613
|
+
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#BABED8;">)</span><span style="color:#89DDFF;">;</span></span></code></pre></div><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">// On the client side</span></span>
|
|
614
|
+
<span class="line"><span style="color:#C792EA;">let</span><span style="color:#BABED8;"> response </span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;font-style:italic;"> await</span><span style="color:#BABED8;"> workport</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">messaging</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">request</span><span style="color:#BABED8;">(</span><span style="color:#89DDFF;">{</span><span style="color:#F07178;"> type</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">TEST</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> }</span><span style="color:#BABED8;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
615
|
+
<span class="line"><span style="color:#BABED8;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#BABED8;">(response)</span><span style="color:#89DDFF;">;</span><span style="color:#676E95;font-style:italic;"> // { type: 'WORKS' }</span></span></code></pre></div></details><details><summary>Method: <code>.messaging.channel()</code></summary><p>The <code>.messaging.channel()</code> method is used for sending <em>broadcast</em> messages to the other side - including all other browsing contents that live on the same origin, using the <a href="https://developer.mozilla.org/docs/Web/API/Broadcast_Channel_API" target="_blank" rel="noreferrer">Broadcast Channel</a> API.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">// On the worker side</span></span>
|
|
616
|
+
<span class="line"><span style="color:#C792EA;">let</span><span style="color:#BABED8;"> channelId </span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">channel-1</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">;</span></span>
|
|
617
|
+
<span class="line"><span style="color:#BABED8;">workport</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">messaging</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">channel</span><span style="color:#BABED8;">(channelId)</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">listen</span><span style="color:#BABED8;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#C792EA;"> =></span><span style="color:#89DDFF;"> {</span></span>
|
|
618
|
+
<span class="line"><span style="color:#BABED8;"> console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#BABED8;">event</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">data</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
619
|
+
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#BABED8;">)</span><span style="color:#89DDFF;">;</span></span></code></pre></div><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">// On the client side</span></span>
|
|
620
|
+
<span class="line"><span style="color:#C792EA;">let</span><span style="color:#BABED8;"> channelId </span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">channel-1</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">;</span></span>
|
|
621
|
+
<span class="line"><span style="color:#BABED8;">workport</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">messaging</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">channel</span><span style="color:#BABED8;">(channelId)</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">broadcast</span><span style="color:#BABED8;">(</span><span style="color:#89DDFF;">{</span><span style="color:#F07178;"> type</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">TEST</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;"> }</span><span style="color:#BABED8;">)</span><span style="color:#89DDFF;">;</span></span></code></pre></div></details><p>For <a href="https://developer.mozilla.org/en-US/docs/Web/API/notification" target="_blank" rel="noreferrer">UI Nofitications</a>, both sides of the API exposes the following methods:</p><details><summary>Method: <code>.nofitications.fire()</code></summary><p>The <code>.nofitications.fire()</code> method is used for firing up a UI notification. This uses the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Notification/Notification" target="_blank" rel="noreferrer"><code>Nofitications constructor</code></a>, and thus, accepts the same arguments as the constructor. But it returns a <code>Promise</code> that resolves when the notification is <em>clicked</em> or <em>closed</em>, but rejects when the notification encounters an error, or when the application isn't granted the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Notification/requestPermission" target="_blank" rel="noreferrer">notification permission</a>.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#C792EA;">let</span><span style="color:#BABED8;"> title </span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">Test Nofitication</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">;</span></span>
|
|
622
|
+
<span class="line"><span style="color:#C792EA;">let</span><span style="color:#BABED8;"> options </span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> body</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">...</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> icon</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">...</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> actions</span><span style="color:#89DDFF;">:</span><span style="color:#BABED8;"> [ </span><span style="color:#89DDFF;">...</span><span style="color:#BABED8;"> ] </span><span style="color:#89DDFF;">};</span></span>
|
|
623
|
+
<span class="line"><span style="color:#BABED8;">workport</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">nofitications</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">fire</span><span style="color:#BABED8;">(title</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;"> options)</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">then</span><span style="color:#BABED8;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#C792EA;"> =></span><span style="color:#89DDFF;"> {</span></span>
|
|
624
|
+
<span class="line"><span style="color:#BABED8;"> console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#BABED8;">event</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">action</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
625
|
+
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#BABED8;">)</span><span style="color:#89DDFF;">;</span></span></code></pre></div></details><details><summary>Method: <code>.nofitications.handle()</code></summary><p>The <code>.nofitications.handle()</code> method (in Service-Workers) is used for handling <a href="https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/notificationclick_event" target="_blank" rel="noreferrer"><code>notificationclick</code></a> events. (Handlers are called each time a notification is clicked.)</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#BABED8;">workport</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">nofitications</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">handle</span><span style="color:#BABED8;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#C792EA;"> =></span><span style="color:#89DDFF;"> {</span></span>
|
|
626
|
+
<span class="line"><span style="color:#BABED8;"> console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#BABED8;">event</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">action</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
627
|
+
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#BABED8;">)</span><span style="color:#89DDFF;">;</span></span></code></pre></div></details><p>For <a href="https://developer.mozilla.org/en-US/docs/Web/API/Push_API" target="_blank" rel="noreferrer">Push Nofitications</a>, the client-side of the API exposes the following methods:</p><details><summary>Method: <code>.push.subscribe()</code></summary><p>The <code>.push.subscribe()</code> method is the equivalent of the <a href="https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe" target="_blank" rel="noreferrer"><code>PushManager.subscribe()</code></a> method. (But this can also take the <em>applicationServerKey</em> as a first argument, and other options as a second argument, in which case it automatically runs the key through an <code>urlBase64ToUint8Array()</code> function.)</p></details><details><summary>Method: <code>.push.unsubscribe()</code></summary><p>The <code>.push.unsubscribe()</code> method is the equivalent of the <a href="https://developer.mozilla.org/en-US/docs/Web/API/PushSubscription/unsubscribe" target="_blank" rel="noreferrer"><code>PushSubscription.unsubscribe()</code></a> method.</p></details><details><summary>Method: <code>.push.getSubscription()</code></summary><p>The <code>.push.getSubscription()</code> method is the equivalent of the <a href="https://developer.mozilla.org/en-US/docs/Web/API/PushManager/getSubscription" target="_blank" rel="noreferrer"><code>PushManager.getSubscription()</code></a> method.</p></details><p>The worker-side of the API exposes the following methods:</p><details><summary>Method: <code>.push.listen()</code></summary><p>The <code>.push.listen()</code> method is for listening to the <a href="https://developer.mozilla.org/en-US/docs/Web/API/PushEvent" target="_blank" rel="noreferrer"><code>push</code></a> from within Service Workers. E.g. <code>workport.push.listen(event => console.log(event.data.type))</code>.</p></details><h6 id="route-events" tabindex="-1">Route <em>events</em> <a class="header-anchor" href="#route-events" aria-label="Permalink to "Route *events*""></a></h6><p>These are simple route events that fire when messaging and notification events happen.</p><p>On both the client and worker side of your application, you can define an event listener alongside your <em>root</em> route handler. The event listener is called to handle all messaging and notification events that happen.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
628
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> [client|worker]</span></span>
|
|
629
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">├── index.js</span></span>
|
|
630
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">*/</span></span>
|
|
631
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#89DDFF;font-style:italic;"> default</span><span style="color:#C792EA;"> async</span><span style="color:#C792EA;"> function</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
632
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span><span style="color:#89DDFF;"> ...</span><span style="color:#89DDFF;"> };</span></span>
|
|
633
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span>
|
|
634
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#C792EA;"> async</span><span style="color:#C792EA;"> function</span><span style="color:#82AAFF;"> alert</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
635
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span><span style="color:#89DDFF;"> ...</span><span style="color:#89DDFF;"> };</span></span>
|
|
636
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><p>The event type is given in the <code>event.type</code> property. This could be:</p><ul><li><strong><code>message</code></strong> - both client and worker side. For <em>replyable</em> messages, the event handler's return value is automatically sent back as response.</li><li><strong><code>notificationclick</code></strong> - worker side.</li><li><strong><code>push</code></strong> - worker side.</li></ul><details><summary>Advanced...</summary><p>The <code>next()</code> 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.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
637
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> worker</span></span>
|
|
638
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">├── index.js</span></span>
|
|
639
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">*/</span></span>
|
|
640
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#C792EA;"> async</span><span style="color:#C792EA;"> function</span><span style="color:#82AAFF;"> alert</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
641
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">event</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">type</span><span style="color:#89DDFF;"> ===</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">push</span><span style="color:#89DDFF;">'</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
|
|
642
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> await</span><span style="color:#82AAFF;"> next</span><span style="color:#F07178;">(</span><span style="color:#BABED8;">context</span><span style="color:#89DDFF;">,</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">/services/push</span><span style="color:#89DDFF;">'</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
643
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;">;</span></span>
|
|
644
|
+
<span class="line"><span style="color:#89DDFF;"> }</span></span>
|
|
645
|
+
<span class="line"><span style="color:#BABED8;"> console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#BABED8;">event</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">type</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
646
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div></details><h4 id="api-backends" tabindex="-1">API Backends <a class="header-anchor" href="#api-backends" aria-label="Permalink to "API Backends""></a></h4><p>In Webflo, an API backend is what you, in essence, come off with with your server-side routes.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
647
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">server</span></span>
|
|
648
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── index.js</span></span>
|
|
649
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
|
|
650
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#89DDFF;font-style:italic;"> default</span><span style="color:#C792EA;"> function</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
651
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">next</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">pathname</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#82AAFF;"> next</span><span style="color:#F07178;">()</span><span style="color:#89DDFF;">;</span></span>
|
|
652
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span><span style="color:#89DDFF;"> ...</span><span style="color:#89DDFF;"> };</span></span>
|
|
653
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><p>You are always able to lay out your route handlers in the structure for a formal REST API.</p><div class="language-shell"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#FFCB6B;">server</span></span>
|
|
654
|
+
<span class="line"><span style="color:#FFCB6B;"> ├──</span><span style="color:#C3E88D;"> index.js</span></span>
|
|
655
|
+
<span class="line"><span style="color:#FFCB6B;"> ├──</span><span style="color:#C3E88D;"> api/v1/index.js</span></span>
|
|
656
|
+
<span class="line"><span style="color:#FFCB6B;"> └──</span><span style="color:#C3E88D;"> api/v1/products/index.js</span></span></code></pre></div><p>And if you will partition your backend for both page routes and a formal REST API...</p><div class="language-shell"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#FFCB6B;">server</span></span>
|
|
657
|
+
<span class="line"><span style="color:#FFCB6B;"> ├──</span><span style="color:#C3E88D;"> index.js</span><span style="color:#C3E88D;"> ──┐</span></span>
|
|
658
|
+
<span class="line"><span style="color:#FFCB6B;"> ├──</span><span style="color:#C3E88D;"> cart/index.js</span><span style="color:#C3E88D;"> ├─</span><span style="color:#C3E88D;"> Page</span><span style="color:#C3E88D;"> Routes</span></span>
|
|
659
|
+
<span class="line"><span style="color:#FFCB6B;"> ├──</span><span style="color:#C3E88D;"> products/index.js</span><span style="color:#C3E88D;"> ──┘</span></span>
|
|
660
|
+
<span class="line"><span style="color:#FFCB6B;"> ├──</span><span style="color:#C3E88D;"> api/v1/index.js</span><span style="color:#C3E88D;"> ──┐</span></span>
|
|
661
|
+
<span class="line"><span style="color:#FFCB6B;"> ├──</span><span style="color:#C3E88D;"> api/v1/orders/index.js</span><span style="color:#C3E88D;"> ├─</span><span style="color:#C3E88D;"> REST</span><span style="color:#C3E88D;"> API</span></span>
|
|
662
|
+
<span class="line"><span style="color:#FFCB6B;"> └──</span><span style="color:#C3E88D;"> api/v1/products/index.js</span><span style="color:#C3E88D;"> ──┘</span></span></code></pre></div><p>...you could get your page routes to run off your REST API by re-routing your <code>next()</code> calls to consume the appropriate API route.</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
|
|
663
|
+
<span class="line"><span style="color:#676E95;font-style:italic;">server</span></span>
|
|
664
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> ├── cart/index.js</span></span>
|
|
665
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
|
|
666
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#89DDFF;font-style:italic;"> default</span><span style="color:#C792EA;"> async</span><span style="color:#C792EA;"> function</span><span style="color:#89DDFF;">(</span><span style="color:#BABED8;font-style:italic;">event</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> context</span><span style="color:#89DDFF;">,</span><span style="color:#BABED8;font-style:italic;"> next</span><span style="color:#89DDFF;">)</span><span style="color:#89DDFF;"> {</span></span>
|
|
667
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> if</span><span style="color:#F07178;"> (</span><span style="color:#BABED8;">next</span><span style="color:#89DDFF;">.</span><span style="color:#BABED8;">pathname</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
|
|
668
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#82AAFF;"> next</span><span style="color:#F07178;">()</span><span style="color:#89DDFF;">;</span></span>
|
|
669
|
+
<span class="line"><span style="color:#89DDFF;"> }</span></span>
|
|
670
|
+
<span class="line"><span style="color:#676E95;font-style:italic;"> // Items to display in cart are in the "/api/v1/orders" route</span></span>
|
|
671
|
+
<span class="line"><span style="color:#C792EA;"> let</span><span style="color:#BABED8;"> cartItems</span><span style="color:#89DDFF;"> =</span><span style="color:#89DDFF;font-style:italic;"> await</span><span style="color:#82AAFF;"> next</span><span style="color:#F07178;">(</span><span style="color:#BABED8;">context</span><span style="color:#89DDFF;">,</span><span style="color:#89DDFF;"> `</span><span style="color:#C3E88D;">/api/v1/orders?user_id=1</span><span style="color:#89DDFF;">`</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
|
|
672
|
+
<span class="line"><span style="color:#89DDFF;font-style:italic;"> return</span><span style="color:#89DDFF;"> {</span><span style="color:#F07178;"> title</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> '</span><span style="color:#C3E88D;">Your Cart</span><span style="color:#89DDFF;">'</span><span style="color:#89DDFF;">,</span><span style="color:#89DDFF;"> ...</span><span style="color:#BABED8;">cartItems</span><span style="color:#89DDFF;"> };</span></span>
|
|
673
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><p>This way, there is one source of truth for your application - both when visiting from a page and from a REST API.</p><h4 id="static-sites" tabindex="-1">Static Sites <a class="header-anchor" href="#static-sites" aria-label="Permalink to "Static Sites""></a></h4><p>You can build an entire static site from off the <code>/public</code> directory alone! It's all about placing files and HTML pages there to be served statically!</p><p>Here, static pages means pages that are not server-rendered during the request/response cycle, but served directly from files. You are free to hand-author each of them - either as standalone <code>index.html</code> files, or as a combination of <code>index.html</code> <em>roots</em> plus <em>templates</em> that can all get resolved client-side. The <a href="https://github.com/webqit/webflo/blob/master/README.md#pages-layout-and-templating" target="_blank" rel="noreferrer">Pages, Layout and Templating</a> section covers layout patterns.</p><p>On the other hand, if you have a dynamic site, you can make a static site off it! The idea is to turn on your server and crawl your dynamic site via HTTP requests, outputting static HTML representations of each page. This is called <em>Pre-Rendering</em> or <em>Static-Site Generation</em> (SSG)!</p><p>A simple tool, like <a href="https://github.com/tj/staticgen" target="_blank" rel="noreferrer"><code>staticgen</code></a>, or the basic <a href="https://www.gnu.org/software/wget/" target="_blank" rel="noreferrer"><code>wget</code></a> command (similar to <code>curl</code>), can get this done in an instant. On figuring out the command that works best for you, you may want to add an alias of the command to your npm scripts in <code>package.json</code>.</p><div class="language-json"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">"</span><span style="color:#C3E88D;">scripts</span><span style="color:#89DDFF;">"</span><span style="color:#BABED8;">: </span><span style="color:#89DDFF;">{</span></span>
|
|
674
|
+
<span class="line"><span style="color:#89DDFF;"> "</span><span style="color:#C792EA;">generate:site</span><span style="color:#89DDFF;">"</span><span style="color:#89DDFF;">:</span><span style="color:#89DDFF;"> "</span><span style="color:#C3E88D;">wget -P public -nv -nH -r -E localhost:3000</span><span style="color:#89DDFF;">"</span></span>
|
|
675
|
+
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><details><summary>How it works...</summary><blockquote><p>Above, we used the <code>-P</code> flag to specify the output directory as <code>public</code>, the <code>-nv</code> flag to opt into “non-verbose” mode which outputs less information, the <code>-r</code> flag to get it to crawl and download recursively, and the <code>-E</code> flag to get it to add the <code>.html</code> extension to generated files.</p></blockquote></details><p><em>Happy static!</em></p><h2 id="webflo-config" tabindex="-1">Webflo Config <a class="header-anchor" href="#webflo-config" aria-label="Permalink to "Webflo Config""></a></h2><p>Webflo comes <em>convention-first</em>! But it is entirely configurable for when you need it! The easiest way to do this is to run the command <code>webflo config</code> and follow the walkthrough. To simply get an overview, use the command <code>webflo config help</code>, and all commands and their description are shown.</p><h2 id="webflo-ecosystem" tabindex="-1">Webflo Ecosystem <a class="header-anchor" href="#webflo-ecosystem" aria-label="Permalink to "Webflo Ecosystem""></a></h2><p>Webflo applications are often built on/with the following technologies.</p><ul><li><a href="#oohtml">OOHTML</a></li><li><a href="#oohtml-ssr">OOHTML SSR</a></li><li><a href="#oohtml-cli">OOHTML CLI</a></li><li><a href="#the-observer-api">The Observer API</a></li></ul><h3 id="oohtml" tabindex="-1">OOHTML <a class="header-anchor" href="#oohtml" aria-label="Permalink to "OOHTML""></a></h3><p><a href="https://github.com/webqit/oohtml" target="_blank" rel="noreferrer">OOHTML</a> is a set of new features for HTML that makes it fun to hand-author your HTML documents! Within OOHTML are <a href="https://github.com/webqit/oohtml#html-modules" target="_blank" rel="noreferrer">HTML Modules</a> and <a href="https://github.com/webqit/oohtml#html-imports" target="_blank" rel="noreferrer">HTML Imports</a>, <a href="https://github.com/webqit/oohtml#subscript" target="_blank" rel="noreferrer">Reactive Scripts</a> and more!</p><h3 id="oohtml-ssr" tabindex="-1">OOHTML SSR <a class="header-anchor" href="#oohtml-ssr" aria-label="Permalink to "OOHTML SSR""></a></h3><p><a href="https://github.com/webqit/oohtml-ssr" target="_blank" rel="noreferrer">OOHTML SSR</a> is a server-side DOM implementation with native support for OOHTML. This is internally used by Webflo as the Server-Side Rendering engine, and it it what gives Webflo its native support for OOHTML.</p><h3 id="oohtml-cli" tabindex="-1">OOHTML CLI <a class="header-anchor" href="#oohtml-cli" aria-label="Permalink to "OOHTML CLI""></a></h3><p><a href="https://github.com/webqit/oohtml-cli" target="_blank" rel="noreferrer">OOHTML CLI</a> is a small Command Line utility that automates certain aspects of hand-authored OOHTML-based documents.</p><h3 id="the-observer-api" tabindex="-1">The Observer API <a class="header-anchor" href="#the-observer-api" aria-label="Permalink to "The Observer API""></a></h3><p><a href="https://github.com/webqit/observer" target="_blank" rel="noreferrer">The Observer API</a> is a simple set of functions for intercepting and observing JavaScript objects and arrays. (Reflection, Interception, and Events.)</p><p>This is part of OOHTML's reactivity system, and it is made available on OOHTML-based documents as <code>window.WebQit.Observer</code>.</p></div></div></main><footer class="VPDocFooter" data-v-39a288b8 data-v-e257564d><!--[--><!--]--><!----><nav class="prev-next" aria-labelledby="doc-footer-aria-label" data-v-e257564d><span class="visually-hidden" id="doc-footer-aria-label" data-v-e257564d>Pager</span><div class="pager" data-v-e257564d><!----></div><div class="pager" data-v-e257564d><a class="VPLink link pager-link next" href="/docs" data-v-e257564d><!--[--><span class="desc" data-v-e257564d>Next page</span><span class="title" data-v-e257564d>Welcome</span><!--]--></a></div></nav></footer><!--[--><!--]--></div></div></div><!--[--><!--]--></div></div><footer class="VPFooter has-sidebar" data-v-5d98c3a5 data-v-e315a0ad><div class="container" data-v-e315a0ad><p class="message" data-v-e315a0ad>MIT Licensed</p><p class="copyright" data-v-e315a0ad>© webqit</p></div></footer><!--[--><!--]--></div></div>
|
|
676
|
+
<script>window.__VP_HASH_MAP__=JSON.parse("{\"-__.md\":\"wD5kDRhS\",\"-_docs.old.md\":\"CiqucE_1\",\"api.md\":\"C9KRNLi_\",\"api_webflo-fetch_fetch.md\":\"Bc9r3Q9I\",\"api_webflo-fetch_formdata.md\":\"CDuexUTz\",\"api_webflo-fetch_headers.md\":\"Cl_4-FUP\",\"api_webflo-fetch_liveresponse.md\":\"BMidKMB1\",\"api_webflo-fetch_request.md\":\"DPwZCPmi\",\"api_webflo-fetch_response.md\":\"BaSyoOLE\",\"api_webflo-routing_handler.md\":\"B0eVFluL\",\"api_webflo-routing_handler_fetch.md\":\"CpwUMFMz\",\"api_webflo-routing_handler_next.md\":\"CA4tDXtV\",\"api_webflo-routing_httpcookies.md\":\"B5ok3jrM\",\"api_webflo-routing_httpevent.md\":\"DBkSQRTa\",\"api_webflo-routing_httpevent_respondwith.md\":\"aAxq-5Ie\",\"api_webflo-routing_httpevent_waituntil.md\":\"DyQZLhPR\",\"api_webflo-routing_httpevent_waituntilnavigate.md\":\"DKLylwhl\",\"api_webflo-routing_httpsession.md\":\"bkeCy7_Q\",\"api_webflo-routing_httpstate.md\":\"DO53IAM1\",\"api_webflo-routing_httpuser.md\":\"CfsaBFdl\",\"contributing.md\":\"gqIMCtVI\",\"docs.md\":\"hvbqYbFY\",\"docs_advanced.md\":\"D048cxnq\",\"docs_advanced_lifecycles.md\":\"BICPL-da\",\"docs_advanced_redirects.md\":\"BMha6D3W\",\"docs_advanced_routing.md\":\"Cv63UDJF\",\"docs_concepts.md\":\"Clwx81Hz\",\"docs_concepts_realtime.md\":\"CBrMq5Ln\",\"docs_concepts_rendering.md\":\"BWr5Lxgn\",\"docs_concepts_request-response.md\":\"DhplzNqt\",\"docs_concepts_routing.md\":\"C2KO1eAu\",\"docs_concepts_state.md\":\"CtbMVS_K\",\"docs_concepts_templates.md\":\"4i6jQcYw\",\"docs_getting-started.md\":\"CNGtwB_L\",\"docs_tech-stack.md\":\"xiWGQstL\",\"examples.md\":\"BqDfJd4G\",\"examples_pwa.md\":\"DREN7J2F\",\"examples_web.md\":\"DUhZ0IQL\",\"faq.md\":\"DtfXaXUI\",\"guides.md\":\"BVdQyeU-\",\"guides_guide-auth.md\":\"DNFuRudp\",\"guides_guide-file-upload.md\":\"DRbRLk7h\",\"guides_guide-service-worker.md\":\"B0wEVcQw\",\"guides_tutorial-1-todo.md\":\"D9ket3Re\",\"index.md\":\"DB-CsGEX\",\"recipes_realtime.md\":\"CX1Vs2FD\",\"recipes_streaming.md\":\"C7GFShgF\",\"reference_cli.md\":\"DERqaQJm\",\"reference_config.md\":\"DI_yG-7N\",\"reference_tools.md\":\"DZxjdVFX\"}");window.__VP_SITE_DATA__=JSON.parse("{\"lang\":\"en-US\",\"dir\":\"ltr\",\"title\":\"Webflo\",\"description\":\"A universal, standards-first web framework for building web-native apps.\",\"base\":\"/\",\"head\":[],\"router\":{\"prefetchLinks\":true},\"appearance\":\"force-dark\",\"themeConfig\":{\"logo\":false,\"siteTitle\":\"Webflo\",\"socialLinks\":[{\"icon\":\"github\",\"link\":\"https://github.com/webqit/webflo\"}],\"nav\":[{\"text\":\"Docs\",\"link\":\"/docs\",\"activeMatch\":\"/docs\"},{\"text\":\"API\",\"link\":\"/api\",\"activeMatch\":\"/api/webflo-routing/handler\"},{\"text\":\"Guides\",\"link\":\"/guides\",\"activeMatch\":\"/guides\"},{\"text\":\"Examples\",\"link\":\"/examples\",\"activeMatch\":\"/examples\"}],\"sidebar\":{\"/\":[{\"text\":\"Getting Started\",\"items\":[{\"text\":\"Welcome\",\"link\":\"/docs\"},{\"text\":\"Quickstart\",\"link\":\"/docs/getting-started\"}]},{\"text\":\"Concepts\",\"items\":[{\"text\":\"Concepts Overview\",\"link\":\"/docs/concepts\"},{\"text\":\"Webflo Routing\",\"link\":\"/docs/concepts/routing\"},{\"text\":\"Rendering\",\"link\":\"/docs/concepts/rendering\"},{\"text\":\"Templates\",\"link\":\"/docs/concepts/templates\"},{\"text\":\"State Management\",\"link\":\"/docs/concepts/state\"},{\"text\":\"Request/Response Lifecycle\",\"link\":\"/docs/concepts/request-response\"},{\"text\":\"Webflo Realtime\",\"link\":\"/docs/concepts/realtime\"}]},{\"text\":\"Advanced\",\"items\":[{\"text\":\"Advanced Overview\",\"link\":\"/docs/advanced\"},{\"text\":\"Redirects\",\"link\":\"/docs/advanced/redirects\"}]},{\"text\":\"API Reference\",\"items\":[{\"text\":\"Webflo Routing\",\"collapsed\":true,\"items\":[{\"text\":\"Handler\",\"link\":\"/api/webflo-routing/handler\"},{\"text\":\"HttpEvent\",\"link\":\"/api/webflo-routing/HttpEvent\"},{\"text\":\"next\",\"link\":\"/api/webflo-routing/handler/next\"},{\"text\":\"fetch\",\"link\":\"/api/webflo-routing/handler/fetch\"}]},{\"text\":\"Webflo Fetch\",\"collapsed\":true,\"items\":[{\"text\":\"fetch\",\"link\":\"/api/webflo-fetch/fetch\"},{\"text\":\"Request\",\"link\":\"/api/webflo-fetch/Request\"},{\"text\":\"Response\",\"link\":\"/api/webflo-fetch/Response\"},{\"text\":\"LiveResponse\",\"link\":\"/api/webflo-fetch/LiveResponse\"},{\"text\":\"FormData\",\"link\":\"/api/webflo-fetch/FormData\"},{\"text\":\"Headers\",\"link\":\"/api/webflo-fetch/Headers\"}]},{\"text\":\"Webflo Messaging\",\"collapsed\":true,\"items\":[{\"text\":\"MessageChannel\",\"link\":\"/api/webflo-messaging/MessageChannel\"},{\"text\":\"MessagePort\",\"link\":\"/api/webflo-messaging/MessagePort\"},{\"text\":\"MessageEvent\",\"link\":\"/api/webflo-messaging/MessageEvent\"}]}]},{\"text\":\"Guides & Recipes\",\"items\":[{\"text\":\"Tutorial: Todo App\",\"link\":\"/guides/tutorial-1-todo\"},{\"text\":\"Auth\",\"link\":\"/guides/guide-auth\"},{\"text\":\"File Upload\",\"link\":\"/guides/guide-file-upload\"},{\"text\":\"Service Worker\",\"link\":\"/guides/guide-service-worker\"},{\"text\":\"Streaming\",\"link\":\"/recipes/streaming\"},{\"text\":\"Realtime Patterns\",\"link\":\"/recipes/realtime\"}]},{\"text\":\"Examples\",\"items\":[{\"text\":\"Web Example\",\"link\":\"/examples/web\"},{\"text\":\"PWA Example\",\"link\":\"/examples/pwa\"}]},{\"text\":\"Reference\",\"items\":[{\"text\":\"CLI\",\"link\":\"/reference/cli\"},{\"text\":\"Config\",\"link\":\"/reference/config\"},{\"text\":\"Tools\",\"link\":\"/reference/tools\"},{\"text\":\"FAQ\",\"link\":\"/faq\"},{\"text\":\"Contributing\",\"link\":\"/contributing\"}]}]},\"footer\":{\"message\":\"MIT Licensed\",\"copyright\":\"© webqit\"},\"search\":{\"provider\":\"local\"}},\"locales\":{},\"scrollOffset\":134,\"cleanUrls\":true}");</script>
|
|
677
|
+
|
|
678
|
+
</body>
|
|
679
|
+
</html>
|