@webqit/webflo 0.11.61 → 0.20.2-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/.github/FUNDING.yml +12 -0
  2. package/.github/workflows/publish.yml +48 -0
  3. package/.gitignore +2 -0
  4. package/LICENSE +2 -2
  5. package/README.md +71 -2050
  6. package/package.json +28 -13
  7. package/site/-/_.md +139 -0
  8. package/site/-/docs.old.md +2010 -0
  9. package/site/.vitepress/cache/deps/@braintree_sanitize-url 2.js +93 -0
  10. package/site/.vitepress/cache/deps/@braintree_sanitize-url.js +93 -0
  11. package/site/.vitepress/cache/deps/@braintree_sanitize-url.js 2.map +7 -0
  12. package/site/.vitepress/cache/deps/@braintree_sanitize-url.js.map +7 -0
  13. package/site/.vitepress/cache/deps/_metadata 2.json +85 -0
  14. package/site/.vitepress/cache/deps/_metadata.json +85 -0
  15. package/site/.vitepress/cache/deps/chunk-BUSYA2B4 2.js +9 -0
  16. package/site/.vitepress/cache/deps/chunk-BUSYA2B4.js +9 -0
  17. package/site/.vitepress/cache/deps/chunk-BUSYA2B4.js 2.map +7 -0
  18. package/site/.vitepress/cache/deps/chunk-BUSYA2B4.js.map +7 -0
  19. package/site/.vitepress/cache/deps/chunk-Q2AYPHVK 2.js +9719 -0
  20. package/site/.vitepress/cache/deps/chunk-Q2AYPHVK.js +9719 -0
  21. package/site/.vitepress/cache/deps/chunk-Q2AYPHVK.js 2.map +7 -0
  22. package/site/.vitepress/cache/deps/chunk-Q2AYPHVK.js.map +7 -0
  23. package/site/.vitepress/cache/deps/chunk-QAXAIFA7 2.js +12705 -0
  24. package/site/.vitepress/cache/deps/chunk-QAXAIFA7.js +12705 -0
  25. package/site/.vitepress/cache/deps/chunk-QAXAIFA7.js 2.map +7 -0
  26. package/site/.vitepress/cache/deps/chunk-QAXAIFA7.js.map +7 -0
  27. package/site/.vitepress/cache/deps/cytoscape 2.js +30278 -0
  28. package/site/.vitepress/cache/deps/cytoscape-cose-bilkent 2.js +4710 -0
  29. package/site/.vitepress/cache/deps/cytoscape-cose-bilkent.js +4710 -0
  30. package/site/.vitepress/cache/deps/cytoscape-cose-bilkent.js 2.map +7 -0
  31. package/site/.vitepress/cache/deps/cytoscape-cose-bilkent.js.map +7 -0
  32. package/site/.vitepress/cache/deps/cytoscape.js +30278 -0
  33. package/site/.vitepress/cache/deps/cytoscape.js 2.map +7 -0
  34. package/site/.vitepress/cache/deps/cytoscape.js.map +7 -0
  35. package/site/.vitepress/cache/deps/dayjs 2.js +285 -0
  36. package/site/.vitepress/cache/deps/dayjs.js +285 -0
  37. package/site/.vitepress/cache/deps/dayjs.js 2.map +7 -0
  38. package/site/.vitepress/cache/deps/dayjs.js.map +7 -0
  39. package/site/.vitepress/cache/deps/debug 2.js +453 -0
  40. package/site/.vitepress/cache/deps/debug.js +453 -0
  41. package/site/.vitepress/cache/deps/debug.js 2.map +7 -0
  42. package/site/.vitepress/cache/deps/debug.js.map +7 -0
  43. package/site/.vitepress/cache/deps/package 2.json +3 -0
  44. package/site/.vitepress/cache/deps/package.json +3 -0
  45. package/site/.vitepress/cache/deps/vitepress___@vue_devtools-api 2.js +4507 -0
  46. package/site/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +4507 -0
  47. package/site/.vitepress/cache/deps/vitepress___@vue_devtools-api.js 2.map +7 -0
  48. package/site/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +7 -0
  49. package/site/.vitepress/cache/deps/vitepress___@vueuse_core 2.js +584 -0
  50. package/site/.vitepress/cache/deps/vitepress___@vueuse_core.js +584 -0
  51. package/site/.vitepress/cache/deps/vitepress___@vueuse_core.js 2.map +7 -0
  52. package/site/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +7 -0
  53. package/site/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap 2.js +1166 -0
  54. package/site/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js +1166 -0
  55. package/site/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js 2.map +7 -0
  56. package/site/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js.map +7 -0
  57. package/site/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js 2.js +1667 -0
  58. package/site/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js +1667 -0
  59. package/site/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js 2.map +7 -0
  60. package/site/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js.map +7 -0
  61. package/site/.vitepress/cache/deps/vitepress___minisearch 2.js +1815 -0
  62. package/site/.vitepress/cache/deps/vitepress___minisearch.js +1815 -0
  63. package/site/.vitepress/cache/deps/vitepress___minisearch.js 2.map +7 -0
  64. package/site/.vitepress/cache/deps/vitepress___minisearch.js.map +7 -0
  65. package/site/.vitepress/cache/deps/vue 2.js +344 -0
  66. package/site/.vitepress/cache/deps/vue.js +344 -0
  67. package/site/.vitepress/cache/deps/vue.js 2.map +7 -0
  68. package/site/.vitepress/cache/deps/vue.js.map +7 -0
  69. package/site/.vitepress/config.ts +147 -0
  70. package/site/.vitepress/theme/custom.css +50 -0
  71. package/site/.vitepress/theme/index.ts +6 -0
  72. package/site/api/webflo-fetch/FormData.md +0 -0
  73. package/site/api/webflo-fetch/Headers.md +0 -0
  74. package/site/api/webflo-fetch/LiveResponse.md +0 -0
  75. package/site/api/webflo-fetch/Request.md +0 -0
  76. package/site/api/webflo-fetch/Response.md +0 -0
  77. package/site/api/webflo-fetch/fetch.md +0 -0
  78. package/site/api/webflo-routing/HttpCookies.md +0 -0
  79. package/site/api/webflo-routing/HttpEvent/respondWith.md +1 -0
  80. package/site/api/webflo-routing/HttpEvent/waitUntil.md +1 -0
  81. package/site/api/webflo-routing/HttpEvent/waitUntilNavigate.md +1 -0
  82. package/site/api/webflo-routing/HttpEvent.md +30 -0
  83. package/site/api/webflo-routing/HttpSession.md +0 -0
  84. package/site/api/webflo-routing/HttpState.md +0 -0
  85. package/site/api/webflo-routing/HttpUser.md +0 -0
  86. package/site/api/webflo-routing/handler/fetch.md +42 -0
  87. package/site/api/webflo-routing/handler/next.md +54 -0
  88. package/site/api/webflo-routing/handler.md +119 -0
  89. package/site/api.md +26 -0
  90. package/site/contributing.md +16 -0
  91. package/site/docs/advanced/lifecycles.md +20 -0
  92. package/site/docs/advanced/redirects.md +0 -0
  93. package/site/docs/advanced/routing.md +1 -0
  94. package/site/docs/advanced.md +9 -0
  95. package/site/docs/concepts/realtime.md +637 -0
  96. package/site/docs/concepts/rendering.md +60 -0
  97. package/site/docs/concepts/request-response.md +47 -0
  98. package/site/docs/concepts/routing.md +656 -0
  99. package/site/docs/concepts/state.md +44 -0
  100. package/site/docs/concepts/templates.md +48 -0
  101. package/site/docs/concepts.md +97 -0
  102. package/site/docs/getting-started.md +378 -0
  103. package/site/docs/tech-stack.md +56 -0
  104. package/site/docs.md +100 -0
  105. package/site/examples/pwa.md +10 -0
  106. package/site/examples/web.md +11 -0
  107. package/site/examples.md +10 -0
  108. package/site/faq.md +13 -0
  109. package/site/guides/guide-auth.md +13 -0
  110. package/site/guides/guide-file-upload.md +11 -0
  111. package/site/guides/guide-service-worker.md +10 -0
  112. package/site/guides/tutorial-1-todo.md +24 -0
  113. package/site/guides.md +15 -0
  114. package/site/index.md +39 -0
  115. package/site/public/img/brand/logo-670x670.png +0 -0
  116. package/site/recipes/realtime.md +11 -0
  117. package/site/recipes/streaming.md +15 -0
  118. package/site/reference/cli.md +11 -0
  119. package/site/reference/config.md +13 -0
  120. package/site/reference/tools.md +9 -0
  121. package/src/Context.js +3 -11
  122. package/src/config-pi/deployment/Env.js +6 -19
  123. package/src/config-pi/deployment/Layout.js +11 -3
  124. package/src/config-pi/runtime/Client.js +40 -48
  125. package/src/config-pi/runtime/Server.js +52 -20
  126. package/src/config-pi/runtime/client/Worker.js +22 -20
  127. package/src/config-pi/static/Init.js +57 -0
  128. package/src/config-pi/static/index.js +2 -0
  129. package/src/deployment-pi/origins/index.js +1 -1
  130. package/src/deployment-pi/util.js +161 -0
  131. package/src/index.js +3 -9
  132. package/src/init-pi/index.js +117 -0
  133. package/src/init-pi/templates/pwa/app/handler.server.js +8 -0
  134. package/src/init-pi/templates/pwa/app/page.html +7 -0
  135. package/src/init-pi/templates/pwa/package.json +19 -0
  136. package/src/init-pi/templates/pwa/public/assets/app.css +16 -0
  137. package/src/init-pi/templates/pwa/public/index.html +39 -0
  138. package/src/init-pi/templates/pwa/public/manifest.json +29 -0
  139. package/src/init-pi/templates/web/app/handler.server.js +8 -0
  140. package/src/init-pi/templates/web/app/page.html +7 -0
  141. package/src/init-pi/templates/web/package.json +19 -0
  142. package/src/init-pi/templates/web/public/assets/app.css +16 -0
  143. package/src/init-pi/templates/web/public/index.html +39 -0
  144. package/src/runtime-pi/WebfloRuntime.js +350 -0
  145. package/src/runtime-pi/index.js +3 -10
  146. package/src/runtime-pi/webflo-client/ClientSideCookies.js +17 -0
  147. package/src/runtime-pi/webflo-client/ClientSideWorkport.js +63 -0
  148. package/src/runtime-pi/webflo-client/DeviceCapabilities.js +213 -0
  149. package/src/runtime-pi/webflo-client/WebfloClient.js +500 -0
  150. package/src/runtime-pi/webflo-client/WebfloRootClient1.js +206 -0
  151. package/src/runtime-pi/webflo-client/WebfloRootClient2.js +113 -0
  152. package/src/runtime-pi/webflo-client/WebfloSubClient.js +118 -0
  153. package/src/runtime-pi/webflo-client/index.js +17 -0
  154. package/src/runtime-pi/webflo-client/webflo-codegen.js +469 -0
  155. package/src/runtime-pi/webflo-client/webflo-devmode.js +243 -0
  156. package/src/runtime-pi/webflo-client/webflo-embedded.js +50 -0
  157. package/src/runtime-pi/webflo-fetch/LiveResponse.js +437 -0
  158. package/src/runtime-pi/webflo-fetch/cookies.js +10 -0
  159. package/src/runtime-pi/webflo-fetch/fetch.js +16 -0
  160. package/src/runtime-pi/webflo-fetch/formdata.js +54 -0
  161. package/src/runtime-pi/webflo-fetch/headers.js +151 -0
  162. package/src/runtime-pi/webflo-fetch/index.js +5 -0
  163. package/src/runtime-pi/webflo-fetch/message.js +49 -0
  164. package/src/runtime-pi/webflo-fetch/request.js +62 -0
  165. package/src/runtime-pi/webflo-fetch/response.js +110 -0
  166. package/src/runtime-pi/webflo-fetch/util.js +28 -0
  167. package/src/runtime-pi/webflo-messaging/WQBroadcastChannel.js +10 -0
  168. package/src/runtime-pi/webflo-messaging/WQMessageChannel.js +26 -0
  169. package/src/runtime-pi/webflo-messaging/WQMessageEvent.js +87 -0
  170. package/src/runtime-pi/webflo-messaging/WQMessagePort.js +38 -0
  171. package/src/runtime-pi/webflo-messaging/WQRelayPort.js +47 -0
  172. package/src/runtime-pi/webflo-messaging/WQSockPort.js +113 -0
  173. package/src/runtime-pi/webflo-messaging/WQStarPort.js +104 -0
  174. package/src/runtime-pi/webflo-messaging/wq-message-port.js +404 -0
  175. package/src/runtime-pi/webflo-routing/HttpCookies.js +42 -0
  176. package/src/runtime-pi/webflo-routing/HttpEvent.js +112 -0
  177. package/src/runtime-pi/webflo-routing/HttpSession.js +11 -0
  178. package/src/runtime-pi/webflo-routing/HttpState.js +153 -0
  179. package/src/runtime-pi/webflo-routing/HttpUser.js +54 -0
  180. package/src/runtime-pi/webflo-routing/WebfloRouter.js +245 -0
  181. package/src/runtime-pi/webflo-server/ServerSideCookies.js +19 -0
  182. package/src/runtime-pi/webflo-server/ServerSideSession.js +38 -0
  183. package/src/runtime-pi/webflo-server/WebfloServer.js +937 -0
  184. package/src/runtime-pi/webflo-server/index.js +11 -0
  185. package/src/runtime-pi/webflo-server/messaging/Client.js +27 -0
  186. package/src/runtime-pi/webflo-server/messaging/ClientRequestRealtime.js +50 -0
  187. package/src/runtime-pi/webflo-server/messaging/Clients.js +25 -0
  188. package/src/runtime-pi/webflo-server/webflo-devmode.js +326 -0
  189. package/src/runtime-pi/{client → webflo-url}/Url.js +27 -76
  190. package/src/runtime-pi/webflo-url/index.js +1 -0
  191. package/src/runtime-pi/webflo-url/urlpattern.js +38 -0
  192. package/src/runtime-pi/{util-url.js → webflo-url/util.js} +5 -43
  193. package/src/runtime-pi/webflo-url/xURL.js +94 -0
  194. package/src/runtime-pi/webflo-worker/WebfloWorker.js +234 -0
  195. package/src/runtime-pi/webflo-worker/WorkerSideCookies.js +19 -0
  196. package/src/runtime-pi/webflo-worker/WorkerSideWorkport.js +18 -0
  197. package/src/runtime-pi/webflo-worker/index.js +11 -0
  198. package/src/services-pi/index.js +2 -0
  199. package/src/services-pi/push/index.js +23 -0
  200. package/src/util.js +10 -0
  201. package/src/{webflo.js → webflo-cli.js} +4 -4
  202. package/src/runtime-pi/Application.js +0 -29
  203. package/src/runtime-pi/Cookies.js +0 -82
  204. package/src/runtime-pi/HttpEvent.js +0 -107
  205. package/src/runtime-pi/Router.js +0 -130
  206. package/src/runtime-pi/Runtime.js +0 -21
  207. package/src/runtime-pi/client/Application.js +0 -76
  208. package/src/runtime-pi/client/Context.js +0 -7
  209. package/src/runtime-pi/client/Router.js +0 -48
  210. package/src/runtime-pi/client/Runtime.js +0 -525
  211. package/src/runtime-pi/client/Workport.js +0 -190
  212. package/src/runtime-pi/client/createStorage.js +0 -58
  213. package/src/runtime-pi/client/generate.js +0 -481
  214. package/src/runtime-pi/client/index.js +0 -21
  215. package/src/runtime-pi/client/worker/Application.js +0 -44
  216. package/src/runtime-pi/client/worker/Context.js +0 -7
  217. package/src/runtime-pi/client/worker/Runtime.js +0 -275
  218. package/src/runtime-pi/client/worker/Workport.js +0 -78
  219. package/src/runtime-pi/client/worker/index.js +0 -21
  220. package/src/runtime-pi/server/Application.js +0 -101
  221. package/src/runtime-pi/server/Context.js +0 -16
  222. package/src/runtime-pi/server/Router.js +0 -159
  223. package/src/runtime-pi/server/Runtime.js +0 -558
  224. package/src/runtime-pi/server/index.js +0 -21
  225. package/src/runtime-pi/util-http.js +0 -86
  226. package/src/runtime-pi/xFormData.js +0 -24
  227. package/src/runtime-pi/xHeaders.js +0 -146
  228. package/src/runtime-pi/xRequest.js +0 -46
  229. package/src/runtime-pi/xRequestHeaders.js +0 -109
  230. package/src/runtime-pi/xResponse.js +0 -33
  231. package/src/runtime-pi/xResponseHeaders.js +0 -117
  232. package/src/runtime-pi/xURL.js +0 -105
  233. package/src/runtime-pi/xfetch.js +0 -23
  234. package/src/runtime-pi/xxHttpMessage.js +0 -102
  235. package/src/static-pi/index.js +0 -11
