visual-node 0.1.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 (56) hide show
  1. package/README.md +42 -0
  2. package/dist/app.d.ts +3 -0
  3. package/dist/app.js +44 -0
  4. package/dist/app.js.map +1 -0
  5. package/dist/codegen-helpers.d.ts +55 -0
  6. package/dist/codegen-helpers.js +142 -0
  7. package/dist/codegen-helpers.js.map +1 -0
  8. package/dist/config.d.ts +9 -0
  9. package/dist/config.js +13 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/connect/compile-function-graph.service.d.ts +14 -0
  12. package/dist/connect/compile-function-graph.service.js +106 -0
  13. package/dist/connect/compile-function-graph.service.js.map +1 -0
  14. package/dist/connect/files.service.d.ts +7 -0
  15. package/dist/connect/files.service.js +175 -0
  16. package/dist/connect/files.service.js.map +1 -0
  17. package/dist/connect/node-registry-flow.service.d.ts +14 -0
  18. package/dist/connect/node-registry-flow.service.js +142 -0
  19. package/dist/connect/node-registry-flow.service.js.map +1 -0
  20. package/dist/connect/plugins.service.d.ts +24 -0
  21. package/dist/connect/plugins.service.js +73 -0
  22. package/dist/connect/plugins.service.js.map +1 -0
  23. package/dist/connect/run.service.d.ts +3 -0
  24. package/dist/connect/run.service.js +159 -0
  25. package/dist/connect/run.service.js.map +1 -0
  26. package/dist/connect/validate-generate.service.d.ts +19 -0
  27. package/dist/connect/validate-generate.service.js +102 -0
  28. package/dist/connect/validate-generate.service.js.map +1 -0
  29. package/dist/file-tree.d.ts +23 -0
  30. package/dist/file-tree.js +63 -0
  31. package/dist/file-tree.js.map +1 -0
  32. package/dist/flow-shape.d.ts +8 -0
  33. package/dist/flow-shape.js +13 -0
  34. package/dist/flow-shape.js.map +1 -0
  35. package/dist/path-safety.d.ts +12 -0
  36. package/dist/path-safety.js +26 -0
  37. package/dist/path-safety.js.map +1 -0
  38. package/dist/plugin-loading.d.ts +18 -0
  39. package/dist/plugin-loading.js +47 -0
  40. package/dist/plugin-loading.js.map +1 -0
  41. package/dist/plugin-readme.d.ts +9 -0
  42. package/dist/plugin-readme.js +235 -0
  43. package/dist/plugin-readme.js.map +1 -0
  44. package/dist/public/assets/index-6_2vDtnd.css +1 -0
  45. package/dist/public/assets/index-BpXY8lVq.js +101 -0
  46. package/dist/public/index.html +13 -0
  47. package/dist/runner.d.ts +23 -0
  48. package/dist/runner.js +65 -0
  49. package/dist/runner.js.map +1 -0
  50. package/dist/server.d.ts +2 -0
  51. package/dist/server.js +34 -0
  52. package/dist/server.js.map +1 -0
  53. package/dist/static.d.ts +12 -0
  54. package/dist/static.js +28 -0
  55. package/dist/static.js.map +1 -0
  56. package/package.json +39 -0
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>FlowServer</title>
7
+ <script type="module" crossorigin src="/assets/index-BpXY8lVq.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-6_2vDtnd.css">
9
+ </head>
10
+ <body>
11
+ <div id="root"></div>
12
+ </body>
13
+ </html>
@@ -0,0 +1,23 @@
1
+ import { EventEmitter } from "node:events";
2
+ /**
3
+ * Manages the single generated-server child process for this editor-server instance.
4
+ * This is a single-project-per-process tool (see AppConfig), so a module-level
5
+ * singleton is correct here — there is never more than one "current" server to run.
6
+ */
7
+ declare class ServerRunner extends EventEmitter {
8
+ private child;
9
+ private logBuffer;
10
+ get running(): boolean;
11
+ /**
12
+ * Stops any existing process and waits for it to actually exit before spawning the
13
+ * new one — starting immediately after `kill()` (without waiting) races the old
14
+ * process's port release against the new process's bind, crashing the new one with
15
+ * EADDRINUSE.
16
+ */
17
+ start(projectDir: string, serverPath: string): Promise<void>;
18
+ /** Kills the current process (if any) and resolves once it has actually exited. */
19
+ stop(): Promise<void>;
20
+ getBufferedLogs(): string[];
21
+ }
22
+ export declare const serverRunner: ServerRunner;
23
+ export {};
package/dist/runner.js ADDED
@@ -0,0 +1,65 @@
1
+ import { spawn } from "node:child_process";
2
+ import { EventEmitter } from "node:events";
3
+ const MAX_BUFFERED_LINES = 500;
4
+ /**
5
+ * Manages the single generated-server child process for this editor-server instance.
6
+ * This is a single-project-per-process tool (see AppConfig), so a module-level
7
+ * singleton is correct here — there is never more than one "current" server to run.
8
+ */
9
+ class ServerRunner extends EventEmitter {
10
+ child = null;
11
+ logBuffer = [];
12
+ get running() {
13
+ return this.child !== null;
14
+ }
15
+ /**
16
+ * Stops any existing process and waits for it to actually exit before spawning the
17
+ * new one — starting immediately after `kill()` (without waiting) races the old
18
+ * process's port release against the new process's bind, crashing the new one with
19
+ * EADDRINUSE.
20
+ */
21
+ async start(projectDir, serverPath) {
22
+ await this.stop();
23
+ this.logBuffer = [];
24
+ const child = spawn(process.execPath, [serverPath], {
25
+ cwd: projectDir,
26
+ stdio: ["ignore", "pipe", "pipe"],
27
+ });
28
+ this.child = child;
29
+ const onData = (chunk) => {
30
+ const line = chunk.toString();
31
+ this.logBuffer.push(line);
32
+ if (this.logBuffer.length > MAX_BUFFERED_LINES)
33
+ this.logBuffer.shift();
34
+ this.emit("log", line);
35
+ };
36
+ child.stdout?.on("data", onData);
37
+ child.stderr?.on("data", onData);
38
+ child.on("exit", (code) => {
39
+ // Fires asynchronously — by then `start()` may already have replaced `this.child`
40
+ // with a newer process. Only clear the reference if it's still this exact child,
41
+ // or a restart's new process gets wiped out from under it.
42
+ if (this.child === child)
43
+ this.child = null;
44
+ this.emit("exit", code);
45
+ });
46
+ child.on("error", (err) => {
47
+ onData(Buffer.from(`Failed to start server: ${err.message}\n`));
48
+ });
49
+ }
50
+ /** Kills the current process (if any) and resolves once it has actually exited. */
51
+ stop() {
52
+ const child = this.child;
53
+ if (!child)
54
+ return Promise.resolve();
55
+ return new Promise((resolve) => {
56
+ child.once("exit", () => resolve());
57
+ child.kill();
58
+ });
59
+ }
60
+ getBufferedLogs() {
61
+ return this.logBuffer;
62
+ }
63
+ }
64
+ export const serverRunner = new ServerRunner();
65
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAE/B;;;;GAIG;AACH,MAAM,YAAa,SAAQ,YAAY;IAC7B,KAAK,GAAwB,IAAI,CAAC;IAClC,SAAS,GAAa,EAAE,CAAC;IAEjC,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC;IAC7B,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,KAAK,CAAC,UAAkB,EAAE,UAAkB;QAChD,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;QAEpB,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,EAAE;YAClD,GAAG,EAAE,UAAU;YACf,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;SAClC,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QAEnB,MAAM,MAAM,GAAG,CAAC,KAAa,EAAE,EAAE;YAC/B,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;YAC9B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1B,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,kBAAkB;gBAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;YACvE,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACzB,CAAC,CAAC;QAEF,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACxB,kFAAkF;YAClF,iFAAiF;YACjF,2DAA2D;YAC3D,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK;gBAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAC5C,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACxB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,2BAA2B,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC;QAClE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,mFAAmF;IACnF,IAAI;QACF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,IAAI,CAAC,KAAK;YAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAErC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;YACpC,KAAK,CAAC,IAAI,EAAE,CAAC;QACf,CAAC,CAAC,CAAC;IACL,CAAC;IAED,eAAe;QACb,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;CACF;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,YAAY,EAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/server.js ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ import { buildApp } from "./app.js";
3
+ import { resolveProjectDir } from "./config.js";
4
+ import { loadInstalledPlugins } from "./plugin-loading.js";
5
+ import { ensurePluginReadme } from "./plugin-readme.js";
6
+ import { serverRunner } from "./runner.js";
7
+ const projectDir = resolveProjectDir();
8
+ const port = Number(process.env.PORT ?? 4000);
9
+ // Re-register any previously-installed plugin nodes (Phase 9 Part B) before the app is
10
+ // built, so they're already live for the very first GetNodeRegistry call. Deliberately kept
11
+ // here rather than inside buildApp()/app.ts: buildApp() is synchronous and constructed
12
+ // directly (without awaiting a plugin-load step) by every existing test in this package.
13
+ const { failed: failedPlugins } = await loadInstalledPlugins(projectDir);
14
+ for (const { file, error } of failedPlugins) {
15
+ console.warn(`Failed to load plugin "${file}": ${error}`);
16
+ }
17
+ // Scaffold README.PLUGIN.md (write-once) so every project — new or pre-existing — has the
18
+ // plugin-authoring guide available without the user having to go looking for it.
19
+ await ensurePluginReadme(projectDir);
20
+ const app = buildApp({ projectDir });
21
+ app.listen(port, () => {
22
+ console.log(`FlowServer editor running at http://localhost:${port}`);
23
+ console.log(`Project directory: ${projectDir}`);
24
+ });
25
+ // Never leave an orphaned generated-server process running after editor-server exits.
26
+ // `stop()`'s kill() call is synchronous even though the returned promise resolves later —
27
+ // no need to await it here, and process.on("exit") handlers can't run async work anyway.
28
+ for (const signal of ["SIGINT", "SIGTERM"]) {
29
+ process.on(signal, () => {
30
+ void serverRunner.stop();
31
+ process.exit(0);
32
+ });
33
+ }
34
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,UAAU,GAAG,iBAAiB,EAAE,CAAC;AACvC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;AAE9C,uFAAuF;AACvF,4FAA4F;AAC5F,uFAAuF;AACvF,yFAAyF;AACzF,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,oBAAoB,CAAC,UAAU,CAAC,CAAC;AACzE,KAAK,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,aAAa,EAAE,CAAC;IAC5C,OAAO,CAAC,IAAI,CAAC,0BAA0B,IAAI,MAAM,KAAK,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED,0FAA0F;AAC1F,iFAAiF;AACjF,MAAM,kBAAkB,CAAC,UAAU,CAAC,CAAC;AAErC,MAAM,GAAG,GAAG,QAAQ,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;AAErC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;IACpB,OAAO,CAAC,GAAG,CAAC,iDAAiD,IAAI,EAAE,CAAC,CAAC;IACrE,OAAO,CAAC,GAAG,CAAC,sBAAsB,UAAU,EAAE,CAAC,CAAC;AAClD,CAAC,CAAC,CAAC;AAEH,sFAAsF;AACtF,0FAA0F;AAC1F,yFAAyF;AACzF,KAAK,MAAM,MAAM,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAU,EAAE,CAAC;IACpD,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;QACtB,KAAK,YAAY,CAAC,IAAI,EAAE,CAAC;QACzB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { type Express } from "express";
2
+ /**
3
+ * Serves editor-ui's built assets if they exist. No-ops otherwise, so editor-server is
4
+ * independently runnable/testable before editor-ui has ever been built, and "just works"
5
+ * once assets appear later with zero changes to this package.
6
+ *
7
+ * Two candidate locations, checked in order: `public/` bundled alongside this compiled
8
+ * file (the published `visual-node` npm package case — see scripts/copy-ui-assets.mjs)
9
+ * and the monorepo-sibling `packages/editor-ui/dist` (local dev/test case, e.g. running
10
+ * `node dist/server.js` directly against a `pnpm -r build` output without publishing).
11
+ */
12
+ export declare function serveStatic(app: Express): void;
package/dist/static.js ADDED
@@ -0,0 +1,28 @@
1
+ import { existsSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import express from "express";
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ /**
7
+ * Serves editor-ui's built assets if they exist. No-ops otherwise, so editor-server is
8
+ * independently runnable/testable before editor-ui has ever been built, and "just works"
9
+ * once assets appear later with zero changes to this package.
10
+ *
11
+ * Two candidate locations, checked in order: `public/` bundled alongside this compiled
12
+ * file (the published `visual-node` npm package case — see scripts/copy-ui-assets.mjs)
13
+ * and the monorepo-sibling `packages/editor-ui/dist` (local dev/test case, e.g. running
14
+ * `node dist/server.js` directly against a `pnpm -r build` output without publishing).
15
+ */
16
+ export function serveStatic(app) {
17
+ const candidates = [path.resolve(__dirname, "public"), path.resolve(__dirname, "../../editor-ui/dist")];
18
+ const uiDist = candidates.find(existsSync);
19
+ if (!uiDist)
20
+ return;
21
+ app.use(express.static(uiDist));
22
+ app.get("*", (req, res, next) => {
23
+ if (req.path.startsWith("/api/"))
24
+ return next();
25
+ res.sendFile(path.join(uiDist, "index.html"));
26
+ });
27
+ }
28
+ //# sourceMappingURL=static.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static.js","sourceRoot":"","sources":["../src/static.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,OAAyB,MAAM,SAAS,CAAC;AAEhD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE/D;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CAAC,GAAY;IACtC,MAAM,UAAU,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,sBAAsB,CAAC,CAAC,CAAC;IACxG,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC3C,IAAI,CAAC,MAAM;QAAE,OAAO;IAEpB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAChC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC9B,IAAI,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;YAAE,OAAO,IAAI,EAAE,CAAC;QAChD,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "visual-node",
3
+ "version": "0.1.0",
4
+ "description": "Visual, node-based backend builder for Node.js. Drag-and-drop flows compile to real, readable Express.js source code.",
5
+ "type": "module",
6
+ "main": "./dist/server.js",
7
+ "types": "./dist/server.d.ts",
8
+ "bin": {
9
+ "visual-node": "./dist/server.js"
10
+ },
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "dependencies": {
15
+ "@connectrpc/connect": "^2.1.2",
16
+ "@connectrpc/connect-express": "^2.1.2",
17
+ "@connectrpc/connect-node": "^2.1.2",
18
+ "cors": "^2.8.5",
19
+ "express": "^4.19.2",
20
+ "@visual-node/core": "0.1.0",
21
+ "@visual-node/proto-gen": "0.1.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/cors": "^2.8.17",
25
+ "@types/express": "^4.17.21",
26
+ "@types/node": "^20.14.15",
27
+ "@types/supertest": "^6.0.2",
28
+ "supertest": "^7.0.0",
29
+ "tsx": "^4.16.5",
30
+ "typescript": "^5.5.4",
31
+ "vitest": "^2.0.5"
32
+ },
33
+ "scripts": {
34
+ "build": "tsc -p tsconfig.json && node ../../scripts/copy-ui-assets.mjs",
35
+ "dev": "tsx watch src/server.ts",
36
+ "start": "node dist/server.js",
37
+ "test": "vitest run"
38
+ }
39
+ }