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 @@
1
+ {"version":3,"file":"files.service.js","sourceRoot":"","sources":["../../src/connect/files.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { type ConnectRouter } from "@connectrpc/connect";
2
+ import type { AppConfig } from "../config.js";
3
+ /**
4
+ * Registers the `GetNodeRegistry`/`GetFlow`/`SaveFlow` RPCs from `EditorService` on a
5
+ * `ConnectRouter`, matching the behavior of the existing REST handlers exactly:
6
+ * - `GetNodeRegistry` mirrors `routes/nodes.routes.ts`'s `GET /api/node-registry`
7
+ * (including its `?scope=function-graph` branch).
8
+ * - `GetFlow`/`SaveFlow` mirror `routes/flow.routes.ts`'s `GET`/`POST /api/flow` — same
9
+ * `<projectDir>/flow.json` path, same on-disk JSON storage format (Phase 8 hasn't cut
10
+ * storage over to FlatBuffers yet, only the RPC wire format). A `Flow` crossing the RPC
11
+ * boundary is carried as the wire's opaque `flatbuffer_flow` bytes field, converted via
12
+ * `@visual-node/core`'s `encodeFlow`/`decodeFlow`.
13
+ */
14
+ export declare function registerNodeRegistryFlowRoutes(router: ConnectRouter, config: AppConfig): ConnectRouter;
@@ -0,0 +1,142 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { Code, ConnectError } from "@connectrpc/connect";
4
+ import { FUNCTION_GRAPH_NODE_DEFINITIONS, FUNCTION_GRAPH_ONLY_TYPES, decodeFlow, encodeFlow, listNodeDefinitions, } from "@visual-node/core";
5
+ import { EditorService } from "@visual-node/proto-gen";
6
+ import { isPlausibleFlow } from "../flow-shape.js";
7
+ // NOTE on message construction: handlers below return plain object literals shaped like
8
+ // the proto message's init shape (e.g. `{ found: true, flatbufferFlow: ... }`) rather than
9
+ // calling `@bufbuild/protobuf`'s `create()` themselves. `@bufbuild/protobuf` is only a
10
+ // *peer* dependency of `@connectrpc/connect` (not a direct dependency of
11
+ // `visual-node`'s package.json, which this task is not allowed to touch), so
12
+ // it isn't resolvable from this package's own imports. This is fine: Connect's own
13
+ // `invokeUnaryImplementation` (`@connectrpc/connect/dist/esm/protocol/normalize.js`) always
14
+ // runs every handler's return value through `create(method.output, returnValue)` internally
15
+ // before serializing, using its *own* bundled `@bufbuild/protobuf` — so a plain
16
+ // `MessageInitShape`-compatible object is exactly what a `UnaryImpl` is documented to
17
+ // return, no local `create()` call needed.
18
+ /** Converts a `ConfigField.default` (documented as "a string, number, boolean, or raw JS
19
+ * source string for 'code' fields") into a `google.protobuf.Value` init shape — a plain
20
+ * `{ kind: { case, value } }` oneof object, which Connect's internal `create()` call
21
+ * normalizes into a real `Value` message just like every other nested field here.
22
+ *
23
+ * Typed `any` rather than importing `MessageInit<Value>`: `@bufbuild/protobuf` is not a
24
+ * direct dependency of this package (see the NOTE above), so even a type-only import of it
25
+ * is unresolvable here. The runtime shape is verified against
26
+ * `@bufbuild/protobuf`'s `wkt/gen/google/protobuf/struct_pb.d.ts` `Value` oneof directly. */
27
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
28
+ function toProtoValueInit(value) {
29
+ if (value === undefined)
30
+ return undefined;
31
+ if (value === null)
32
+ return { kind: { case: "nullValue", value: 0 } };
33
+ if (typeof value === "string")
34
+ return { kind: { case: "stringValue", value } };
35
+ if (typeof value === "number")
36
+ return { kind: { case: "numberValue", value } };
37
+ if (typeof value === "boolean")
38
+ return { kind: { case: "boolValue", value } };
39
+ // Not expected per ConfigField.default's documented type, but fall back to a string
40
+ // representation rather than dropping the value silently.
41
+ return { kind: { case: "stringValue", value: JSON.stringify(value) } };
42
+ }
43
+ /** Mirrors nodes.routes.ts's `{ emit, ...rest }` destructure: strips the non-serializable
44
+ * `emit`/`resultIdentifier` function fields before the definition crosses the wire. */
45
+ function toProtoPort(port) {
46
+ return { id: port.id, label: port.label, kind: port.kind ?? "" };
47
+ }
48
+ function toProtoConfigField(field) {
49
+ return {
50
+ key: field.key,
51
+ label: field.label,
52
+ type: field.type,
53
+ options: field.options ?? [],
54
+ defaultValue: toProtoValueInit(field.default),
55
+ hint: field.hint ?? "",
56
+ };
57
+ }
58
+ function toProtoNodeDefinition(def) {
59
+ return {
60
+ type: def.type,
61
+ category: def.category,
62
+ label: def.label,
63
+ description: def.description,
64
+ inputs: def.inputs.map(toProtoPort),
65
+ outputs: def.outputs.map(toProtoPort),
66
+ configSchema: def.configSchema.map(toProtoConfigField),
67
+ };
68
+ }
69
+ /**
70
+ * Registers the `GetNodeRegistry`/`GetFlow`/`SaveFlow` RPCs from `EditorService` on a
71
+ * `ConnectRouter`, matching the behavior of the existing REST handlers exactly:
72
+ * - `GetNodeRegistry` mirrors `routes/nodes.routes.ts`'s `GET /api/node-registry`
73
+ * (including its `?scope=function-graph` branch).
74
+ * - `GetFlow`/`SaveFlow` mirror `routes/flow.routes.ts`'s `GET`/`POST /api/flow` — same
75
+ * `<projectDir>/flow.json` path, same on-disk JSON storage format (Phase 8 hasn't cut
76
+ * storage over to FlatBuffers yet, only the RPC wire format). A `Flow` crossing the RPC
77
+ * boundary is carried as the wire's opaque `flatbuffer_flow` bytes field, converted via
78
+ * `@visual-node/core`'s `encodeFlow`/`decodeFlow`.
79
+ */
80
+ export function registerNodeRegistryFlowRoutes(router, config) {
81
+ const flowPath = path.join(config.projectDir, "flow.json");
82
+ router.rpc(EditorService.method.getNodeRegistry, async (req) => {
83
+ // Plugin nodes are always shaped as either pure-value (no exec pins) or a single
84
+ // exec-in("in")/exec-out("out") pair (validatePluginNodeSpec enforces this — see
85
+ // Phase 9 notes in CLAUDE.md) — the same shape as the builtin operators/consoleLog/
86
+ // customCode types already offered inside a function body. So unlike
87
+ // FUNCTION_GRAPH_NODE_DEFINITIONS (a static list of builtins), plugins are appended
88
+ // live from the mutable registry rather than hardcoded, since they're registered at
89
+ // runtime via InstallPlugin and would otherwise never appear here.
90
+ const definitions = req.scope === "function-graph"
91
+ ? [
92
+ ...FUNCTION_GRAPH_NODE_DEFINITIONS,
93
+ ...listNodeDefinitions().filter((def) => def.type.startsWith("plugin.")),
94
+ ]
95
+ : listNodeDefinitions().filter((def) => !FUNCTION_GRAPH_ONLY_TYPES.has(def.type));
96
+ return { definitions: definitions.map(toProtoNodeDefinition) };
97
+ });
98
+ router.rpc(EditorService.method.getFlow, async () => {
99
+ let raw;
100
+ try {
101
+ raw = await readFile(flowPath, "utf8");
102
+ }
103
+ catch (err) {
104
+ if (err.code === "ENOENT") {
105
+ return { found: false };
106
+ }
107
+ throw err;
108
+ }
109
+ let flow;
110
+ try {
111
+ flow = JSON.parse(raw);
112
+ }
113
+ catch (err) {
114
+ throw new ConnectError(`flow.json is not valid JSON: ${err.message}`, Code.Internal);
115
+ }
116
+ let flatbufferFlow;
117
+ try {
118
+ flatbufferFlow = encodeFlow(flow);
119
+ }
120
+ catch (err) {
121
+ throw new ConnectError(`flow.json could not be encoded: ${err.message}`, Code.Internal);
122
+ }
123
+ return { found: true, flatbufferFlow };
124
+ });
125
+ router.rpc(EditorService.method.saveFlow, async (req) => {
126
+ let flow;
127
+ try {
128
+ flow = decodeFlow(req.flatbufferFlow);
129
+ }
130
+ catch (err) {
131
+ throw new ConnectError(`flatbuffer_flow could not be decoded: ${err.message}`, Code.InvalidArgument);
132
+ }
133
+ if (!isPlausibleFlow(flow)) {
134
+ throw new ConnectError("flatbuffer_flow must decode to a Flow with `nodes`, `edges`, and `meta`", Code.InvalidArgument);
135
+ }
136
+ await mkdir(config.projectDir, { recursive: true });
137
+ await writeFile(flowPath, JSON.stringify(flow, null, 2), "utf8");
138
+ return { ok: true };
139
+ });
140
+ return router;
141
+ }
142
+ //# sourceMappingURL=node-registry-flow.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-registry-flow.service.js","sourceRoot":"","sources":["../../src/connect/node-registry-flow.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { ConnectRouter } from "@connectrpc/connect";
2
+ import type { AppConfig } from "../config.js";
3
+ /**
4
+ * Registers the `InstallPlugin` RPC (Phase 9 Part B — JSON-based plugin node system, see
5
+ * packages/core/src/plugins/plugin-schema.ts and plugin-node.ts). Uploading a plugin is
6
+ * equivalent in trust level to writing a Custom Code node: the spec's `codegen`
7
+ * imports/setup/body strings are spliced verbatim into generated server code with no
8
+ * sandboxing — appropriate for a local single-developer tool.
9
+ *
10
+ * Always returns `{ ok: false, errors: [...] }` rather than throwing a `ConnectError` on any
11
+ * expected failure (malformed JSON, spec validation errors, type collision, unsafe path) —
12
+ * same "expected failure modeled as response data" pattern as
13
+ * ValidateFlowResponse/GenerateCodeResponse in connect/validate-generate.service.ts, rather
14
+ * than the `ConnectError`-throwing pattern connect/files.service.ts uses for its CRUD RPCs.
15
+ *
16
+ * On success, the validated spec is written to
17
+ * `<projectDir>/.flowserver/plugins/<sanitized-type>.node.json` (the leading dot directory
18
+ * means file-tree.ts's existing dotfile filter already hides it from the file explorer with
19
+ * zero changes there) and immediately `registerNode()`-ed — live for the very next
20
+ * `GetNodeRegistry` call, no restart needed. Re-uploading a `type` that's already registered
21
+ * (builtin or a previously installed plugin) is rejected outright: `registerNode()` has no
22
+ * update path in this codebase, so iterating on a plugin requires renaming its `type`.
23
+ */
24
+ export declare function registerPluginsRoutes(router: ConnectRouter, config: AppConfig): ConnectRouter;
@@ -0,0 +1,73 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { EditorService } from "@visual-node/proto-gen";
4
+ import { createPluginNodeDefinition, getNodeDefinition, registerNode, validatePluginNodeSpec, } from "@visual-node/core";
5
+ import { resolveSafePath } from "../path-safety.js";
6
+ /**
7
+ * Registers the `InstallPlugin` RPC (Phase 9 Part B — JSON-based plugin node system, see
8
+ * packages/core/src/plugins/plugin-schema.ts and plugin-node.ts). Uploading a plugin is
9
+ * equivalent in trust level to writing a Custom Code node: the spec's `codegen`
10
+ * imports/setup/body strings are spliced verbatim into generated server code with no
11
+ * sandboxing — appropriate for a local single-developer tool.
12
+ *
13
+ * Always returns `{ ok: false, errors: [...] }` rather than throwing a `ConnectError` on any
14
+ * expected failure (malformed JSON, spec validation errors, type collision, unsafe path) —
15
+ * same "expected failure modeled as response data" pattern as
16
+ * ValidateFlowResponse/GenerateCodeResponse in connect/validate-generate.service.ts, rather
17
+ * than the `ConnectError`-throwing pattern connect/files.service.ts uses for its CRUD RPCs.
18
+ *
19
+ * On success, the validated spec is written to
20
+ * `<projectDir>/.flowserver/plugins/<sanitized-type>.node.json` (the leading dot directory
21
+ * means file-tree.ts's existing dotfile filter already hides it from the file explorer with
22
+ * zero changes there) and immediately `registerNode()`-ed — live for the very next
23
+ * `GetNodeRegistry` call, no restart needed. Re-uploading a `type` that's already registered
24
+ * (builtin or a previously installed plugin) is rejected outright: `registerNode()` has no
25
+ * update path in this codebase, so iterating on a plugin requires renaming its `type`.
26
+ */
27
+ export function registerPluginsRoutes(router, config) {
28
+ router.rpc(EditorService.method.installPlugin, async (req) => {
29
+ let raw;
30
+ try {
31
+ raw = JSON.parse(Buffer.from(req.pluginJson).toString("utf8"));
32
+ }
33
+ catch (err) {
34
+ return {
35
+ ok: false,
36
+ type: "",
37
+ relativePath: "",
38
+ errors: [`\`plugin_json\` is not valid JSON: ${err instanceof Error ? err.message : String(err)}`],
39
+ };
40
+ }
41
+ const validationErrors = validatePluginNodeSpec(raw);
42
+ if (validationErrors.length > 0) {
43
+ return { ok: false, type: "", relativePath: "", errors: validationErrors };
44
+ }
45
+ // Safe: validatePluginNodeSpec(raw) returned no errors above.
46
+ const spec = raw;
47
+ if (getNodeDefinition(spec.type)) {
48
+ return {
49
+ ok: false,
50
+ type: "",
51
+ relativePath: "",
52
+ errors: [`Node type "${spec.type}" is already registered`],
53
+ };
54
+ }
55
+ const filename = `${spec.type.replace(/[^A-Za-z0-9_.-]/g, "_")}.node.json`;
56
+ const relativePath = `.flowserver/plugins/${filename}`;
57
+ const target = resolveSafePath(config.projectDir, relativePath);
58
+ if (!target) {
59
+ return {
60
+ ok: false,
61
+ type: "",
62
+ relativePath: "",
63
+ errors: [`Could not resolve a safe on-disk path for plugin type "${spec.type}"`],
64
+ };
65
+ }
66
+ await mkdir(path.dirname(target), { recursive: true });
67
+ await writeFile(target, JSON.stringify(spec, null, 2), "utf8");
68
+ registerNode(createPluginNodeDefinition(spec));
69
+ return { ok: true, type: spec.type, relativePath, errors: [] };
70
+ });
71
+ return router;
72
+ }
73
+ //# sourceMappingURL=plugins.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugins.service.js","sourceRoot":"","sources":["../../src/connect/plugins.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ConnectRouter } from "@connectrpc/connect";
2
+ import type { AppConfig } from "../config.js";
3
+ export declare function registerRunRoutes(router: ConnectRouter, config: AppConfig): ConnectRouter;
@@ -0,0 +1,159 @@
1
+ import path from "node:path";
2
+ import { EditorService } from "@visual-node/proto-gen";
3
+ import { collectProjectDependencies, writeGeneratedFile, } from "@visual-node/core";
4
+ import { compileProjectFromDisk, ensureCommonJsPackageJson, findEntryFile, nodeModulesInstalled, } from "../codegen-helpers.js";
5
+ import { serverRunner } from "../runner.js";
6
+ /**
7
+ * Connect RPC mirror of packages/editor-server/src/routes/run.routes.ts's four
8
+ * `/api/run/*` endpoints, reusing the exact same helpers (`compileProjectFromDisk`,
9
+ * `findEntryFile`, `ensureCommonJsPackageJson`, `nodeModulesInstalled`) and the same
10
+ * `serverRunner` singleton — this is a transport-only rewrite, not a behavior change.
11
+ *
12
+ * Deliberately no explicit `MessageInitShape`/proto type imports here: every RPC
13
+ * implementation below is passed to `router.rpc(EditorService.method.x, handler)`
14
+ * individually (NOT one `router.service(EditorService, {...})` call — see note below),
15
+ * so TypeScript contextually types each method's parameters and return value from
16
+ * `EditorService`'s own descriptor. That sidesteps importing anything from
17
+ * "@bufbuild/protobuf" directly, which this package cannot resolve on its own (it's only
18
+ * a transitive dependency here, reachable through `@visual-node/proto-gen`'s and
19
+ * `@connectrpc/connect`'s own node_modules, not this package's) — `@visual-node/proto-gen`'s
20
+ * generated `.d.ts` resolves it fine from its own directory, but a direct
21
+ * `import ... from "@bufbuild/protobuf"` written in this file would not resolve under
22
+ * pnpm's strict per-package node_modules.
23
+ *
24
+ * NOTE on `router.rpc()` vs `router.service()`: `router.service(EditorService, partial)`
25
+ * fills every method of `EditorService` NOT present in `partial` with an "unimplemented"
26
+ * stub handler and registers all of them (real + stubs) on the router. Since several other
27
+ * files in `src/connect/` also register a subset of `EditorService`'s methods on the same
28
+ * shared router, calling `.service()` here too would push "unimplemented" stubs for every
29
+ * RPC this file doesn't own — and `connect-express`'s middleware keys its route table by
30
+ * path in a plain `Map`, so whichever registration call runs last for a given path wins,
31
+ * silently clobbering another file's real implementation. `router.rpc()` registers exactly
32
+ * one method per call with no such fill-in behavior, which is what safely composes multiple
33
+ * per-group registration functions on one router.
34
+ */
35
+ /** Mirrors packages/core's ValidationError/ProjectFileError shapes into the proto ValidationError init shape (proto3 strings default to "", not undefined, for absent optional fields). */
36
+ function toProtoValidationError(err) {
37
+ return {
38
+ nodeId: err.nodeId ?? "",
39
+ blueprintNodeId: err.blueprintNodeId ?? "",
40
+ message: err.message,
41
+ relativePath: "relativePath" in err ? err.relativePath : "",
42
+ };
43
+ }
44
+ export function registerRunRoutes(router, config) {
45
+ /**
46
+ * Mirrors POST /api/run/start. The REST route's three failure shapes collapse into
47
+ * StartRunResponse's `result` oneof: 422 (invalid project) -> `validationFailure`
48
+ * (structured errors), 400 (no/multiple entry file) and 409 (deps not installed) ->
49
+ * the generic `error` string variant, success -> `started`.
50
+ */
51
+ async function startRun() {
52
+ const { sourceFiles, result } = await compileProjectFromDisk(config.projectDir);
53
+ if (!result.valid) {
54
+ return { result: { case: "validationFailure", value: { errors: result.errors.map(toProtoValidationError) } } };
55
+ }
56
+ const entry = findEntryFile(sourceFiles);
57
+ if ("error" in entry) {
58
+ return { result: { case: "error", value: entry.error } };
59
+ }
60
+ const entryIndex = sourceFiles.indexOf(entry);
61
+ const entryOutputPath = result.files[entryIndex].relativePath;
62
+ for (const file of result.files) {
63
+ await writeGeneratedFile(path.join(config.projectDir, file.relativePath), file.code);
64
+ }
65
+ const { dependencies } = collectProjectDependencies(sourceFiles);
66
+ await ensureCommonJsPackageJson(config.projectDir, path.basename(config.projectDir) || "flowserver-app", {
67
+ dependencies,
68
+ });
69
+ const { installed, missing } = await nodeModulesInstalled(config.projectDir);
70
+ if (!installed) {
71
+ return {
72
+ result: {
73
+ case: "error",
74
+ value: `Dependencies not installed (missing: ${missing.join(", ")}). Run "npm install" in ${config.projectDir}, then try again.`,
75
+ },
76
+ };
77
+ }
78
+ const serverPath = path.join(config.projectDir, entryOutputPath);
79
+ await serverRunner.start(config.projectDir, serverPath);
80
+ return { result: { case: "started", value: { running: true } } };
81
+ }
82
+ /** Mirrors POST /api/run/stop. */
83
+ async function stopRun() {
84
+ await serverRunner.stop();
85
+ return { running: false };
86
+ }
87
+ /** Mirrors GET /api/run/status. */
88
+ async function getRunStatus() {
89
+ return { running: serverRunner.running };
90
+ }
91
+ /**
92
+ * Mirrors GET /api/run/logs (SSE): replays up to 500 buffered lines, then streams
93
+ * live "log"/"exit" events off `serverRunner`'s EventEmitter as they happen, forever
94
+ * (mirroring the SSE route, which never closes the connection itself — only the
95
+ * client disconnecting ends it).
96
+ *
97
+ * Cancellation: a Connect server-streaming handler's second argument is a
98
+ * `HandlerContext` whose `signal: AbortSignal` fires when the client disconnects or
99
+ * the call otherwise ends — this is the Connect-idiomatic replacement for the SSE
100
+ * route's `req.on("close", ...)`. We listen for `context.signal`'s "abort" event to
101
+ * unblock the generator's wait-for-next-event promise, and unregister the
102
+ * `serverRunner` "log"/"exit" listeners in a `finally` block so cleanup also runs
103
+ * when the consumer stops pulling and the generator is `.return()`-ed instead of
104
+ * aborted (the two ways this loop can end).
105
+ */
106
+ async function* runLogs(_req, context) {
107
+ for (const line of serverRunner.getBufferedLogs()) {
108
+ yield { event: { case: "log", value: line } };
109
+ }
110
+ const queue = [];
111
+ let wake = null;
112
+ let done = false;
113
+ const push = (item) => {
114
+ queue.push(item);
115
+ if (wake) {
116
+ const resolve = wake;
117
+ wake = null;
118
+ resolve();
119
+ }
120
+ };
121
+ const onLog = (line) => push({ event: { case: "log", value: line } });
122
+ const onExit = (code) => push({ event: { case: "exit", value: { code: code ?? undefined } } });
123
+ const onAbort = () => {
124
+ done = true;
125
+ if (wake) {
126
+ const resolve = wake;
127
+ wake = null;
128
+ resolve();
129
+ }
130
+ };
131
+ serverRunner.on("log", onLog);
132
+ serverRunner.on("exit", onExit);
133
+ context.signal.addEventListener("abort", onAbort);
134
+ try {
135
+ while (!done) {
136
+ if (queue.length > 0) {
137
+ yield queue.shift();
138
+ continue;
139
+ }
140
+ if (context.signal.aborted)
141
+ break;
142
+ await new Promise((resolve) => {
143
+ wake = resolve;
144
+ });
145
+ }
146
+ }
147
+ finally {
148
+ serverRunner.off("log", onLog);
149
+ serverRunner.off("exit", onExit);
150
+ context.signal.removeEventListener("abort", onAbort);
151
+ }
152
+ }
153
+ router.rpc(EditorService.method.startRun, startRun);
154
+ router.rpc(EditorService.method.stopRun, stopRun);
155
+ router.rpc(EditorService.method.getRunStatus, getRunStatus);
156
+ router.rpc(EditorService.method.runLogs, runLogs);
157
+ return router;
158
+ }
159
+ //# sourceMappingURL=run.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.service.js","sourceRoot":"","sources":["../../src/connect/run.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { type ConnectRouter } from "@connectrpc/connect";
2
+ import type { AppConfig } from "../config.js";
3
+ /**
4
+ * Connect RPC implementations for ValidateFlow, GenerateCode, and WriteGeneratedCode —
5
+ * the Connect-transport equivalents of packages/editor-server/src/routes/validate.routes.ts
6
+ * (POST /api/validate) and packages/editor-server/src/routes/generate.routes.ts (POST
7
+ * /api/generate, POST /api/generate/write). Business logic is not duplicated: this file
8
+ * calls the exact same `validateFlow`/`compile`/`writeGeneratedFile`/
9
+ * `ensureCommonJsPackageJson` helpers the REST routes call, only the transport
10
+ * (Connect unary RPC instead of an Express JSON body) and the `Flow` wire format
11
+ * (FlatBuffers-encoded `bytes flatbuffer_flow` instead of a JSON `req.body.flow`) differ.
12
+ *
13
+ * Registered as individual `router.rpc(...)` calls rather than one `router.service(...)`
14
+ * call, since this only implements 3 of EditorService's ~21 methods — `router.service()`
15
+ * would register "unimplemented" stubs for every method this file doesn't own, clobbering
16
+ * whatever another service-group file registers for those methods when both are wired into
17
+ * the same router during final app.ts integration.
18
+ */
19
+ export declare function registerValidateGenerateRoutes(router: ConnectRouter, config: AppConfig): ConnectRouter;
@@ -0,0 +1,102 @@
1
+ import path from "node:path";
2
+ import { ConnectError, Code } from "@connectrpc/connect";
3
+ import { collectFlowDependencies, decodeFlow, validateFlow as validateFlowCore, writeGeneratedFile, } from "@visual-node/core";
4
+ import { EditorService, } from "@visual-node/proto-gen";
5
+ import { compile, ensureCommonJsPackageJson } from "../codegen-helpers.js";
6
+ /**
7
+ * Connect RPC implementations for ValidateFlow, GenerateCode, and WriteGeneratedCode —
8
+ * the Connect-transport equivalents of packages/editor-server/src/routes/validate.routes.ts
9
+ * (POST /api/validate) and packages/editor-server/src/routes/generate.routes.ts (POST
10
+ * /api/generate, POST /api/generate/write). Business logic is not duplicated: this file
11
+ * calls the exact same `validateFlow`/`compile`/`writeGeneratedFile`/
12
+ * `ensureCommonJsPackageJson` helpers the REST routes call, only the transport
13
+ * (Connect unary RPC instead of an Express JSON body) and the `Flow` wire format
14
+ * (FlatBuffers-encoded `bytes flatbuffer_flow` instead of a JSON `req.body.flow`) differ.
15
+ *
16
+ * Registered as individual `router.rpc(...)` calls rather than one `router.service(...)`
17
+ * call, since this only implements 3 of EditorService's ~21 methods — `router.service()`
18
+ * would register "unimplemented" stubs for every method this file doesn't own, clobbering
19
+ * whatever another service-group file registers for those methods when both are wired into
20
+ * the same router during final app.ts integration.
21
+ */
22
+ export function registerValidateGenerateRoutes(router, config) {
23
+ router.rpc(EditorService.method.validateFlow, (req) => validateFlowRpc(req));
24
+ router.rpc(EditorService.method.generateCode, (req) => generateCodeRpc(req));
25
+ router.rpc(EditorService.method.writeGeneratedCode, (req) => writeGeneratedCodeRpc(req, config));
26
+ return router;
27
+ }
28
+ /**
29
+ * Decodes the request's opaque FlatBuffers-encoded `Flow`. A malformed/corrupt byte payload
30
+ * is a client-supplied-garbage problem, not an expected "flow is invalid" outcome the UI
31
+ * displays inline — so it's surfaced as a `ConnectError` (InvalidArgument), not folded into
32
+ * the `valid`/`errors` response fields the way `validateFlow()`'s structural errors are.
33
+ */
34
+ function decodeFlowOrThrow(bytes) {
35
+ try {
36
+ return decodeFlow(bytes);
37
+ }
38
+ catch (err) {
39
+ throw new ConnectError(`Request's flatbuffer_flow could not be decoded: ${err instanceof Error ? err.message : String(err)}`, Code.InvalidArgument);
40
+ }
41
+ }
42
+ function toProtoValidationError(e) {
43
+ return {
44
+ $typeName: "flowserver.v1.ValidationError",
45
+ nodeId: e.nodeId ?? "",
46
+ blueprintNodeId: e.blueprintNodeId ?? "",
47
+ message: e.message,
48
+ relativePath: "",
49
+ };
50
+ }
51
+ async function validateFlowRpc(req) {
52
+ const flow = decodeFlowOrThrow(req.flatbufferFlow);
53
+ const result = validateFlowCore(flow);
54
+ return {
55
+ $typeName: "flowserver.v1.ValidateFlowResponse",
56
+ valid: result.valid,
57
+ errors: result.errors.map(toProtoValidationError),
58
+ };
59
+ }
60
+ async function generateCodeRpc(req) {
61
+ const flow = decodeFlowOrThrow(req.flatbufferFlow);
62
+ const result = await compile(flow);
63
+ if (!result.valid) {
64
+ return {
65
+ $typeName: "flowserver.v1.GenerateCodeResponse",
66
+ valid: false,
67
+ code: "",
68
+ errors: result.errors.map(toProtoValidationError),
69
+ };
70
+ }
71
+ return {
72
+ $typeName: "flowserver.v1.GenerateCodeResponse",
73
+ valid: true,
74
+ code: result.code,
75
+ errors: [],
76
+ };
77
+ }
78
+ async function writeGeneratedCodeRpc(req, config) {
79
+ const flow = decodeFlowOrThrow(req.flatbufferFlow);
80
+ const result = await compile(flow);
81
+ if (!result.valid) {
82
+ return {
83
+ $typeName: "flowserver.v1.WriteGeneratedCodeResponse",
84
+ valid: false,
85
+ written: false,
86
+ path: "",
87
+ errors: result.errors.map(toProtoValidationError),
88
+ };
89
+ }
90
+ const serverPath = path.join(config.projectDir, "server.js");
91
+ await writeGeneratedFile(serverPath, result.code);
92
+ const { dependencies } = collectFlowDependencies(flow);
93
+ await ensureCommonJsPackageJson(config.projectDir, flow.meta?.name ?? "flowserver-app", { dependencies });
94
+ return {
95
+ $typeName: "flowserver.v1.WriteGeneratedCodeResponse",
96
+ valid: true,
97
+ written: true,
98
+ path: serverPath,
99
+ errors: [],
100
+ };
101
+ }
102
+ //# sourceMappingURL=validate-generate.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-generate.service.js","sourceRoot":"","sources":["../../src/connect/validate-generate.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ export interface FileTreeFileNode {
2
+ type: "file";
3
+ name: string;
4
+ relativePath: string;
5
+ }
6
+ export interface FileTreeFolderNode {
7
+ type: "folder";
8
+ name: string;
9
+ relativePath: string;
10
+ children: FileTreeNode[];
11
+ }
12
+ export type FileTreeNode = FileTreeFileNode | FileTreeFolderNode;
13
+ /**
14
+ * Lists the whole project directory as a tree, excluding node_modules/.git/dotfiles.
15
+ * Shows *all* remaining files (not just `.blueprint`) so the tree reflects the real
16
+ * directory — the UI only treats `.blueprint` files as openable. Folders sort before
17
+ * files, alphabetical within each group.
18
+ */
19
+ export declare function listTree(projectDir: string): Promise<FileTreeNode[]>;
20
+ /** Flat list of every ".blueprint" file's relative path, for /api/compile. */
21
+ export declare function listBlueprintFiles(projectDir: string): Promise<{
22
+ relativePath: string;
23
+ }[]>;