@@ -20,16 +20,22 @@ export default class Server extends Dotfile {
20
20
  withDefaults(config) {
21
21
  return this.merge({
22
22
  port: process.env.PORT || 3000,
23
- domains: [],
23
+ hostnames: [],
24
24
  https: {
25
25
  port: process.env.SSL_PORT || 0,
26
- domains: [],
26
+ hostnames: [],
27
27
  keyfile: '/etc/letsencrypt/live/[domain]/privkey.pem',
28
28
  certfile: '/etc/letsencrypt/live/[domain]/fullchain.pem',
29
29
  force: false,
30
30
  },
31
31
  force_www: '',
32
- oohtml_support: 'full',
32
+ capabilities: {
33
+ database: false,
34
+ database_dialect: 'postgres',
35
+ redis: false,
36
+ webpush: false,
37
+ vapid_subject: 'mailto:foo@example.com',
38
+ },
33
39
  }, config, 'patch');
34
40
  }
35
41
 
@@ -42,13 +48,6 @@ export default class Server extends Dotfile {
42
48
  {value: 'add',},
43
49
  {value: 'remove',},
44
50
  ],
45
- oohtml_support: [
46
- {value: 'full', title: 'full'},
47
- {value: 'namespacing', title: 'namespacing'},
48
- {value: 'scripting', title: 'scripting'},
49
- {value: 'templating', title: 'templating'},
50
- {value: 'none', title: 'none'},
51
- ],
52
51
  }, choices, 'patch');
