visual-node 0.1.0 → 0.2.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 (26) hide show
  1. package/dist/examples/01-hello-world/README.md +25 -0
  2. package/dist/examples/01-hello-world/flow.blueprint +0 -0
  3. package/dist/examples/01-hello-world/flow.json +38 -0
  4. package/dist/examples/01-hello-world/server.js +13 -0
  5. package/dist/examples/02-rest-crud-with-variables/README.md +34 -0
  6. package/dist/examples/02-rest-crud-with-variables/flow.blueprint +0 -0
  7. package/dist/examples/02-rest-crud-with-variables/flow.json +76 -0
  8. package/dist/examples/02-rest-crud-with-variables/server.js +31 -0
  9. package/dist/examples/03-custom-middleware-logging/README.md +26 -0
  10. package/dist/examples/03-custom-middleware-logging/flow.blueprint +0 -0
  11. package/dist/examples/03-custom-middleware-logging/flow.json +45 -0
  12. package/dist/examples/03-custom-middleware-logging/server.js +18 -0
  13. package/dist/examples/04-function-graph-branch/README.md +29 -0
  14. package/dist/examples/04-function-graph-branch/flow.blueprint +0 -0
  15. package/dist/examples/04-function-graph-branch/flow.json +85 -0
  16. package/dist/examples/04-function-graph-branch/server.js +24 -0
  17. package/dist/examples/05-npm-package-require/README.md +30 -0
  18. package/dist/examples/05-npm-package-require/flow.blueprint +0 -0
  19. package/dist/examples/05-npm-package-require/flow.json +47 -0
  20. package/dist/examples/05-npm-package-require/server.js +17 -0
  21. package/dist/examples/README.md +30 -0
  22. package/dist/public/assets/{index-BpXY8lVq.js → index-DFqW8joi.js} +18 -18
  23. package/dist/public/index.html +1 -1
  24. package/dist/server.js +10 -0
  25. package/dist/server.js.map +1 -1
  26. package/package.json +4 -4
