@webqit/webflo 0.20.4-next.1 → 0.20.4-next.3
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/package.json +5 -20
- package/site/.vitepress/config.ts +1 -1
- package/site/docs/concepts/realtime.md +57 -53
- package/site/docs/concepts/{request-response.md → requests-responses.md} +1 -1
- package/site/docs/concepts/state.md +1 -1
- package/site/docs/getting-started.md +40 -40
- package/src/{Context.js → CLIContext.js} +9 -8
- package/src/build-pi/esbuild-plugin-livejs-transform.js +35 -0
- package/src/{runtime-pi/webflo-client/webflo-codegen.js → build-pi/index.js} +145 -141
- package/src/index.js +3 -1
- package/src/init-pi/index.js +6 -3
- package/src/init-pi/templates/pwa/package.json +2 -2
- package/src/init-pi/templates/web/package.json +2 -2
- package/src/runtime-pi/AppBootstrap.js +38 -0
- package/src/runtime-pi/WebfloRuntime.js +50 -47
- package/src/runtime-pi/apis.js +9 -0
- package/src/runtime-pi/index.js +2 -4
- package/src/runtime-pi/webflo-client/WebfloClient.js +31 -35
- package/src/runtime-pi/webflo-client/WebfloRootClient1.js +16 -14
- package/src/runtime-pi/webflo-client/WebfloSubClient.js +13 -13
- package/src/runtime-pi/webflo-client/bootstrap.js +37 -0
- package/src/runtime-pi/webflo-client/index.js +2 -8
- package/src/runtime-pi/webflo-client/webflo-devmode.js +3 -3
- package/src/runtime-pi/webflo-fetch/LiveResponse.js +127 -96
- package/src/runtime-pi/webflo-fetch/index.js +435 -5
- package/src/runtime-pi/webflo-routing/HttpCookies.js +1 -1
- package/src/runtime-pi/webflo-routing/HttpEvent.js +5 -6
- package/src/runtime-pi/webflo-routing/HttpUser.js +7 -7
- package/src/runtime-pi/webflo-server/ServerSideCookies.js +3 -1
- package/src/runtime-pi/webflo-server/ServerSideSession.js +2 -1
- package/src/runtime-pi/webflo-server/WebfloServer.js +98 -195
- package/src/runtime-pi/webflo-server/bootstrap.js +59 -0
- package/src/runtime-pi/webflo-server/index.js +2 -6
- package/src/runtime-pi/webflo-server/webflo-devmode.js +13 -24
- package/src/runtime-pi/webflo-worker/WebfloWorker.js +11 -15
- package/src/runtime-pi/webflo-worker/WorkerSideCookies.js +2 -1
- package/src/runtime-pi/webflo-worker/bootstrap.js +38 -0
- package/src/runtime-pi/webflo-worker/index.js +3 -7
- package/src/webflo-cli.js +1 -2
- package/src/runtime-pi/webflo-fetch/cookies.js +0 -10
- package/src/runtime-pi/webflo-fetch/fetch.js +0 -16
- package/src/runtime-pi/webflo-fetch/formdata.js +0 -54
- package/src/runtime-pi/webflo-fetch/headers.js +0 -151
- package/src/runtime-pi/webflo-fetch/message.js +0 -49
- package/src/runtime-pi/webflo-fetch/request.js +0 -62
- package/src/runtime-pi/webflo-fetch/response.js +0 -110
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"vanila-javascript"
|
|
13
13
|
],
|
|
14
14
|
"homepage": "https://webqit.io/tooling/webflo",
|
|
15
|
-
"version": "0.20.4-next.
|
|
15
|
+
"version": "0.20.4-next.3",
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
@@ -33,6 +33,9 @@
|
|
|
33
33
|
"site:build": "vitepress build site",
|
|
34
34
|
"site:preview": "vitepress preview site --port 4173"
|
|
35
35
|
},
|
|
36
|
+
"exports": {
|
|
37
|
+
"./apis": "./src/runtime-pi/apis.js"
|
|
38
|
+
},
|
|
36
39
|
"bin": {
|
|
37
40
|
"webflo": "src/webflo-cli.js",
|
|
38
41
|
"webflo-certbot-http-auth-hook": "src/services-pi/cert/http-auth-hook.js",
|
|
@@ -73,23 +76,5 @@
|
|
|
73
76
|
"maintainers": [
|
|
74
77
|
"Oxford Harrison <oxharris.dev@gmail.com>"
|
|
75
78
|
],
|
|
76
|
-
"contributors": []
|
|
77
|
-
"funding": {
|
|
78
|
-
"type": "patreon",
|
|
79
|
-
"url": "https://patreon.com/ox_harris"
|
|
80
|
-
},
|
|
81
|
-
"badges": {
|
|
82
|
-
"list": [
|
|
83
|
-
"npmversion",
|
|
84
|
-
"npmdownloads",
|
|
85
|
-
"patreon"
|
|
86
|
-
],
|
|
87
|
-
"config": {
|
|
88
|
-
"patreonUsername": "ox_harris",
|
|
89
|
-
"githubUsername": "webqit",
|
|
90
|
-
"githubRepository": "webflo",
|
|
91
|
-
"githubSlug": "webqit/webflo",
|
|
92
|
-
"npmPackageName": "@webqit/webflo"
|
|
93
|
-
}
|
|
94
|
-
}
|
|
79
|
+
"contributors": []
|
|
95
80
|
}
|
|
@@ -46,7 +46,7 @@ const config = {
|
|
|
46
46
|
{ text: 'Rendering', link: '/docs/concepts/rendering' },
|
|
47
47
|
{ text: 'Templates', link: '/docs/concepts/templates' },
|
|
48
48
|
{ text: 'State Management', link: '/docs/concepts/state' },
|
|
49
|
-
{ text: '
|
|
49
|
+
{ text: 'Requests & Responses', link: '/docs/concepts/requests-responses' },
|
|
50
50
|
{ text: 'Webflo Realtime', link: '/docs/concepts/realtime' },
|
|
51
51
|
]
|
|
52
52
|
},
|
|
@@ -20,17 +20,17 @@ event.waitUntilNavigate();
|
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
```js
|
|
23
|
-
event.client.addEventListener(
|
|
24
|
-
event.client.postMessage({ message:
|
|
23
|
+
event.client.addEventListener("message", handle);
|
|
24
|
+
event.client.postMessage({ message: "You seem to be typing something." });
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
```js
|
|
28
|
-
const result = await event.user.confirm({ message:
|
|
29
|
-
const result = await event.user.prompt({ message:
|
|
28
|
+
const result = await event.user.confirm({ message: "Should we proceed?" });
|
|
29
|
+
const result = await event.user.prompt({ message: "What is your name?" });
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
```js
|
|
33
|
-
const { channel, leave } = event.client.enterChannel(
|
|
33
|
+
const { channel, leave } = event.client.enterChannel("test-channel");
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
## The Traditional Setup
|
|
@@ -124,7 +124,7 @@ The **primary** realtime API here is `event.client` — a Webflo `MessagePort` s
|
|
|
124
124
|
|
|
125
125
|
```js
|
|
126
126
|
export default async function (event) {
|
|
127
|
-
event.client.postMessage({ hello:
|
|
127
|
+
event.client.postMessage({ hello: "world" });
|
|
128
128
|
}
|
|
129
129
|
```
|
|
130
130
|
|
|
@@ -138,10 +138,10 @@ These HTTP clients or handlers are the _Port B_.
|
|
|
138
138
|
The primary realtime API here is `liveResponse.background` — the same Webflo `MessagePort` interface as `event.client`, supporting things like: `.postMessage()`, `.addEventListener()`, etc.
|
|
139
139
|
|
|
140
140
|
```js
|
|
141
|
-
const response = await fetch(
|
|
142
|
-
const liveResponse = LiveResponse.from(response);
|
|
141
|
+
const response = await fetch("https://api.example.com/data");
|
|
142
|
+
const liveResponse = await LiveResponse.from(response);
|
|
143
143
|
|
|
144
|
-
liveResponse.background.addEventListener(
|
|
144
|
+
liveResponse.background.addEventListener("message", (message) => {
|
|
145
145
|
console.log(message);
|
|
146
146
|
});
|
|
147
147
|
```
|
|
@@ -172,7 +172,7 @@ The `app.background` port is also a _multi-port_ hub housing all _active_ backgr
|
|
|
172
172
|
Every interaction on this API is thus an interaction over all active realtime conversations.
|
|
173
173
|
|
|
174
174
|
```js
|
|
175
|
-
app.background.addEventListener(
|
|
175
|
+
app.background.addEventListener("message", (message) => {
|
|
176
176
|
console.log(message);
|
|
177
177
|
});
|
|
178
178
|
```
|
|
@@ -193,8 +193,8 @@ export default async function (event, next) {
|
|
|
193
193
|
if (next.stepname) return await next(); // The conventional delegation line
|
|
194
194
|
|
|
195
195
|
const response = await next();
|
|
196
|
-
const liveResponse = LiveResponse.from(response);
|
|
197
|
-
liveResponse.background.addEventListener(
|
|
196
|
+
const liveResponse = await LiveResponse.from(response);
|
|
197
|
+
liveResponse.background.addEventListener("message", (message) => {
|
|
198
198
|
console.log(message);
|
|
199
199
|
});
|
|
200
200
|
|
|
@@ -209,7 +209,7 @@ The handler’s own `event.client` port remains its own _Port A_ for communicati
|
|
|
209
209
|
---
|
|
210
210
|
|
|
211
211
|
::: tip
|
|
212
|
-
While not explicitly mentioned, external Webflo servers are just as accessible and interactive as the local server. A server-to-server interaction, for example, is just a matter of `LiveResponse.from(await fetch('https://api.example.com/data'))`.
|
|
212
|
+
While not explicitly mentioned, external Webflo servers are just as accessible and interactive as the local server. A server-to-server interaction, for example, is just a matter of `await LiveResponse.from(await fetch('https://api.example.com/data'))`.
|
|
213
213
|
:::
|
|
214
214
|
|
|
215
215
|
## Entering Background Mode
|
|
@@ -230,7 +230,7 @@ event.client.postMessage({ progress: 10 });
|
|
|
230
230
|
2. or achieving the same through higher-level APIs like `event.user.confirm()`:
|
|
231
231
|
|
|
232
232
|
```js
|
|
233
|
-
const result = await event.user.confirm({ message:
|
|
233
|
+
const result = await event.user.confirm({ message: "Are you sure?" });
|
|
234
234
|
```
|
|
235
235
|
|
|
236
236
|
### _Handler returns a response and explicitly marks it **not done**_
|
|
@@ -278,9 +278,9 @@ event.respondWith(
|
|
|
278
278
|
```js
|
|
279
279
|
event.respondWith(data, async ($data /* reactive copy of data */) => {
|
|
280
280
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
281
|
-
$data.someProp =
|
|
281
|
+
$data.someProp = "someValue";
|
|
282
282
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
283
|
-
$data.someProp =
|
|
283
|
+
$data.someProp = "someOtherValue";
|
|
284
284
|
});
|
|
285
285
|
```
|
|
286
286
|
|
|
@@ -294,7 +294,7 @@ A handler may return a `Generator` object by either:
|
|
|
294
294
|
export default async function* (event) {
|
|
295
295
|
yield data1;
|
|
296
296
|
yield new Response(data2);
|
|
297
|
-
yield new LiveResponse(null, { headers: { Location:
|
|
297
|
+
yield new LiveResponse(null, { headers: { Location: "/" } });
|
|
298
298
|
}
|
|
299
299
|
```
|
|
300
300
|
|
|
@@ -339,7 +339,9 @@ Webflo knows to switch the connection to background mode in all the above cases.
|
|
|
339
339
|
- **Webflo simply fulfills the intent of however a handler works**.
|
|
340
340
|
:::
|
|
341
341
|
|
|
342
|
-
## Real-
|
|
342
|
+
## Real-World Examples
|
|
343
|
+
|
|
344
|
+
Below are some examples of how Webflo's realtime features work in action.
|
|
343
345
|
|
|
344
346
|
### Live Responses
|
|
345
347
|
|
|
@@ -350,14 +352,14 @@ This handler returns a `LiveResponse` whose body starts as `{ step: 1 }` and is
|
|
|
350
352
|
**_Result_:** The page first renders step: 1, then updates to step: 2 after ~200ms.
|
|
351
353
|
|
|
352
354
|
```js
|
|
353
|
-
import { LiveResponse } from '@webqit/webflo';
|
|
355
|
+
import { LiveResponse } from '@webqit/webflo/apis';
|
|
354
356
|
```
|
|
355
357
|
|
|
356
358
|
```js
|
|
357
359
|
export async function GET(event, next) {
|
|
358
360
|
if (next.stepname) return await next(); // The conventional delegation line
|
|
359
361
|
|
|
360
|
-
const res = LiveResponse.from({ step: 1 }, { done: false });
|
|
362
|
+
const res = await LiveResponse.from({ step: 1 }, { done: false });
|
|
361
363
|
setTimeout(() => res.replaceWith({ step: 2 }), 200); // [!code highlight]
|
|
362
364
|
|
|
363
365
|
return res;
|
|
@@ -371,7 +373,7 @@ This handler returns a skeleton object immediately for fast page load and then b
|
|
|
371
373
|
**_Result_:** The UI renders a skeleton first, then progressively fills in as the object tree is built from the server.
|
|
372
374
|
|
|
373
375
|
```js
|
|
374
|
-
import { Observer } from '@webqit/
|
|
376
|
+
import { Observer } from '@webqit/webflo/apis';
|
|
375
377
|
```
|
|
376
378
|
|
|
377
379
|
```js
|
|
@@ -380,30 +382,30 @@ export async function GET(event, next) {
|
|
|
380
382
|
|
|
381
383
|
const data = {
|
|
382
384
|
menu: [
|
|
383
|
-
{ name:
|
|
384
|
-
{ name:
|
|
385
|
-
{ name:
|
|
385
|
+
{ name: "Home", href: "/" },
|
|
386
|
+
{ name: "About", href: "/about" },
|
|
387
|
+
{ name: "Contact", href: "/contact" },
|
|
386
388
|
],
|
|
387
389
|
content: {
|
|
388
|
-
header:
|
|
390
|
+
header: "Loading...",
|
|
389
391
|
body: [],
|
|
390
392
|
},
|
|
391
393
|
};
|
|
392
394
|
|
|
393
395
|
event.waitUntil(
|
|
394
396
|
new Promise(async (resolve) => {
|
|
395
|
-
const someData = await fetch(
|
|
396
|
-
Observer.set(data.content,
|
|
397
|
+
const someData = await fetch("https://api.example.com/data");
|
|
398
|
+
Observer.set(data.content, "header", someData.header);
|
|
397
399
|
|
|
398
400
|
Observer.proxy(data.content.body).push(
|
|
399
|
-
{ text:
|
|
400
|
-
{ text:
|
|
401
|
+
{ text: "Inventory 1:" + someData.items[0] },
|
|
402
|
+
{ text: "Inventory 2:" + someData.items[1] }
|
|
401
403
|
);
|
|
402
404
|
|
|
403
|
-
const someData2 = await fetch(
|
|
405
|
+
const someData2 = await fetch("https://api.example.com/data2");
|
|
404
406
|
Observer.proxy(data.content.body).push(
|
|
405
|
-
{ text:
|
|
406
|
-
{ text:
|
|
407
|
+
{ text: "Inventory 3:" + someData2.items[0] },
|
|
408
|
+
{ text: "Inventory 4:" + someData2.items[1] }
|
|
407
409
|
);
|
|
408
410
|
|
|
409
411
|
resolve();
|
|
@@ -448,12 +450,12 @@ export async function POST(event, next) {
|
|
|
448
450
|
// Initiate response
|
|
449
451
|
event.respondWith({ step: 1 }, { done: false });
|
|
450
452
|
// Make API call
|
|
451
|
-
await fetch(
|
|
453
|
+
await fetch("https://api.example.com/data");
|
|
452
454
|
// Update response
|
|
453
455
|
event.respondWith({ step: 2 }, { done: false });
|
|
454
456
|
|
|
455
457
|
// Redirect
|
|
456
|
-
event.respondWith(null, { status: 302, headers: { Location:
|
|
458
|
+
event.respondWith(null, { status: 302, headers: { Location: "/done" } }); // [!code highlight]
|
|
457
459
|
}
|
|
458
460
|
```
|
|
459
461
|
|
|
@@ -469,7 +471,7 @@ This handler pauses request processing to interact with the user before proceedi
|
|
|
469
471
|
export async function DELETE(event, next) {
|
|
470
472
|
if (next.stepname) return await next(); // The conventional delegation line
|
|
471
473
|
|
|
472
|
-
const message =
|
|
474
|
+
const message = "This item will be permanently deleted. Are you sure?";
|
|
473
475
|
const answer = await event.user.confirm({ message }); // [!code highlight]
|
|
474
476
|
if (answer?.data === true) {
|
|
475
477
|
// proceed
|
|
@@ -485,7 +487,7 @@ Customization is as simple as intercepting these via `preventDefault()` to rende
|
|
|
485
487
|
|
|
486
488
|
```javascript
|
|
487
489
|
// Intercept at window.webqit.app.background
|
|
488
|
-
window.webqit.app.background.addEventListener(
|
|
490
|
+
window.webqit.app.background.addEventListener("prompt", (e) => {
|
|
489
491
|
e.preventDefault(); // [!code highlight]
|
|
490
492
|
customDialog(e.data.message).then((result) => {
|
|
491
493
|
// Reply to the request on the provided port
|
|
@@ -507,17 +509,17 @@ export async function GET(event, next) {
|
|
|
507
509
|
if (next.stepname) return await next(); // The conventional delegation line
|
|
508
510
|
|
|
509
511
|
// Channel ID - must tally with the client-side ID
|
|
510
|
-
const channelID =
|
|
512
|
+
const channelID = "test-channel";
|
|
511
513
|
|
|
512
514
|
const { channel, leave } = event.client.enterChannel(channelID, {
|
|
513
515
|
// Optionally annotate user's messages with user's name
|
|
514
|
-
resolveData: (message) => ({ ...message, user:
|
|
516
|
+
resolveData: (message) => ({ ...message, user: "sample name" }),
|
|
515
517
|
});
|
|
516
518
|
|
|
517
519
|
event.waitUntilNavigate(); // keep open
|
|
518
|
-
event.client.addEventListener(
|
|
520
|
+
event.client.addEventListener("close", (e) => {
|
|
519
521
|
//e.preventDefault();
|
|
520
|
-
console.log(
|
|
522
|
+
console.log("Chat closed");
|
|
521
523
|
});
|
|
522
524
|
}
|
|
523
525
|
```
|
|
@@ -529,15 +531,15 @@ export default async function (event, next) {
|
|
|
529
531
|
if (next.stepname) return await next(); // The conventional delegation line
|
|
530
532
|
|
|
531
533
|
// Initialize states
|
|
532
|
-
const data = { messageTrail: [{ message:
|
|
534
|
+
const data = { messageTrail: [{ message: "Beginning of chat..." }] };
|
|
533
535
|
await event.respondWith(data, { done: false }); // [!code highlight]
|
|
534
536
|
|
|
535
537
|
// The messaging part
|
|
536
538
|
const response = await next(); // Call server
|
|
537
|
-
const chatPort = LiveResponse.from(response).background;
|
|
539
|
+
const chatPort = (await LiveResponse.from(response)).background;
|
|
538
540
|
|
|
539
541
|
// Channel ID - must tally with the server-side ID
|
|
540
|
-
const channelID =
|
|
542
|
+
const channelID = "test-channel";
|
|
541
543
|
|
|
542
544
|
// Listen to upstream messages - from others in the same channel
|
|
543
545
|
chatPort.addEventListener(`${channelID}:message`, (e) => {
|
|
@@ -604,17 +606,19 @@ export default async function (event, next) {
|
|
|
604
606
|
|
|
605
607
|
## Appendix A – The Realtime Lifecycle
|
|
606
608
|
|
|
609
|
+
The realtime lifecycle — from handshake to termination — can be summarized as follows. Note that this is the model between any two ports — Port A and Port B, not just between the client and the server, as that's only typical.
|
|
610
|
+
|
|
607
611
|
```mermaid
|
|
608
612
|
sequenceDiagram
|
|
609
|
-
participant
|
|
610
|
-
participant
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
Note over
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
613
|
+
participant Port A
|
|
614
|
+
participant Port B
|
|
615
|
+
Port A->>Port B: HTTP request
|
|
616
|
+
Port B-->>Port A: Initial Response (or a Temporary 202 Accepted) <br>+ X-Background-Messaging-Port
|
|
617
|
+
Port A-->>Port B: Connect (WebSocket / Broadcast / MessageChannel)
|
|
618
|
+
Note over Port A,Port B: Background mode established
|
|
619
|
+
Port B-->>Port A: Messages, updates, dialogs, etc.
|
|
620
|
+
Port A-->>Port B: Replies, requests, etc.
|
|
621
|
+
Port A<<-->>Port B: Termination
|
|
618
622
|
```
|
|
619
623
|
|
|
620
624
|
### The Handshake
|
|
@@ -622,7 +626,7 @@ sequenceDiagram
|
|
|
622
626
|
On entering background mode, Webflo initiates a handshake sequence as follows:
|
|
623
627
|
|
|
624
628
|
1. Webflo gives the initial response a unique `X-Background-Messaging-Port` header that tells the client to connect in the background after rendering the initial response.
|
|
625
|
-
> If the scenario is
|
|
629
|
+
> If the scenario is that the handler tiggers `event.client` messaging _before_ yielding a response, Webflo sends a temporary `202 Accepted` response to the client carrying this header.
|
|
626
630
|
2. The client reads that header and opens the background channel.
|
|
627
631
|
> If the client fails to connect within the handshake window, Webflo abandons the wait and concludes the request normally.
|
|
628
632
|
3. On connecting, both sides resume the same request context — now in live mode.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# State
|
|
1
|
+
# State, Mutation, & Reactivity
|
|
2
2
|
|
|
3
3
|
Webflo’s approach to state is refreshingly simple: your UI state is just a plain JavaScript object. When your server handler returns data, it becomes available as `document.bindings.data` for your templates and UI. No special syntax, no framework-specific magic—just JavaScript you already know.
|
|
4
4
|
|
|
@@ -22,23 +22,23 @@ This documentation is a work in progress. Please expect some rough edges, missin
|
|
|
22
22
|
|
|
23
23
|
## Installation
|
|
24
24
|
|
|
25
|
-
### Option 1:
|
|
25
|
+
### Option 1: Local Installation (Recommended)
|
|
26
26
|
|
|
27
|
-
Install Webflo
|
|
27
|
+
Install Webflo as a dependency in your project:
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
npm install
|
|
30
|
+
npm install @webqit/webflo
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
### Option 2:
|
|
33
|
+
### Option 2: Global Installation
|
|
34
34
|
|
|
35
|
-
Install Webflo
|
|
35
|
+
Install Webflo globally to use the CLI from anywhere:
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
npm install @webqit/webflo
|
|
38
|
+
npm install -g @webqit/webflo
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
The scope you choose will determine how you run Webflo commands. The Webflo commands in the rest of this page will show in both `
|
|
41
|
+
The scope you choose will determine how you run Webflo commands. The Webflo commands in the rest of this page will show in both `local` and `global` styles.
|
|
42
42
|
|
|
43
43
|
## Creating a New Project
|
|
44
44
|
|
|
@@ -46,14 +46,14 @@ Webflo provides a CLI command to scaffold new projects:
|
|
|
46
46
|
|
|
47
47
|
::: code-group
|
|
48
48
|
|
|
49
|
-
```bash [global]
|
|
50
|
-
webflo init my-webflo-app
|
|
51
|
-
```
|
|
52
|
-
|
|
53
49
|
```bash [local]
|
|
54
50
|
npx webflo init my-webflo-app
|
|
55
51
|
```
|
|
56
52
|
|
|
53
|
+
```bash [global]
|
|
54
|
+
webflo init my-webflo-app
|
|
55
|
+
```
|
|
56
|
+
|
|
57
57
|
:::
|
|
58
58
|
|
|
59
59
|
This will create a new directory called `my-webflo-app` with a basic Webflo project structure.
|
|
@@ -70,28 +70,28 @@ The above command could take a project title and project description too:
|
|
|
70
70
|
|
|
71
71
|
::: code-group
|
|
72
72
|
|
|
73
|
-
```bash [global]
|
|
74
|
-
webflo init my-webflo-app "My Webflo App" "My first ever Webflo app"
|
|
75
|
-
```
|
|
76
|
-
|
|
77
73
|
```bash [local]
|
|
78
74
|
npx webflo init my-webflo-app "My Webflo App" "My first ever Webflo app"
|
|
79
75
|
```
|
|
80
76
|
|
|
77
|
+
```bash [global]
|
|
78
|
+
webflo init my-webflo-app "My Webflo App" "My first ever Webflo app"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
81
|
:::
|
|
82
82
|
|
|
83
83
|
And you can specify a template to use:
|
|
84
84
|
|
|
85
85
|
::: code-group
|
|
86
86
|
|
|
87
|
-
```bash [global]
|
|
88
|
-
webflo init my-webflo-app --template=web
|
|
89
|
-
```
|
|
90
|
-
|
|
91
87
|
```bash [local]
|
|
92
88
|
npx webflo init my-webflo-app --template=web
|
|
93
89
|
```
|
|
94
90
|
|
|
91
|
+
```bash [global]
|
|
92
|
+
webflo init my-webflo-app --template=web
|
|
93
|
+
```
|
|
94
|
+
|
|
95
95
|
:::
|
|
96
96
|
|
|
97
97
|
The default is: `web`.
|
|
@@ -151,11 +151,11 @@ The generated `package.json` for your project will include scripts like `dev`, `
|
|
|
151
151
|
"scripts": {
|
|
152
152
|
// Builds HTML templates and client bundles into public/assets
|
|
153
153
|
"build:html": "oohtml bundle --recursive --outdir=public/assets --auto-embed=app",
|
|
154
|
-
"build:js": "webflo
|
|
154
|
+
"build:js": "webflo build --client --worker --server --recursive --outdir=public/assets --auto-embed",
|
|
155
155
|
// Production build: runs both steps
|
|
156
156
|
"build": "npm run build:html && npm run build:js",
|
|
157
157
|
// Development server with auto-rebuilds and fine-grained HMR
|
|
158
|
-
"dev": "webflo start --dev --build-sensitivity=
|
|
158
|
+
"dev": "webflo start --dev --build-sensitivity=1",
|
|
159
159
|
// Production server
|
|
160
160
|
"start": "webflo start"
|
|
161
161
|
},
|
|
@@ -181,7 +181,7 @@ You can customize as necessary.
|
|
|
181
181
|
::: info Scripts & customization
|
|
182
182
|
- You can customize `build:html` or `build:js` scripts if you need finer control
|
|
183
183
|
- Add a `build:css` script if your CSS requires a build step; it'll be called in dev mode as necessary on CSS file changes
|
|
184
|
-
- Adjust dev mode's rebuild frequency for assets with `--build-sensitivity` (e.g., `--build-sensitivity=
|
|
184
|
+
- Adjust dev mode's rebuild frequency for assets with `--build-sensitivity` (e.g., `--build-sensitivity=1` — to defer rebuild until page reload; `0` — to turn off)
|
|
185
185
|
:::
|
|
186
186
|
|
|
187
187
|
## Running Your Application
|
|
@@ -194,14 +194,14 @@ Start the development server:
|
|
|
194
194
|
|
|
195
195
|
::: code-group
|
|
196
196
|
|
|
197
|
-
```bash [global]
|
|
198
|
-
webflo start --dev
|
|
199
|
-
```
|
|
200
|
-
|
|
201
197
|
```bash [npm]
|
|
202
198
|
npm run dev
|
|
203
199
|
```
|
|
204
200
|
|
|
201
|
+
```bash [global]
|
|
202
|
+
webflo start --dev
|
|
203
|
+
```
|
|
204
|
+
|
|
205
205
|
:::
|
|
206
206
|
|
|
207
207
|
The server starts on `http://localhost:3000` by default.
|
|
@@ -220,14 +220,14 @@ The server starts on `http://localhost:3000` by default.
|
|
|
220
220
|
|
|
221
221
|
::: code-group
|
|
222
222
|
|
|
223
|
-
```bash [global]
|
|
224
|
-
webflo start --dev --open --port 8080
|
|
225
|
-
```
|
|
226
|
-
|
|
227
223
|
```bash [npm]
|
|
228
224
|
npm run dev -- --open --port 8080
|
|
229
225
|
```
|
|
230
226
|
|
|
227
|
+
```bash [global]
|
|
228
|
+
webflo start --dev --open --port 8080
|
|
229
|
+
```
|
|
230
|
+
|
|
231
231
|
:::
|
|
232
232
|
|
|
233
233
|
### Webflo Build
|
|
@@ -254,14 +254,14 @@ Start the production server when ready:
|
|
|
254
254
|
|
|
255
255
|
::: code-group
|
|
256
256
|
|
|
257
|
-
```bash [global]
|
|
258
|
-
webflo start
|
|
259
|
-
```
|
|
260
|
-
|
|
261
257
|
```bash [npm]
|
|
262
258
|
npm start
|
|
263
259
|
```
|
|
264
260
|
|
|
261
|
+
```bash [global]
|
|
262
|
+
webflo start
|
|
263
|
+
```
|
|
264
|
+
|
|
265
265
|
Your app runs in production mode. Production can be in any filesystem-enabled JavaScript runtime.
|
|
266
266
|
|
|
267
267
|
::: warning Production differences
|
|
@@ -354,22 +354,22 @@ You’ve:
|
|
|
354
354
|
|
|
355
355
|
```bash
|
|
356
356
|
# Initialize a new project
|
|
357
|
-
webflo init <project-name>
|
|
357
|
+
npx webflo init <project-name>
|
|
358
358
|
|
|
359
359
|
# Start development server
|
|
360
|
-
webflo start --dev
|
|
360
|
+
npx webflo start --dev
|
|
361
361
|
|
|
362
362
|
# Start production server
|
|
363
|
-
webflo start
|
|
363
|
+
npx webflo start
|
|
364
364
|
|
|
365
365
|
# Build for production
|
|
366
|
-
webflo build
|
|
366
|
+
npx webflo build
|
|
367
367
|
|
|
368
368
|
# Configure Webflo
|
|
369
|
-
webflo config
|
|
369
|
+
npx webflo config
|
|
370
370
|
|
|
371
371
|
# View help
|
|
372
|
-
webflo --help
|
|
372
|
+
npx webflo --help
|
|
373
373
|
```
|
|
374
374
|
|
|
375
375
|
</details>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export class
|
|
1
|
+
export class CLIContext {
|
|
2
2
|
|
|
3
3
|
constructor(dict, CD = null) {
|
|
4
4
|
// dict can be plain object or some Context instance itself
|
|
@@ -14,15 +14,16 @@ export class Context {
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
get name() {
|
|
18
|
-
return 'webflo';
|
|
19
|
-
}
|
|
20
|
-
|
|
21
17
|
// create
|
|
22
18
|
static create(...args) {
|
|
23
19
|
return new this(...args);
|
|
24
20
|
}
|
|
25
21
|
|
|
22
|
+
// name
|
|
23
|
+
get name() {
|
|
24
|
+
return 'webflo';
|
|
25
|
+
}
|
|
26
|
+
|
|
26
27
|
// CWD
|
|
27
28
|
get CWD() {
|
|
28
29
|
return this.dict.CWD || '';
|
|
@@ -33,7 +34,7 @@ export class Context {
|
|
|
33
34
|
return this.dict.meta || {};
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
//
|
|
37
|
+
// appMeta
|
|
37
38
|
get appMeta() {
|
|
38
39
|
return this.dict.appMeta || {};
|
|
39
40
|
}
|
|
@@ -63,10 +64,10 @@ export class Context {
|
|
|
63
64
|
|
|
64
65
|
// logger
|
|
65
66
|
get logger() {
|
|
66
|
-
return this.dict.logger;
|
|
67
|
+
return this.dict.logger || console;
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
set logger(value) {
|
|
70
71
|
Object.defineProperty(this.dict, 'logger', { value } );
|
|
71
72
|
}
|
|
72
|
-
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import Fs from 'fs/promises';
|
|
2
|
+
//import { transformQuantum } from '@webqit/quantum-js';
|
|
3
|
+
|
|
4
|
+
export function LiveJSTransform() {
|
|
5
|
+
return {
|
|
6
|
+
name: 'livejs-transform',
|
|
7
|
+
setup(build) {
|
|
8
|
+
build.onLoad({ filter: /\.(js|mjs|ts|jsx|tsx)$/ }, async (args) => {
|
|
9
|
+
let code = await Fs.readFile(args.path, 'utf8');
|
|
10
|
+
|
|
11
|
+
//console.log('LiveJS -- transform:', args);
|
|
12
|
+
|
|
13
|
+
// super dirty detection
|
|
14
|
+
if (!/\bquantum\s+function\b/.test(code) &&
|
|
15
|
+
!/\basync\s+quantum\s+function\b/.test(code)) {
|
|
16
|
+
return { contents: code, loader: 'default' };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
console.log('LiveJS transform:', args.path);
|
|
21
|
+
|
|
22
|
+
return { contents: code, loader: 'default' };
|
|
23
|
+
const result = await transformQuantum(code, {
|
|
24
|
+
filename: args.path,
|
|
25
|
+
sourceMaps: true
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
contents: result.code,
|
|
30
|
+
loader: 'js' // or 'ts' if you want esbuild TS transform after
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|