53
52
  // Questions
54
53
  return [
@@ -60,9 +59,10 @@ export default class Server extends Dotfile {
60
59
  validation: ['important'],
61
60
  },
62
61
  {
63
- name: 'domains',
62
+ name: 'hostnames',
64
63
  type: 'list',
65
- message: '[domains]: Enter a list of allowed domains if necessary (comma-separated)',
64
+ message: '[hostnames]: Enter a list of allowed hostnames if necessary (comma-separated)',
65
+ initial: (config.hostnames || []).join(', '),
66
66
  validation: ['important'],
67
67
  },
68
68
  {
@@ -98,9 +98,9 @@ export default class Server extends Dotfile {
98
98
  validation: ['important'],
99
99
  },
100
100
  {
101
- name: 'domains',
101
+ name: 'hostnames',
102
102
  type: 'list',
103
- message: '[domains]: Enter the CERT domains (comma-separated)',
103
+ message: '[hostnames]: Enter the CERT hostnames (comma-separated)',
104
104
  validation: ['important'],
105
105
  },
106
106
  {
@@ -113,12 +113,44 @@ export default class Server extends Dotfile {
113
113
  ],
114
114
  },
115
115
  {
116
- name: 'oohtml_support',
117
- type: 'select',
118
- message: '[oohtml_support]: Specify OOHTML support level',
119
- choices: CHOICES.oohtml_support,
120
- initial: this.indexOfInitial(CHOICES.oohtml_support, config.oohtml_support),
121
- validation: ['important'],
116
+ name: 'capabilities',
117
+ controls: {
118
+ name: 'capabilities',
119
+ },
120
+ initial: config.capabilities,
121
+ schema: [
122
+ {
123
+ name: 'database',
124
+ type: 'toggle',
125
+ message: 'Add database integration?',
126
+ active: 'YES',
127
+ inactive: 'NO',
128
+ },
129
+ {
130
+ name: 'database_dialect',
131
+ type: (prev, answers) => !answers.database ? null : 'text',
132
+ message: 'Enter the database dialect (postgres for now)',
133
+ },
134
+ {
135
+ name: 'redis',
136
+ type: 'toggle',
137
+ message: 'Add redis integration?',
138
+ active: 'YES',
139
+ inactive: 'NO',
140
+ },
141
+ {
142
+ name: 'webpush',
143
+ type: 'toggle',
144
+ message: 'Add webpush integration?',
145
+ active: 'YES',
146
+ inactive: 'NO',
147
+ },
148
+ {
149
+ name: 'vapid_subject',
150
+ type: (prev, answers) => !answers.webpush ? null : 'text',
151
+ message: 'Enter the vapid_subject URL',
152
+ }
153
+ ]
122
154
  },
123
155
  ];
124
156
  }
