@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.
Files changed (46) hide show
  1. package/package.json +5 -20
  2. package/site/.vitepress/config.ts +1 -1
  3. package/site/docs/concepts/realtime.md +57 -53
  4. package/site/docs/concepts/{request-response.md → requests-responses.md} +1 -1
  5. package/site/docs/concepts/state.md +1 -1
  6. package/site/docs/getting-started.md +40 -40
  7. package/src/{Context.js → CLIContext.js} +9 -8
  8. package/src/build-pi/esbuild-plugin-livejs-transform.js +35 -0
  9. package/src/{runtime-pi/webflo-client/webflo-codegen.js → build-pi/index.js} +145 -141
  10. package/src/index.js +3 -1
  11. package/src/init-pi/index.js +6 -3
  12. package/src/init-pi/templates/pwa/package.json +2 -2
  13. package/src/init-pi/templates/web/package.json +2 -2
  14. package/src/runtime-pi/AppBootstrap.js +38 -0
  15. package/src/runtime-pi/WebfloRuntime.js +50 -47
  16. package/src/runtime-pi/apis.js +9 -0
  17. package/src/runtime-pi/index.js +2 -4
  18. package/src/runtime-pi/webflo-client/WebfloClient.js +31 -35
  19. package/src/runtime-pi/webflo-client/WebfloRootClient1.js +16 -14
  20. package/src/runtime-pi/webflo-client/WebfloSubClient.js +13 -13
  21. package/src/runtime-pi/webflo-client/bootstrap.js +37 -0
  22. package/src/runtime-pi/webflo-client/index.js +2 -8
  23. package/src/runtime-pi/webflo-client/webflo-devmode.js +3 -3
  24. package/src/runtime-pi/webflo-fetch/LiveResponse.js +127 -96
  25. package/src/runtime-pi/webflo-fetch/index.js +435 -5
  26. package/src/runtime-pi/webflo-routing/HttpCookies.js +1 -1
  27. package/src/runtime-pi/webflo-routing/HttpEvent.js +5 -6
  28. package/src/runtime-pi/webflo-routing/HttpUser.js +7 -7
  29. package/src/runtime-pi/webflo-server/ServerSideCookies.js +3 -1
  30. package/src/runtime-pi/webflo-server/ServerSideSession.js +2 -1
  31. package/src/runtime-pi/webflo-server/WebfloServer.js +98 -195
  32. package/src/runtime-pi/webflo-server/bootstrap.js +59 -0
  33. package/src/runtime-pi/webflo-server/index.js +2 -6
  34. package/src/runtime-pi/webflo-server/webflo-devmode.js +13 -24
  35. package/src/runtime-pi/webflo-worker/WebfloWorker.js +11 -15
  36. package/src/runtime-pi/webflo-worker/WorkerSideCookies.js +2 -1
  37. package/src/runtime-pi/webflo-worker/bootstrap.js +38 -0
  38. package/src/runtime-pi/webflo-worker/index.js +3 -7
  39. package/src/webflo-cli.js +1 -2
  40. package/src/runtime-pi/webflo-fetch/cookies.js +0 -10
  41. package/src/runtime-pi/webflo-fetch/fetch.js +0 -16
  42. package/src/runtime-pi/webflo-fetch/formdata.js +0 -54
  43. package/src/runtime-pi/webflo-fetch/headers.js +0 -151
  44. package/src/runtime-pi/webflo-fetch/message.js +0 -49
  45. package/src/runtime-pi/webflo-fetch/request.js +0 -62
  46. 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.1",
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: 'Request/Response Lifecycle', link: '/docs/concepts/request-response' },
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('message', handle);
24
- event.client.postMessage({ message: 'You seem to be typing something.' });
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: 'Should we proceed?' });
29
- const result = await event.user.prompt({ message: 'What is your name?' });
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('test-channel');
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: 'world' });
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('https://api.example.com/data');
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('message', (message) => {
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('message', (message) => {
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('message', (message) => {
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: 'Are you sure?' });
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 = 'someValue';
281
+ $data.someProp = "someValue";
282
282
  await new Promise((resolve) => setTimeout(resolve, 1000));
283
- $data.someProp = 'someOtherValue';
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-world Examples
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/observer';
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: 'Home', href: '/' },
384
- { name: 'About', href: '/about' },
385
- { name: 'Contact', href: '/contact' },
385
+ { name: "Home", href: "/" },
386
+ { name: "About", href: "/about" },
387
+ { name: "Contact", href: "/contact" },
386
388
  ],
387
389
  content: {
388
- header: 'Loading...',
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('https://api.example.com/data');
396
- Observer.set(data.content, 'header', someData.header);
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: 'Inventory 1:' + someData.items[0] },
400
- { text: 'Inventory 2:' + someData.items[1] }
401
+ { text: "Inventory 1:" + someData.items[0] },
402
+ { text: "Inventory 2:" + someData.items[1] }
401
403
  );
402
404
 
403
- const someData2 = await fetch('https://api.example.com/data2');
405
+ const someData2 = await fetch("https://api.example.com/data2");
404
406
  Observer.proxy(data.content.body).push(
405
- { text: 'Inventory 3:' + someData2.items[0] },
406
- { text: 'Inventory 4:' + someData2.items[1] }
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('https://api.example.com/data');
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: '/done' } }); // [!code highlight]
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 = 'This item will be permanently deleted. Are you sure?';
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('prompt', (e) => {
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 = 'test-channel';
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: 'sample name' }),
516
+ resolveData: (message) => ({ ...message, user: "sample name" }),
515
517
  });
516
518
 
517
519
  event.waitUntilNavigate(); // keep open
518
- event.client.addEventListener('close', (e) => {
520
+ event.client.addEventListener("close", (e) => {
519
521
  //e.preventDefault();
520
- console.log('Chat closed');
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: 'Beginning of chat...' }] };
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 = 'test-channel';
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 Browser
610
- participant Handler
611
- Browser->>Handler: HTTP request
612
- Handler-->>Browser: Initial Response (or a Temporary 202 Accepted) <br>+ X-Background-Messaging-Port
613
- Browser-->>Handler: Connect (WebSocket / Broadcast / MessageChannel)
614
- Note over Browser,Handler: Background mode established
615
- Handler-->>Browser: Messages, updates, dialogs, etc.
616
- Browser-->>Handler: Replies, requests, etc.
617
- Browser<<-->>Handler: Termination
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 case `1.` above happening _before_ yielding a response, Webflo sends a temporary `202 Accepted` response to the client carrying this header.
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
- # Request/Response Lifecycle
1
+ # Requests & Responses
2
2
 
3
3
  This page explains how Webflo orchestrates each interaction from request to response, and how you can hook into various stages, including realtime.
4
4
 
@@ -1,4 +1,4 @@
1
- # State Management
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: Global Installation (Recommended)
25
+ ### Option 1: Local Installation (Recommended)
26
26
 
27
- Install Webflo globally to use the CLI from anywhere:
27
+ Install Webflo as a dependency in your project:
28
28
 
29
29
  ```bash
30
- npm install -g @webqit/webflo
30
+ npm install @webqit/webflo
31
31
  ```
32
32
 
33
- ### Option 2: Local Installation
33
+ ### Option 2: Global Installation
34
34
 
35
- Install Webflo as a dependency in your project:
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 `global` and `local` styles.
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 generate::client --recursive --outdir=public/assets --auto-embed",
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=2",
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=2` — to defer rebuild until page reload; `0` — to turn off)
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 Context {
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
- // app
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
+ }