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.
- package/README.md +42 -0
- package/dist/app.d.ts +3 -0
- package/dist/app.js +44 -0
- package/dist/app.js.map +1 -0
- package/dist/codegen-helpers.d.ts +55 -0
- package/dist/codegen-helpers.js +142 -0
- package/dist/codegen-helpers.js.map +1 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.js +13 -0
- package/dist/config.js.map +1 -0
- package/dist/connect/compile-function-graph.service.d.ts +14 -0
- package/dist/connect/compile-function-graph.service.js +106 -0
- package/dist/connect/compile-function-graph.service.js.map +1 -0
- package/dist/connect/files.service.d.ts +7 -0
- package/dist/connect/files.service.js +175 -0
- package/dist/connect/files.service.js.map +1 -0
- package/dist/connect/node-registry-flow.service.d.ts +14 -0
- package/dist/connect/node-registry-flow.service.js +142 -0
- package/dist/connect/node-registry-flow.service.js.map +1 -0
- package/dist/connect/plugins.service.d.ts +24 -0
- package/dist/connect/plugins.service.js +73 -0
- package/dist/connect/plugins.service.js.map +1 -0
- package/dist/connect/run.service.d.ts +3 -0
- package/dist/connect/run.service.js +159 -0
- package/dist/connect/run.service.js.map +1 -0
- package/dist/connect/validate-generate.service.d.ts +19 -0
- package/dist/connect/validate-generate.service.js +102 -0
- package/dist/connect/validate-generate.service.js.map +1 -0
- package/dist/file-tree.d.ts +23 -0
- package/dist/file-tree.js +63 -0
- package/dist/file-tree.js.map +1 -0
- package/dist/flow-shape.d.ts +8 -0
- package/dist/flow-shape.js +13 -0
- package/dist/flow-shape.js.map +1 -0
- package/dist/path-safety.d.ts +12 -0
- package/dist/path-safety.js +26 -0
- package/dist/path-safety.js.map +1 -0
- package/dist/plugin-loading.d.ts +18 -0
- package/dist/plugin-loading.js +47 -0
- package/dist/plugin-loading.js.map +1 -0
- package/dist/plugin-readme.d.ts +9 -0
- package/dist/plugin-readme.js +235 -0
- package/dist/plugin-readme.js.map +1 -0
- package/dist/public/assets/index-6_2vDtnd.css +1 -0
- package/dist/public/assets/index-BpXY8lVq.js +101 -0
- package/dist/public/index.html +13 -0
- package/dist/runner.d.ts +23 -0
- package/dist/runner.js +65 -0
- package/dist/runner.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +34 -0
- package/dist/server.js.map +1 -0
- package/dist/static.d.ts +12 -0
- package/dist/static.js +28 -0
- package/dist/static.js.map +1 -0
- 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,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
|
+
}[]>;
|