@@ -21,14 +21,15 @@ export default class Worker extends Dotfile {
21
21
  // Defaults merger
22
22
  withDefaults(config) {
23
23
  return this.merge({
24
+ filename: 'app.worker.js',
25
+ scope: '/',
26
+ skip_waiting: true,
24
27
  cache_name: 'cache_v0',
25
28
  default_fetching_strategy: 'network-first',
26
29
  network_first_urls: [],
27
30
  cache_first_urls: [],
28
31
  network_only_urls: [],
29
32
  cache_only_urls: [],
30
- skip_waiting: true,
31
- bundle_public_env: false,
32
33
  }, config, 'patch');
33
34
  }
34
35
 
@@ -49,6 +50,24 @@ export default class Worker extends Dotfile {
49
50
  }, choices, 'patch');
50
51
  // Questions
51
52
  return [
53
+ {
54
+ name: 'filename',
55
+ type: 'text',
56
+ message: 'Specify the Service Worker filename',
57
+ },
58
+ {
59
+ name: 'scope',
60
+ type: 'text',
61
+ message: 'Specify the Service Worker scope',
62
+ },
63
+ {
64
+ name: 'skip_waiting',
65
+ type: 'toggle',
66
+ message: 'Choose whether to skip the "waiting" state for updated Service Workers',
67
+ active: 'YES',
68
+ inactive: 'NO',
69
+ initial: config.skip_waiting,
70
+ },
52
71
  {
53
72
  name: 'cache_name',
54
73
  type: 'text',
@@ -86,24 +105,7 @@ export default class Worker extends Dotfile {
86
105
  type: (prev, answers) => answers.default_fetching_strategy === 'cache-only' ? null : 'list',
87
106
  message: 'Specify URLs for a "cache-only" fetching strategy (comma-separated, globe supported)',
88
107
  initial: (config.cache_only_urls || []).join(', '),
89
- },
90
- {
91
- name: 'skip_waiting',
92
- type: 'toggle',
93
- message: 'Choose whether to skip the "waiting" state for updated Service Workers',
94
- active: 'YES',
95
- inactive: 'NO',
96
- initial: config.skip_waiting,
97
- },
98
- {
99
- name: 'bundle_public_env',
100
- type: 'toggle',
101
- message: '[bundle_public_env]: Bundle public ENV variables?',
102
- active: 'YES',
103
- inactive: 'NO',
104
- initial: config.bundle_public_env,
105
- validation: ['important'],
106
- },
108
+ }
107
109
  ];