@@ -0,0 +1,25 @@
1
+ # Hello World
2
+
3
+ The canonical minimal flow — the shape every other example builds on.
4
+
5
+ **Nodes involved:** `express.init`, `express.middleware.jsonParser`, `express.route`,
6
+ `handler.sendJson`, `express.listen`.
7
+
8
+ A single `GET /hello` route answers with a static JSON body. No custom code, no
9
+ variables, no dependencies — just the five node types every generated server starts
10
+ from.
11
+
12
+ ## Run it
13
+
14
+ ```bash
15
+ npx visual-node examples/01-hello-world
16
+ ```
17
+
18
+ Open `http://localhost:4000`, hit **Compile**, then **Run Server** to spawn it — or just
19
+ read the committed [`server.js`](server.js), which is exactly what compiling this flow
20
+ produces.
21
+
22
+ ```bash
23
+ curl http://localhost:3001/hello
24
+ # {"message":"Hello World"}
25
+ ```
@@ -0,0 +1,38 @@
1
+ {
2
+ "version": "1",
3
+ "meta": { "name": "hello-world", "target": "express" },
4
+ "nodes": [
5
+ { "id": "init_1", "type": "express.init", "position": { "x": 0, "y": 0 }, "data": {} },
6
+ {
7
+ "id": "json_parser_1",
8
+ "type": "express.middleware.jsonParser",
9
+ "position": { "x": 200, "y": 0 },
10
+ "data": {}
11
+ },
12
+ {
13
+ "id": "route_1",
14
+ "type": "express.route",
15
+ "position": { "x": 400, "y": 0 },
16
+ "data": { "method": "GET", "path": "/hello" }
17
+ },
18
+ {
19
+ "id": "send_json_1",
20
+ "type": "handler.sendJson",
21
+ "position": { "x": 600, "y": 0 },
22
+ "data": { "statusCode": 200, "body": { "message": "Hello World" } }
23
+ },
24
+ {
25
+ "id": "listen_1",
26
+ "type": "express.listen",
27
+ "position": { "x": 200, "y": 200 },
28
+ "data": { "port": 3001 }
29
+ }
30
+ ],
31
+ "edges": [
32
+ { "id": "e1", "source": "init_1", "target": "json_parser_1", "sourceHandle": "out", "targetHandle": "in" },
33
+ { "id": "e2", "source": "json_parser_1", "target": "route_1", "sourceHandle": "out", "targetHandle": "in" },
34
+ { "id": "e3", "source": "route_1", "target": "send_json_1", "sourceHandle": "out", "targetHandle": "in" },
35
+ { "id": "e4", "source": "init_1", "target": "listen_1", "sourceHandle": "out", "targetHandle": "in" }
36
+ ],
37
+ "variables": []
38
+ }
@@ -0,0 +1,13 @@
1
+ const express = require("express");
2
+
3
+ const app = express();
4
+
5
+ app.use(express.json());
6
+
7
+ app.get("/hello", (req, res) => {
8
+ res.status(200).json({ message: "Hello World" });
9
+ });
10
+
11
+ app.listen(3001, () => {
12
+ console.log("Server running on port 3001");
13
+ });
@@ -0,0 +1,34 @@
1
+ # REST CRUD with Variables
2
+
3
+ An in-memory `items` REST API demonstrating the **Variables** system alongside the
4
+ **Custom Code** escape hatch.
5
+
6
+ **Nodes involved:** `variable.set`, `variable.get`, `debug.consoleLog`,
7
+ `handler.customCode`, `handler.sendJson`, plus the standard `express.*` wiring.
8
+
9
+ A file-scoped `let items = []` (declared once, in the flow's **Variables** panel) backs
10
+ four routes:
11
+
12
+ - `GET /items` — reads `items` directly (a Custom Code node can reference any file-scoped
13
+ variable by name, no wiring needed).
14
+ - `POST /items` — pushes a new item built from `req.body`.
15
+ - `DELETE /items/:id` — reassigns `items` via a wired **Set Variable** node (its literal
16
+ value field holds `items.filter(...)`), then responds with a static `Send JSON` node.
17
+ - `GET /items/count` — wires a **Get Variable** node's output into a **Console Log**
18
+ node (logging the current array server-side) before a Custom Code node responds with
19
+ the count — the one route here that actually wires a value pin end to end, rather than
20
+ referencing the variable by bare identifier.
21
+
22
+ ## Run it
23
+
24
+ ```bash
25
+ npx visual-node examples/02-rest-crud-with-variables
26
+ ```
27
+
28
+ ```bash
29
+ curl http://localhost:3002/items # []
30
+ curl -X POST -H 'content-type: application/json' -d '{"name":"widget"}' http://localhost:3002/items
31
+ curl http://localhost:3002/items # [{"id":"1","name":"widget"}]
32
+ curl http://localhost:3002/items/count # {"count":1}
33
+ curl -X DELETE http://localhost:3002/items/1 # {"success":true}
34
+ ```
@@ -0,0 +1,76 @@
1
+ {
2
+ "version": "1",
3
+ "meta": { "name": "rest-crud-with-variables", "target": "express" },
4
+ "nodes": [
5
+ { "id": "init_1", "type": "express.init", "position": { "x": 0, "y": 0 }, "data": {} },
6
+ {
7
+ "id": "json_parser_1",
8
+ "type": "express.middleware.jsonParser",
9
+ "position": { "x": 200, "y": 0 },
10
+ "data": {}
11
+ },
12
+ {
13
+ "id": "listen_1",
14
+ "type": "express.listen",
15
+ "position": { "x": 0, "y": 400 },
16
+ "data": { "port": 3002 }
17
+ },
18
+
19
+ { "id": "route_list", "type": "express.route", "position": { "x": 450, "y": 0 }, "data": { "method": "GET", "path": "/items" } },
20
+ { "id": "handler_list", "type": "handler.customCode", "position": { "x": 650, "y": 0 }, "data": { "code": "res.status(200).json(items);" } },
21
+
22
+ { "id": "route_create", "type": "express.route", "position": { "x": 450, "y": 100 }, "data": { "method": "POST", "path": "/items" } },
23
+ {
24
+ "id": "handler_create",
25
+ "type": "handler.customCode",
26
+ "position": { "x": 650, "y": 100 },
27
+ "data": { "code": "const item = { id: String(items.length + 1), ...req.body };\nitems.push(item);\nres.status(201).json(item);" }
28
+ },
29
+
30
+ { "id": "route_delete", "type": "express.route", "position": { "x": 450, "y": 200 }, "data": { "method": "DELETE", "path": "/items/:id" } },
31
+ {
32
+ "id": "var_set_delete",
33
+ "type": "variable.set",
34
+ "position": { "x": 650, "y": 200 },
35
+ "data": { "variableId": "var_items", "literals": { "value": "items.filter((item) => item.id !== req.params.id)" } }
36
+ },
37
+ { "id": "handler_delete_response", "type": "handler.sendJson", "position": { "x": 850, "y": 200 }, "data": { "statusCode": 200, "body": { "success": true } } },
38
+
39
+ { "id": "route_count", "type": "express.route", "position": { "x": 450, "y": 300 }, "data": { "method": "GET", "path": "/items/count" } },
40
+ { "id": "var_get_count", "type": "variable.get", "position": { "x": 450, "y": 380 }, "data": { "variableId": "var_items" } },
41
+ {
42
+ "id": "log_count",
43
+ "type": "debug.consoleLog",
44
+ "position": { "x": 650, "y": 300 },
45
+ "data": { "expression": "\"items snapshot:\"" }
46
+ },
47
+ {
48
+ "id": "handler_count",
49
+ "type": "handler.customCode",
50
+ "position": { "x": 850, "y": 300 },
51
+ "data": { "code": "res.status(200).json({ count: items.length });" }
52
+ }
53
+ ],
54
+ "edges": [
55
+ { "id": "e1", "source": "init_1", "target": "json_parser_1", "sourceHandle": "out", "targetHandle": "in" },
56
+ { "id": "e2", "source": "init_1", "target": "listen_1", "sourceHandle": "out", "targetHandle": "in" },
57
+
58
+ { "id": "e3", "source": "json_parser_1", "target": "route_list", "sourceHandle": "out", "targetHandle": "in" },
59
+ { "id": "e4", "source": "route_list", "target": "handler_list", "sourceHandle": "out", "targetHandle": "in" },
60
+
61
+ { "id": "e5", "source": "json_parser_1", "target": "route_create", "sourceHandle": "out", "targetHandle": "in" },
62
+ { "id": "e6", "source": "route_create", "target": "handler_create", "sourceHandle": "out", "targetHandle": "in" },
63
+
64
+ { "id": "e7", "source": "json_parser_1", "target": "route_delete", "sourceHandle": "out", "targetHandle": "in" },
65
+ { "id": "e8", "source": "route_delete", "target": "var_set_delete", "sourceHandle": "out", "targetHandle": "in" },
66
+ { "id": "e9", "source": "var_set_delete", "target": "handler_delete_response", "sourceHandle": "out", "targetHandle": "in" },
67
+
68
+ { "id": "e10", "source": "json_parser_1", "target": "route_count", "sourceHandle": "out", "targetHandle": "in" },
69
+ { "id": "e11", "source": "route_count", "target": "log_count", "sourceHandle": "out", "targetHandle": "in" },
70
+ { "id": "e12", "source": "var_get_count", "target": "log_count", "sourceHandle": "value", "targetHandle": "value" },
71
+ { "id": "e13", "source": "log_count", "target": "handler_count", "sourceHandle": "out", "targetHandle": "in" }
72
+ ],
73
+ "variables": [
74
+ { "id": "var_items", "name": "items", "keyword": "let", "dataType": "array", "defaultValue": "[]" }
75
+ ]
76
+ }
@@ -0,0 +1,31 @@
1
+ const express = require("express");
2
+
3
+ const app = express();
4
+
5
+ let items = [];
6
+
7
+ app.use(express.json());
8
+
9
+ app.get("/items", (req, res) => {
10
+ res.status(200).json(items);
11
+ });
12
+
13
+ app.post("/items", (req, res) => {
14
+ const item = { id: String(items.length + 1), ...req.body };
15
+ items.push(item);
16
+ res.status(201).json(item);
17
+ });
18
+
19
+ app.delete("/items/:id", (req, res) => {
20
+ items = items.filter((item) => item.id !== req.params.id);
21
+ res.status(200).json({ success: true });
22
+ });
23
+
24
+ app.get("/items/count", (req, res) => {
25
+ console.log(items);
26
+ res.status(200).json({ count: items.length });
27
+ });
28
+
29
+ app.listen(3002, () => {
30
+ console.log("Server running on port 3002");
31
+ });
@@ -0,0 +1,26 @@
1
+ # Custom Middleware Logging
2
+
3
+ Demonstrates the middleware escape hatch: a hand-written `app.use(...)` request logger
4
+ placed ahead of the JSON body parser.
5
+
6
+ **Nodes involved:** `middleware.customCode`, plus the standard `express.*` wiring and a
7
+ `handler.sendJson` route.
8
+
9
+ `middleware.customCode` wraps its raw code in `app.use((req, res, next) => { ... })` —
10
+ call `next()` to continue the chain, or respond directly to end it there. This example
11
+ just logs `METHOD path` for every request before falling through to a trivial
12
+ `GET /ping` route.
13
+
14
+ ## Run it
15
+
16
+ ```bash
17
+ npx visual-node examples/03-custom-middleware-logging
18
+ ```
19
+
20
+ ```bash
21
+ curl http://localhost:3003/ping
22
+ # {"pong":true}
23
+ ```
24
+
25
+ Watch the server's console output — it prints `GET /ping` for the request above, logged
26
+ entirely from the custom middleware node.
@@ -0,0 +1,45 @@
1
+ {
2
+ "version": "1",
3
+ "meta": { "name": "custom-middleware-logging", "target": "express" },
4
+ "nodes": [
5
+ { "id": "init_1", "type": "express.init", "position": { "x": 0, "y": 0 }, "data": {} },
6
+ {
7
+ "id": "mw_logger",
8
+ "type": "middleware.customCode",
9
+ "position": { "x": 200, "y": 0 },
10
+ "data": { "code": "console.log(`${req.method} ${req.path}`);\nnext();", "isAsync": false, "npmDependencies": "" }
11
+ },
12
+ {
13
+ "id": "json_parser_1",
14
+ "type": "express.middleware.jsonParser",
15
+ "position": { "x": 400, "y": 0 },
16
+ "data": {}
17
+ },
18
+ {
19
+ "id": "route_1",
20
+ "type": "express.route",
21
+ "position": { "x": 600, "y": 0 },
22
+ "data": { "method": "GET", "path": "/ping" }
23
+ },
24
+ {
25
+ "id": "send_json_1",
26
+ "type": "handler.sendJson",
27
+ "position": { "x": 800, "y": 0 },
28
+ "data": { "statusCode": 200, "body": { "pong": true } }
29
+ },
30
+ {
31
+ "id": "listen_1",
32
+ "type": "express.listen",
33
+ "position": { "x": 200, "y": 200 },
34
+ "data": { "port": 3003 }
35
+ }
36
+ ],
37
+ "edges": [
38
+ { "id": "e1", "source": "init_1", "target": "mw_logger", "sourceHandle": "out", "targetHandle": "in" },
39
+ { "id": "e2", "source": "mw_logger", "target": "json_parser_1", "sourceHandle": "out", "targetHandle": "in" },
40
+ { "id": "e3", "source": "json_parser_1", "target": "route_1", "sourceHandle": "out", "targetHandle": "in" },
41
+ { "id": "e4", "source": "route_1", "target": "send_json_1", "sourceHandle": "out", "targetHandle": "in" },
42
+ { "id": "e5", "source": "init_1", "target": "listen_1", "sourceHandle": "out", "targetHandle": "in" }
43
+ ],
44
+ "variables": []
45
+ }
@@ -0,0 +1,18 @@
1
+ const express = require("express");
2
+
3
+ const app = express();
4
+
5
+ app.use((req, res, next) => {
6
+ console.log(`${req.method} ${req.path}`);
7
+ next();
8
+ });
9
+
10
+ app.use(express.json());
11
+
12
+ app.get("/ping", (req, res) => {
13
+ res.status(200).json({ pong: true });
14
+ });
15
+
16
+ app.listen(3003, () => {
17
+ console.log("Server running on port 3003");
18
+ });
@@ -0,0 +1,29 @@
1
+ # Function Graph with Branch
2
+
3
+ A `logic.function` node ("isEven") authored in **blueprint mode** — a nested visual node
4
+ graph instead of hand-typed code — using `controlFlow.branch` to fork execution and a
5
+ function-scoped **Variable** to carry the result back out.
6
+
7
+ **Nodes involved (inside the function's nested graph):** `logic.graphEntry`,
8
+ `controlFlow.branch`, `variable.set`, `variable.get`, `logic.graphReturn`. **On the main
9
+ canvas:** the same `logic.function` node (private — never wired to an Export, so it stays
10
+ a file-local helper), called directly by name from a `handler.customCode` node.
11
+
12
+ The nested graph: `Start` → `Branch` (condition `n % 2 === 0`, a literal expression, not
13
+ wired) → **True** arm sets a `result` variable to `true`, **False** arm sets it to
14
+ `false`. A **Get Variable** node feeds the **Return** node's value — since reading a
15
+ variable is always safe regardless of which arm set it, this sidesteps the restriction
16
+ that a Branch/Switch arm's own values can't be read directly from the Return node. Open
17
+ this function's "Blueprint Graph" from its config panel to see the two-arm graph on
18
+ canvas.
19
+
20
+ ## Run it
21
+
22
+ ```bash
23
+ npx visual-node examples/04-function-graph-branch
24
+ ```
25
+
26
+ ```bash
27
+ curl "http://localhost:3004/is-even?n=4" # {"n":4,"isEven":true}
28
+ curl "http://localhost:3004/is-even?n=7" # {"n":7,"isEven":false}
29
+ ```
@@ -0,0 +1,85 @@
1
+ {
2
+ "version": "1",
3
+ "meta": { "name": "function-graph-branch", "target": "express" },
4
+ "nodes": [
5
+ { "id": "init_1", "type": "express.init", "position": { "x": 0, "y": 0 }, "data": {} },
6
+ {
7
+ "id": "json_parser_1",
8
+ "type": "express.middleware.jsonParser",
9
+ "position": { "x": 200, "y": 0 },
10
+ "data": {}
11
+ },
12
+ {
13
+ "id": "listen_1",
14
+ "type": "express.listen",
15
+ "position": { "x": 0, "y": 300 },
16
+ "data": { "port": 3004 }
17
+ },
18
+ {
19
+ "id": "fn_isEven",
20
+ "type": "logic.function",
21
+ "position": { "x": 0, "y": 150 },
22
+ "data": {
23
+ "name": "isEven",
24
+ "params": "n",
25
+ "body": "",
26
+ "mode": "blueprint",
27
+ "isAsync": false,
28
+ "npmDependencies": "",
29
+ "graph": {
30
+ "variables": [
31
+ { "id": "var_result", "name": "result", "keyword": "let", "dataType": "boolean", "defaultValue": "false" }
32
+ ],
33
+ "nodes": [
34
+ { "id": "entry1", "type": "logic.graphEntry", "position": { "x": 0, "y": 0 }, "data": { "params": ["n"] } },
35
+ {
36
+ "id": "branch1",
37
+ "type": "controlFlow.branch",
38
+ "position": { "x": 200, "y": 0 },
39
+ "data": { "literals": { "condition": "n % 2 === 0" } }
40
+ },
41
+ {
42
+ "id": "set_true",
43
+ "type": "variable.set",
44
+ "position": { "x": 400, "y": -80 },
45
+ "data": { "variableId": "var_result", "literals": { "value": "true" } }
46
+ },
47
+ {
48
+ "id": "set_false",
49
+ "type": "variable.set",
50
+ "position": { "x": 400, "y": 80 },
51
+ "data": { "variableId": "var_result", "literals": { "value": "false" } }
52
+ },
53
+ { "id": "get_result", "type": "variable.get", "position": { "x": 600, "y": 0 }, "data": { "variableId": "var_result" } },
54
+ { "id": "ret1", "type": "logic.graphReturn", "position": { "x": 800, "y": 0 }, "data": {} }
55
+ ],
56
+ "edges": [
57
+ { "id": "ge1", "source": "entry1", "target": "branch1", "sourceHandle": "out", "targetHandle": "in" },
58
+ { "id": "ge2", "source": "branch1", "target": "set_true", "sourceHandle": "true", "targetHandle": "in" },
59
+ { "id": "ge3", "source": "branch1", "target": "set_false", "sourceHandle": "false", "targetHandle": "in" },
60
+ { "id": "ge4", "source": "get_result", "target": "ret1", "sourceHandle": "value", "targetHandle": "value" }
61
+ ]
62
+ }
63
+ }
64
+ },
65
+ {
66
+ "id": "route_1",
67
+ "type": "express.route",
68
+ "position": { "x": 400, "y": 0 },
69
+ "data": { "method": "GET", "path": "/is-even" }
70
+ },
71
+ {
72
+ "id": "handler_1",
73
+ "type": "handler.customCode",
74
+ "position": { "x": 600, "y": 0 },
75
+ "data": { "code": "const n = Number(req.query.n ?? 0);\nres.status(200).json({ n, isEven: isEven(n) });" }
76
+ }
77
+ ],
78
+ "edges": [
79
+ { "id": "e1", "source": "init_1", "target": "json_parser_1", "sourceHandle": "out", "targetHandle": "in" },
80
+ { "id": "e2", "source": "json_parser_1", "target": "route_1", "sourceHandle": "out", "targetHandle": "in" },
81
+ { "id": "e3", "source": "route_1", "target": "handler_1", "sourceHandle": "out", "targetHandle": "in" },
82
+ { "id": "e4", "source": "init_1", "target": "listen_1", "sourceHandle": "out", "targetHandle": "in" }
83
+ ],
84
+ "variables": []
85
+ }
@@ -0,0 +1,24 @@
1
+ const express = require("express");
2
+
3
+ const app = express();
4
+
5
+ function isEven(n) {
6
+ let result = false;
7
+ if (n % 2 === 0) {
8
+ result = true;
9
+ } else {
10
+ result = false;
11
+ }
12
+ return result;
13
+ }
14
+
15
+ app.use(express.json());
16
+
17
+ app.get("/is-even", (req, res) => {
18
+ const n = Number(req.query.n ?? 0);
19
+ res.status(200).json({ n, isEven: isEven(n) });
20
+ });
21
+
22
+ app.listen(3004, () => {
23
+ console.log("Server running on port 3004");
24
+ });
@@ -0,0 +1,30 @@
1
+ # npm Package Require
2
+
3
+ Demonstrates requiring an installed npm package (not just another local `.blueprint`
4
+ file) and the **Async Handler** checkbox for `await`-ing inside a route.
5
+
6
+ **Nodes involved:** `logic.require` (Source: npm, Package: `uuid`, Version: `^9.0.0`),
7
+ `express.route` with **Async Handler** enabled, `handler.customCode`.
8
+
9
+ The Require node emits `const uuid = require("uuid");` at the top of the file and
10
+ declares `uuid` as a dependency — compiling this flow (or the whole project) collects it
11
+ into the generated `package.json`, so `npm install` in the output directory pulls in
12
+ everything the flow needs. **Note the pinned `^9.0.0`**: newer `uuid` majors ship
13
+ ESM-only and can't be `require()`'d from CommonJS output — this pin is load-bearing, not
14
+ arbitrary.
15
+
16
+ The route itself has **Async Handler** checked, so its generated handler is
17
+ `async (req, res) => { ... }`, letting the Custom Code node `await` a promise (here just
18
+ a small `setTimeout` delay, standing in for any real async work like a database call or
19
+ network request) before responding.
20
+
21
+ ## Run it
22
+
23
+ ```bash
24
+ npx visual-node examples/05-npm-package-require
25
+ ```
26
+
27
+ ```bash
28
+ curl http://localhost:3005/id
29
+ # {"id":"...","generatedAt":"..."}
30
+ ```
@@ -0,0 +1,47 @@
1
+ {
2
+ "version": "1",
3
+ "meta": { "name": "npm-package-require", "target": "express" },
4
+ "nodes": [
5
+ { "id": "init_1", "type": "express.init", "position": { "x": 0, "y": 0 }, "data": {} },
6
+ {
7
+ "id": "json_parser_1",
8
+ "type": "express.middleware.jsonParser",
9
+ "position": { "x": 200, "y": 0 },
10
+ "data": {}
11
+ },
12
+ {
13
+ "id": "req_uuid",
14
+ "type": "logic.require",
15
+ "position": { "x": 0, "y": 150 },
16
+ "data": { "sourceType": "npm", "path": "uuid", "variableName": "uuid", "version": "^9.0.0" }
17
+ },
18
+ {
19
+ "id": "route_1",
20
+ "type": "express.route",
21
+ "position": { "x": 400, "y": 0 },
22
+ "data": { "method": "GET", "path": "/id", "isAsync": true }
23
+ },
24
+ {
25
+ "id": "handler_1",
26
+ "type": "handler.customCode",
27
+ "position": { "x": 600, "y": 0 },
28
+ "data": {
29
+ "code": "await new Promise((resolve) => setTimeout(resolve, 5));\nres.status(200).json({ id: uuid.v4(), generatedAt: new Date().toISOString() });",
30
+ "npmDependencies": ""
31
+ }
32
+ },
33
+ {
34
+ "id": "listen_1",
35
+ "type": "express.listen",
36
+ "position": { "x": 200, "y": 300 },
37
+ "data": { "port": 3005 }
38
+ }
39
+ ],
40
+ "edges": [
41
+ { "id": "e1", "source": "init_1", "target": "json_parser_1", "sourceHandle": "out", "targetHandle": "in" },
42
+ { "id": "e2", "source": "json_parser_1", "target": "route_1", "sourceHandle": "out", "targetHandle": "in" },
43
+ { "id": "e3", "source": "route_1", "target": "handler_1", "sourceHandle": "out", "targetHandle": "in" },
44
+ { "id": "e4", "source": "init_1", "target": "listen_1", "sourceHandle": "out", "targetHandle": "in" }
45
+ ],
46
+ "variables": []
47
+ }
@@ -0,0 +1,17 @@
1
+ const express = require("express");
2
+ const uuid = require("uuid");
3
+
4
+ const app = express();
5
+
6
+ app.use(express.json());
7
+
8
+ app.get("/id", async (req, res) => {
9
+ await new Promise((resolve) => setTimeout(resolve, 5));
10
+ res
11
+ .status(200)
12
+ .json({ id: uuid.v4(), generatedAt: new Date().toISOString() });
13
+ });
14
+
15
+ app.listen(3005, () => {
16
+ console.log("Server running on port 3005");
17
+ });
@@ -0,0 +1,30 @@
1
+ # Examples
2
+
3
+ Five worked flows, each in its own folder with a source `flow.json`, the compiled
4
+ `server.js` output, and a `.blueprint` binary you can open directly in the editor. Every
5
+ example has been built and run for real — the curl commands in each `README.md` are not
6
+ hypothetical.
7
+
8
+ | Example | Demonstrates |
9
+ | --- | --- |
10
+ | [`01-hello-world`](01-hello-world/) | The minimal flow: init → middleware → route → handler → listen |
11
+ | [`02-rest-crud-with-variables`](02-rest-crud-with-variables/) | Variables (`variable.get`/`variable.set`) + the Custom Code escape hatch |
12
+ | [`03-custom-middleware-logging`](03-custom-middleware-logging/) | The middleware escape hatch (`middleware.customCode`) |
13
+ | [`04-function-graph-branch`](04-function-graph-branch/) | A visual Function Graph with `controlFlow.branch` |
14
+ | [`05-npm-package-require`](05-npm-package-require/) | Requiring an npm package + the Async Handler checkbox |
15
+
16
+ ## Opening an example in the editor
17
+
18
+ ```bash
19
+ npx visual-node examples/01-hello-world
20
+ ```
21
+
22
+ ## Regenerating `server.js` / `.blueprint` from `flow.json`
23
+
24
+ Each example's `flow.json` is the hand-editable source of truth. After changing one,
25
+ regenerate its compiled output from the repo root (requires `pnpm -r run build` to have
26
+ been run first, so `packages/core/dist` exists):
27
+
28
+ ```bash
29
+ node examples/build.mjs
30
+ ```