108
110
  }
109
111
  }
@@ -0,0 +1,57 @@
1
+
2
+ /**
3
+ * imports
4
+ */
5
+ import { Dotfile } from '@webqit/backpack';
6
+
7
+ export default class Init extends Dotfile {
8
+
9
+ // Base name
10
+ get name() {
11
+ return 'init';
12
+ }
13
+
14
+ // @desc
15
+ static get ['@desc']() {
16
+ return 'Webflo starter app config.';
17
+ }
18
+
19
+ // Defaults merger
20
+ withDefaults(config) {
21
+ return this.merge({
22
+ template: 'web',
23
+ install: true,
24
+ git: true,
25
+ }, config, 'patch');
26
+ }
27
+
28
+ // Questions generator
29
+ getSchema(config, choices = {}) {
30
+ // Questions
31
+ return [
32
+ {
33
+ name: 'template',
34
+ type: 'text',
35
+ message: 'Enter the application template',
36
+ initial: config.template,
37
+ validation: ['important'],
38
+ },
39
+ {
40
+ name: 'install',
41
+ type: 'toggle',
42
+ message: 'Run "npm install" after initialization?',
43
+ active: 'YES',
44
+ inactive: 'NO',
45
+ initial: config.install,
46
+ },
47
+ {
48
+ name: 'git',
49
+ type: 'toggle',
50
+ message: 'Run "git init" after initialization?',
51
+ active: 'YES',
52
+ inactive: 'NO',
53
+ initial: config.git,
54
+ },
55
+ ];
56
+ }
57
+ }
@@ -2,6 +2,7 @@
2
2
  /**
3
3
  * @imports
4
4
  */
5
+ import Init from './Init.js';
5
6
  import Manifest from './Manifest.js';
6
7
  import Ssg from './Ssg.js';
7
8
 
@@ -9,6 +10,7 @@ import Ssg from './Ssg.js';
9
10
  * @exports
10
11
  */
11
12
  export {
13
+ Init,
12
14
  Manifest,
13
15
  Ssg,
14
16
  }
@@ -198,7 +198,7 @@ export async function webhook(httpEvent, router, next) {
198
198
  }, _isNumeric(deployParams.ondeploy_autoexit) ? parseInt(deployParams.ondeploy_autoexit) : 0);
199
199
  }
200
200
  resolve(
201
- new httpEvent.Response(`Deployment ${ exitCode === 0 ? 'success' : 'error: ' + exitCode }!`, { status: exitCode === 0 ? 200 : 500 })
201
+ new Response(`Deployment ${ exitCode === 0 ? 'success' : 'error: ' + exitCode }!`, { status: exitCode === 0 ? 200 : 500 })
202
202
  );
203
203
  if (cx.logger) {
204
204
  cx.logger.log('');
@@ -0,0 +1,161 @@
1
+ import Fs from 'fs';
2
+ import Path from 'path';
3
+
4
+ function parseHostnames(hostnames) {
5
+ return [].concat(hostnames).reduce((arr, str) => arr.concat(str.split(',')), []).map((str) => str.trim()).filter(str => str);
6
+ }
7
+
8
+ function selectHostnames(serverDefs, matchingPort = null) {
9
+ return serverDefs.reduce((hosts, def) => hosts.length ? hosts : (((!matchingPort || def.port === matchingPort) && parseHostnames(def.hostnames)) || []), []);
10
+ }
11
+
12
+ // config.runtime
13
+
14
+ export async function readInitConfig($context) {
15
+ if (!$context.config.static.Init) {
16
+ throw new Error(`The Client configurator "config.static.Init" is required in context.`);
17
+ }
18
+ return await (new $context.config.static.Init($context)).read();
19
+ }
20
+
21
+ export async function readClientConfig($context) {
22
+ if (!$context.config.runtime?.Client) {
23
+ throw new Error(`The Client configurator "config.runtime.Client" is required in context.`);
24
+ }
25
+ return await (new $context.config.runtime.Client($context)).read();
26
+ }
27
+
28
+ export async function readWorkerConfig($context) {
29
+ if (!$context.config.runtime?.client.Worker) {
30
+ throw new Error(`The Client configurator "config.runtime.client.Worker" is required in context.`);
31
+ }
32
+ return await (new $context.config.runtime.client.Worker($context)).read();
33
+ }
34
+
35
+ export async function readServerConfig($context) {
36
+ if (!$context.config.runtime?.Server) {
37
+ throw new Error(`The Client configurator "config.runtime.Server" is required in context.`);
38
+ }
39
+ const serverConfig = await (new $context.config.runtime.Server($context)).read();
40
+ serverConfig.hostnames = serverConfig.hostnames.length ? parseHostnames(serverConfig.hostnames) : ['*'];
41
+ if (serverConfig.https.port) {
42
+ serverConfig.https.hostnames = serverConfig.https.hostnames.length ? parseHostnames(serverConfig.https.hostnames) : ['*'];
43
+ }
44
+ return serverConfig;
45
+ }
46
+
47
+ export async function readHeadersConfig($context) {
48
+ if (!$context.config.runtime?.server.Headers) {
49
+ throw new Error(`The Client configurator "config.runtime.server.Headers" is required in context.`);
50
+ }
51
+ return await (new $context.config.runtime.server.Headers($context)).read();
52
+ }
53
+
54
+ export async function readRedirectsConfig($context) {
55
+ if (!$context.config.runtime?.server.Redirects) {
56
+ throw new Error(`The Client configurator "config.runtime.server.Redirects" is required in context.`);
57
+ }
58
+ return await (new $context.config.runtime.server.Redirects($context)).read();
59
+ }
60
+
61
+ // config.deployment
62
+
63
+ export async function readLayoutConfig($context) {
64
+ if (!$context.config.deployment?.Layout) {
65
+ throw new Error(`The Client configurator "config.deployment.Layout" is required in context.`);
66
+ }
67
+ const layoutConfig = await (new $context.config.deployment.Layout($context)).read();
68
+ return Object.fromEntries(['CLIENT_DIR', 'WORKER_DIR', 'SERVER_DIR', 'PUBLIC_DIR', 'VIEWS_DIR'].map((name) => {
69
+ return [name, Path.resolve($context.CWD || '', layoutConfig[name])];
70
+ }));
71
+ }
72
+
73
+ export async function readEnvConfig($context) {
74
+ if (!$context.config.deployment?.Env) {
75
+ throw new Error(`The Client configurator "config.deployment.Env" is required in context.`);
76
+ }
77
+ return await (new $context.config.deployment.Env($context)).read();
78
+ }
79
+
80
+ export async function readOriginsConfig($context) {
81
+ if (!$context.config.deployment?.Origins) {
82
+ throw new Error(`The Client configurator "config.deployment.Origins" is required in context.`);
83
+ }
84
+ return await (new $context.config.deployment.Origins($context)).read();
85
+ }
86
+
87
+ export async function readProxyConfig($context) {
88
+ if (!$context.config.deployment?.Proxy) {
89
+ throw new Error(`The Client configurator "config.deployment.Proxy" is required in context.`);
90
+ }
91
+ const proxyConfig = await (new $context.config.deployment.Proxy($context)).read();
92
+ const entries = await Promise.all(proxyConfig.entries.map(async ({ ...proxy }) => {
93
+ if (proxy.hostnames) {
94
+ proxy.hostnames = parseHostnames(proxy.hostnames);
95
+ }
96
+ if (proxy.path) {
97
+ const $$context = $context.constructor.create($context, Path.join($context.CWD, proxy.path));
98
+ proxy.SERVER = await (new $$context.config.runtime.Server($$context)).read();
99
+ if (!proxy.port) {
100
+ // Dynamically figure the local port on which this could be running
101
+ proxy.port = proxy.SERVER.https.port || proxy.SERVER.port;
102
+ }
103
+ if (!proxy.proto) {
104
+ // Dynamically figure the proto to forward the request with - to match running server
105
+ proxy.proto = proxy.port === proxy.SERVER.https.port ? 'https' : 'http';
106
+ }
107
+ if (!proxy.hostnames?.length) {
108
+ // Dynamically figure the hostnames to forward
109
+ proxy.hostnames = selectHostnames([proxy.SERVER.https, proxy.SERVER], proxy.port);
110
+ if (!proxy.hostnames.length) {
111
+ proxy.hostnames = selectHostnames([proxy.SERVER.https, proxy.SERVER]);
112
+ }
113
+ }
114
+ }
115
+ // Whether local or remote...
116
+ if (!proxy.hostnames) {
117
+ proxy.hostnames = ['*'];
118
+ }
119
+ return proxy;
120
+ }));
121
+ return { entries };
122
+ }
123
+
124
+ // -----------
125
+
126
+ export function scanRoots(DIR, rootFileName, offset = '') {
127
+ return Fs.readdirSync(Path.join(DIR, offset)).reduce((roots, f) => {
128
+ const resource = Path.join(DIR, offset, f);
129
+ if (Fs.statSync(resource).isDirectory()) {
130
+ const $offset = Path.join(offset, f);
131
+ if (Fs.existsSync(Path.join(resource, rootFileName))) {
132
+ return roots.concat($offset.replace(/\\/g, '/'));
133
+ }
134
+ return roots.concat(scanRoots(DIR, rootFileName, $offset));
135
+ }
136
+ return roots;
137
+ }, []);
138
+ }
139
+
140
+ export function scanRouteHandlers(LAYOUT, which, callback, offset, roots = []) {
141
+ const routingDir = LAYOUT[`${which.toUpperCase()}_DIR`];
142
+ const dir = Path.join(routingDir, offset);
143
+ const routeFileEnding = new RegExp(`(?:handler\\.${which}|handler)\\.js$`);
144
+ const dedicatedRouteFileName = `handler.${which}.js`;
145
+ const hasDedicatedRouteFile = Fs.existsSync(Path.join(dir, dedicatedRouteFileName));
146
+ try {
147
+ Fs.readdirSync(dir).forEach((f) => {
148
+ const resource = Path.join(dir, f);
149
+ const fstat = Fs.statSync(resource);
150
+ if (fstat.isDirectory()) {
151
+ const $offset = Path.join(offset, f).replace(/\\/g, '/');
152
+ if (roots.includes($offset)) return;
153
+ scanRouteHandlers(LAYOUT, which, callback, $offset, roots);
154
+ } else {
155
+ if (!routeFileEnding.test(f)
156
+ || (hasDedicatedRouteFile && f !== dedicatedRouteFileName)) return;
157
+ callback(resource, `/${offset}`, f, fstat);
158
+ }
159
+ });
160
+ } catch(e) {}
161
+ }
package/src/index.js CHANGED
@@ -1,20 +1,14 @@
1
-
2
- /**
3
- * @imports
4
- */
5
1
  import * as config from './config-pi/index.js';
6
2
  import * as deployment from './deployment-pi/index.js';
7
3
  import * as runtime from './runtime-pi/index.js';
8
4
  import * as services from './services-pi/index.js';
9
- import Context from './Context.js';
5
+ import * as starter from './init-pi/index.js';
10
6
 
11
- /**
12
- * @exports
13
- */
7
+ export { Context } from './Context.js';
14
8
  export {
15
9
  config,
16
10
  deployment,
17
11
  runtime,
18
12
  services,
19
- Context,
13
+ starter,
20
14
  }
@@ -0,0 +1,117 @@
1
+ import Fs from 'fs';
2
+ import Fs2 from 'fs/promises';
3
+ import Path from 'path';
4
+ import { exec } from 'child_process';
5
+ import { _toTitle } from '@webqit/util/str/index.js';
6
+ import { readInitConfig } from '../deployment-pi/util.js';
7
+ import { Context } from '../Context.js';
8
+
9
+ export const desc = {
10
+ init: 'Generate a preset Webflo starter app.',
11
+ };
12
+
13
+ export async function init(projectName = 'my-webflo-app', projectTitle = '', projectDescription = '') {
14
+ const $context = this;
15
+
16
+ if (!($context instanceof Context)) {
17
+ throw new Error(`The "this" context must be a Webflo Context object.`);
18
+ }
19
+
20
+ const { flags: FLAGS, logger: LOGGER } = $context;
21
+ const $config = {
22
+ INIT: await readInitConfig($context),
23
+ };
24
+
25
+ // Validate chosen tamplate
26
+ const template = FLAGS.template || $config.INIT.template;
27
+ const templateDir = Path.resolve(new URL(import.meta.url).pathname, '../templates', template);
28
+ if (!Fs.existsSync(templateDir)) {
29
+ LOGGER?.error(LOGGER.style.err(`Error: template "${template}" not found.`));
30
+ process.exit(1);
31
+ }
32
+
33
+ // Validate target dir
34
+ const targetDir = Path.resolve(process.cwd(), projectName);
35
+ if (Fs.existsSync(targetDir)) {
36
+ LOGGER?.error(LOGGER.style.err(`Error: directory ${projectName} already exists.`));
37
+ process.exit(1);
38
+ }
39
+
40
+ LOGGER?.log(LOGGER.style.keyword(`> `) + `Initializing your webflo app: "${projectName}" using template "${template}"...\n`);
41
+
42
+ // 1. Create project dir
43
+ await Fs2.mkdir(targetDir, { recursive: true });
44
+ // 2. Copy template files
45
+ await Fs2.cp(templateDir, targetDir, { recursive: true });
46
+
47
+ if (!projectTitle) {
48
+ projectTitle = _toTitle(projectName.replace(/-/g, ' '));
49
+ }
50
+ if (!projectDescription) {
51
+ projectDescription = `${projectTitle} - powered by Webflo`;
52
+ }
53
+
54
+ // 3. Customize package.json
55
+ const pkgFile = Path.join(targetDir, 'package.json');
56
+ if (Fs.existsSync(pkgFile)) {
57
+ const pkgData = JSON.parse(await Fs2.readFile(pkgFile, 'utf8'));
58
+ pkgData.name = projectName;
59
+ pkgData.title = projectTitle;
60
+ pkgData.description = projectDescription;
61
+ await Fs2.writeFile(pkgFile, JSON.stringify(pkgData, null, 2));
62
+ }
63
+
64
+ // 4. Customize index.html and manifest.json
65
+ for (const fileName of ['public/index.html', 'public/manifest.json']) {
66
+ const filePath = Path.join(targetDir, fileName);
67
+ if (Fs.existsSync(filePath)) {
68
+ let fileContents = await Fs2.readFile(filePath, 'utf8');
69
+ fileContents = fileContents.replace(/APP_NAME/g, projectName);
70
+ fileContents = fileContents.replace(/APP_TITLE/g, projectTitle);
71
+ fileContents = fileContents.replace(/APP_DESCRIPTION/g, projectDescription);
72
+ await Fs2.writeFile(filePath, fileContents);
73
+ }
74
+ }
75
+
76
+ // 5. Run setup commands
77
+ const comands = [];
78
+ const nextSteps = [];
79
+ if (FLAGS.install ?? $config.INIT.install) {
80
+ comands.push('npm install');
81
+ comands.push('npm run build');
82
+ } else {
83
+ nextSteps.push('npm install');
84
+ }
85
+ if (FLAGS.git ?? $config.INIT.git) {
86
+ comands.push('git init');
87
+ } else {
88
+ nextSteps.push('git init');
89
+ }
90
+ if (comands.length) {
91
+ LOGGER?.log(LOGGER.style.keyword(`> `) + `Running setup commands...`);
92
+ for (const cmd of comands) {
93
+ LOGGER?.log(LOGGER.style.token(`$ `) + cmd);
94
+ await new Promise((resolve, reject) => {
95
+ exec(cmd, { cwd: targetDir }, (error, stdout, stderr) => {
96
+ if (error) {
97
+ LOGGER?.log(LOGGER.style.err(stderr || error.message));
98
+ return reject(error);
99
+ }
100
+ resolve();
101
+ });
102
+ });
103
+ }
104
+ }
105
+
106
+ // 6. Done message
107
+ LOGGER?.log(`\n✔ Successfully created "${projectName}" using template "${template}"`);
108
+ LOGGER?.log(`\nNext steps:`);
109
+ LOGGER?.log(` cd ${projectName}`);
110
+ if (nextSteps.length) {
111
+ LOGGER?.log(` # Additional setup commands:`);
112
+ for (const step of nextSteps) {
113
+ LOGGER?.log(` ${step}`);
114
+ }
115
+ }
116
+ LOGGER?.log(` npm run dev -- --open\n`);
117
+ }
@@ -0,0 +1,8 @@
1
+ export default async function (event, next, fetch) {
2
+ if (next.stepname) return await next();
3
+ return {
4
+ title: 'Webflo PWA',
5
+ greeting: 'Hello World!',
6
+ menu: [{ title: 'Home', href: '/' }],
7
+ };
8
+ }
@@ -0,0 +1,7 @@
1
+ <ul render="#items: (item) of data?.menu / 'menu'">
2
+ <template def="menu" scoped="">
3
+ <li>
4
+ <a render="~href: item?.href; #text: item?.title;"></a>
5
+ </li>
6
+ </template>
7
+ </ul>
@@ -0,0 +1,19 @@
1
+ {
2
+ "title": "",
3
+ "name": "",
4
+ "description": "",
5
+ "type": "module",
6
+ "scripts": {
7
+ "build:html": "oohtml bundle --recursive --outdir=public/assets --auto-embed=app",
8
+ "build:js": "webflo generate::client --recursive --outdir=public/assets --auto-embed",
9
+ "build": "npm run build:html && npm run build:js",
10
+ "dev": "webflo start --dev --build-sensitivity=2",
11
+ "start": "webflo start"
12
+ },
13
+ "dependencies": {
14
+ "@webqit/webflo": "file:../../webflo"
15
+ },
16
+ "devDependencies": {
17
+ "@webqit/oohtml-cli": "latest"
18
+ }
19
+ }
@@ -0,0 +1,16 @@
1
+ body {
2
+ background-color: #4e40ce;
3
+ margin: 0;
4
+ color: white;
5
+ }
6
+ h1 {
7
+ background-color: black;
8
+ padding: 2rem;
9
+ }
10
+ h1 span:first-of-type {
11
+ color: crimson;
12
+ }
13
+ ul {
14
+ padding: 2rem;
15
+ padding-inline: 3rem;
16
+ }
@@ -0,0 +1,39 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en-US">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+ <title render="#text: titleBar || title">APP_TITLE</title>
8
+ <meta name="description" content="APP_DESCRIPTION">
9
+ <meta name="apple-mobile-web-app-title" content="APP_TITLE">
10
+ <meta name="color-scheme" content="dark">
11
+ <meta name="theme-color" content="#313131">
12
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
13
+ <meta name="apple-mobile-web-app-capable" content="yes">
14
+ <meta name="mobile-web-app-capable" content="yes">
15
+ <link rel="manifest" href="/manifest.json">
16
+ <!--
17
+ <link rel="icon" href="/assets/..." type="image/png">
18
+ <link rel="apple-touch-icon" href="/assets/...">
19
+ -->
20
+ <script crossorigin ssr src="https://unpkg.com/@webqit/oohtml@latest/dist/main.lite.js"></script>
21
+ <template def="app" ssr src="/assets/app.html" by="oohtml-cli"></template>
22
+ <script type="module" src="/assets/app.js" by="webflo"></script>
23
+ <link rel="stylesheet" href="/assets/app.css">
24
+ </head>
25
+
26
+ <body importscontext="/app">
27
+
28
+ <h1>
29
+ <span><?{ data.title }?></span><br>
30
+ <span><?{ data.greeting }?></span>
31
+ </h1>
32
+
33
+ <import ref="#page.html">
34
+ Import will show here...
35
+ </import>
36
+
37
+ </body>
38
+
39
+ </html>