@timo9378/flow2code 0.1.9 → 0.2.1
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/CHANGELOG.md +28 -0
- package/README.md +15 -2
- package/dist/cli.js +74 -7
- package/dist/compiler.cjs +32 -2
- package/dist/compiler.d.cts +6 -0
- package/dist/compiler.d.ts +6 -0
- package/dist/compiler.js +32 -2
- package/dist/server.d.ts +1 -1
- package/dist/server.js +1227 -1010
- package/out/404.html +1 -1
- package/out/__next.__PAGE__.txt +4 -4
- package/out/__next._full.txt +13 -13
- package/out/__next._head.txt +4 -4
- package/out/__next._index.txt +6 -6
- package/out/__next._tree.txt +2 -2
- package/out/_next/static/chunks/05328cd26bdc795c.js +176 -0
- package/out/_next/static/chunks/06e01c846ae01892.js +1 -0
- package/out/_next/static/chunks/1011f174944c0ca2.js +70 -0
- package/out/_next/static/chunks/1570e9ba5f1b44ed.js +5 -0
- package/out/_next/static/chunks/6167fccccde2e675.css +1 -0
- package/out/_next/static/chunks/{b112c2f519e4b429.js → 7cd04052abfadac1.js} +1 -1
- package/out/_next/static/chunks/8091c1216a95d294.js +1 -0
- package/out/_next/static/chunks/98d53aae29c36c6b.js +1 -0
- package/out/_next/static/chunks/a6dad97d9634a72d.js.map +1 -1
- package/out/_next/static/chunks/{b163b5d7cccbcf42.js → b05daf00cdc6058f.js} +1 -1
- package/out/_next/static/chunks/b3419ee3e3a616d9.js +1 -0
- package/out/_next/static/chunks/{acf223168ac429f7.js → be40d79540010a0d.js} +1 -1
- package/out/_next/static/chunks/{turbopack-576234c945ffdc44.js → turbopack-9da9810f42c97265.js} +1 -1
- package/out/_not-found/__next._full.txt +11 -11
- package/out/_not-found/__next._head.txt +4 -4
- package/out/_not-found/__next._index.txt +6 -6
- package/out/_not-found/__next._not-found/__PAGE__.txt +2 -2
- package/out/_not-found/__next._not-found.txt +3 -3
- package/out/_not-found/__next._tree.txt +2 -2
- package/out/_not-found.html +1 -1
- package/out/_not-found.txt +11 -11
- package/out/index.html +2 -2
- package/out/index.txt +13 -13
- package/package.json +130 -124
- package/out/_next/static/chunks/0bc0a50347ee5f3c.js +0 -51
- package/out/_next/static/chunks/58bf94a9d7047ec0.js +0 -125
- package/out/_next/static/chunks/6b84376656bd9887.js +0 -1
- package/out/_next/static/chunks/83ab8820627f8bfe.css +0 -1
- package/out/_next/static/chunks/ab8888d4b78b94be.js +0 -5
- package/out/_next/static/chunks/b6e8711267bccbbd.js +0 -1
- package/out/_next/static/chunks/fbca595129527827.js +0 -1
- package/scripts/publish-all.sh +0 -56
- /package/out/_next/static/{Ma0MmC8j1mxpQbtLwNajF → dSp6-CaehEDKVU9OSJABu}/_buildManifest.js +0 -0
- /package/out/_next/static/{Ma0MmC8j1mxpQbtLwNajF → dSp6-CaehEDKVU9OSJABu}/_clientMiddlewareManifest.json +0 -0
- /package/out/_next/static/{Ma0MmC8j1mxpQbtLwNajF → dSp6-CaehEDKVU9OSJABu}/_ssgManifest.js +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,34 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
7
|
|
|
8
|
+
## [0.2.1] — 2026-03-05
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Unified API handlers** — All 4 Next.js API routes (`/api/compile`, `/api/decompile`, `/api/generate`, `/api/import-openapi`) are now thin wrappers delegating to `src/server/handlers.ts`, eliminating duplicated logic and drift risk.
|
|
12
|
+
- **Bundle size 97% reduction** — `server.js` 5.5MB → 145KB, `cli.js` 5.5MB → 191KB. Root cause was Prettier (not ts-morph) being inlined; externalized in `tsup.config.ts`.
|
|
13
|
+
- **OpenAPI tag-based filtering** — `/api/import-openapi` tag filter now works correctly (previously was a `return true` TODO stub). Fixed by delegating to `handlers.ts` which has a proper implementation checking `flow.meta.tags`.
|
|
14
|
+
|
|
15
|
+
## [0.2.0] — 2026-03-05
|
|
16
|
+
|
|
17
|
+
### Added — VSCode Extension (`vscode-extension/`)
|
|
18
|
+
- **Right-click Decompile** — Right-click any `.ts`/`.js` file (or selection) → "Flow2Code: Decompile to Flow IR" generates `.flow.json` side-by-side with confidence score
|
|
19
|
+
- **Right-click Compile** — Right-click any `.flow.json` → "Flow2Code: Compile to TypeScript" with configurable platform (Next.js / Express / Cloudflare)
|
|
20
|
+
- **Flow Preview** — SVG-based DAG visualization with pan, zoom, fit-to-view, category-colored nodes, and hover tooltips
|
|
21
|
+
- **Auto-Validation Diagnostics** — Inline errors/warnings on open and save for `.flow.json` files, positioned at the offending node/edge in JSON
|
|
22
|
+
- **Custom Editor** — "Open With… > Flow2Code Visual Editor" provides read-only graphical view of `.flow.json` files
|
|
23
|
+
- **Status Bar** — Shows flow name and node/edge count (`$(graph) MyFlow (5N·4E)`) when a `.flow.json` is active; click to preview
|
|
24
|
+
- **Configurable Settings** — `flow2code.platform`, `flow2code.autoValidate`, `flow2code.compileOnSave`
|
|
25
|
+
- **esbuild Bundling** — Self-contained 6.3MB bundle with `@/` alias plugin resolving to main project source
|
|
26
|
+
|
|
27
|
+
### Added — Playwright E2E Testing
|
|
28
|
+
- **20 Playwright E2E tests** — Smoke tests, Node Operations, Toolbar Actions, API Compile Endpoint
|
|
29
|
+
- **Playwright config** — Chromium browser with auto-start dev server on port 3000
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
- **Infinite re-render loop** — `FlowNode` badge selector `?? []` created a new array on every store update, causing infinite React setState loop with React Flow dimension measurements. Fixed by using a stable empty array constant.
|
|
33
|
+
- **`useFlowLint` unconditional updates** — Lint hook always called `setNodeBadges()` even when results hadn't changed, feeding the re-render loop. Added shallow comparison before updating.
|
|
34
|
+
- **Platform registration tree-shaking** — Turbopack tree-shook barrel file side effects, causing "Unknown platform" errors in API routes. Fixed by registering platforms directly in `compiler.ts`.
|
|
35
|
+
|
|
8
36
|
## [0.1.9] — 2026-03-05
|
|
9
37
|
|
|
10
38
|
### Fixed
|
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
| Platform Adapters | NextjsPlatform / ExpressPlatform / CloudflarePlatform |
|
|
54
54
|
| Plugin System | `createPluginRegistry()` factory (per-instance) |
|
|
55
55
|
| CLI | Commander.js + Chokidar |
|
|
56
|
-
| Testing | Vitest 4 —
|
|
56
|
+
| Testing | Vitest 4 — 413 tests across 33 test files + 20 Playwright E2E tests |
|
|
57
57
|
| CI | GitHub Actions (Node 20/22 matrix) |
|
|
58
58
|
|
|
59
59
|
## Quick Start
|
|
@@ -185,7 +185,7 @@ flow2code/
|
|
|
185
185
|
│ │ └── storage/ # .flow.json split/merge
|
|
186
186
|
│ ├── cli/ # CLI (compile/watch/init)
|
|
187
187
|
│ └── server/ # Standalone HTTP Server
|
|
188
|
-
├── tests/ #
|
|
188
|
+
├── tests/ # 413 unit tests + 20 E2E tests
|
|
189
189
|
├── .github/workflows/ci.yml # GitHub Actions CI
|
|
190
190
|
├── CONTRIBUTING.md
|
|
191
191
|
└── vitest.config.ts
|
|
@@ -225,6 +225,19 @@ See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
|
225
225
|
|
|
226
226
|
For detailed usage examples and workflows, see [USAGE.md](USAGE.md).
|
|
227
227
|
|
|
228
|
+
## VS Code Extension
|
|
229
|
+
|
|
230
|
+
Flow2Code ships a companion VS Code extension under `vscode-extension/`:
|
|
231
|
+
|
|
232
|
+
- **Right-click Decompile** — Decompile any `.ts`/`.js` to a `.flow.json` visual IR
|
|
233
|
+
- **Right-click Compile** — Compile `.flow.json` to TypeScript with platform selection
|
|
234
|
+
- **Flow Preview** — SVG-based DAG visualization with pan, zoom, category coloring
|
|
235
|
+
- **Auto-Validation** — Inline diagnostics for `.flow.json` on open/save
|
|
236
|
+
- **Custom Editor** — "Open With… > Flow2Code Visual Editor" for graphical view
|
|
237
|
+
- **Status Bar** — Shows node/edge count; click to preview
|
|
238
|
+
|
|
239
|
+
See [vscode-extension/README.md](vscode-extension/README.md) for details.
|
|
240
|
+
|
|
228
241
|
## License
|
|
229
242
|
|
|
230
243
|
MIT
|
package/dist/cli.js
CHANGED
|
@@ -205,14 +205,38 @@ function validateFlowIR(ir) {
|
|
|
205
205
|
});
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
|
+
for (const edge of workingIR.edges) {
|
|
209
|
+
const sourceNode = workingNodeMap.get(edge.sourceNodeId);
|
|
210
|
+
const targetNode = workingNodeMap.get(edge.targetNodeId);
|
|
211
|
+
if (!sourceNode || !targetNode) continue;
|
|
212
|
+
const sourcePort = sourceNode.outputs?.find((p) => p.id === edge.sourcePortId);
|
|
213
|
+
const targetPort = targetNode.inputs?.find((p) => p.id === edge.targetPortId);
|
|
214
|
+
if (!sourcePort || !targetPort) continue;
|
|
215
|
+
if (!isTypeCompatible(sourcePort.dataType, targetPort.dataType)) {
|
|
216
|
+
errors.push({
|
|
217
|
+
code: "TYPE_MISMATCH",
|
|
218
|
+
message: `Type mismatch on edge "${edge.id}": output "${sourcePort.label}" (${sourcePort.dataType}) \u2192 input "${targetPort.label}" (${targetPort.dataType})`,
|
|
219
|
+
edgeId: edge.id,
|
|
220
|
+
severity: "warning"
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
208
224
|
return {
|
|
209
|
-
valid: errors.length === 0,
|
|
225
|
+
valid: errors.filter((e) => e.severity !== "warning" && e.severity !== "info").length === 0,
|
|
210
226
|
errors,
|
|
211
227
|
migrated,
|
|
212
228
|
migratedIR: migrated ? workingIR : void 0,
|
|
213
229
|
migrationLog
|
|
214
230
|
};
|
|
215
231
|
}
|
|
232
|
+
function isTypeCompatible(source, target) {
|
|
233
|
+
if (source === target) return true;
|
|
234
|
+
if (source === "any" || target === "any") return true;
|
|
235
|
+
if (target === "object" && source === "array") return true;
|
|
236
|
+
if (target === "string" && source === "number") return true;
|
|
237
|
+
if (target === "number" && source === "string") return true;
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
216
240
|
function detectCycles(nodes, edges) {
|
|
217
241
|
const errors = [];
|
|
218
242
|
const adjacency = /* @__PURE__ */ new Map();
|
|
@@ -1077,6 +1101,9 @@ var init_platforms = __esm({
|
|
|
1077
1101
|
"src/lib/compiler/platforms/index.ts"() {
|
|
1078
1102
|
"use strict";
|
|
1079
1103
|
init_types2();
|
|
1104
|
+
init_nextjs();
|
|
1105
|
+
init_express();
|
|
1106
|
+
init_cloudflare();
|
|
1080
1107
|
init_types2();
|
|
1081
1108
|
init_nextjs();
|
|
1082
1109
|
init_express();
|
|
@@ -1838,6 +1865,13 @@ var init_symbol_table = __esm({
|
|
|
1838
1865
|
});
|
|
1839
1866
|
|
|
1840
1867
|
// src/lib/compiler/compiler.ts
|
|
1868
|
+
var compiler_exports = {};
|
|
1869
|
+
__export(compiler_exports, {
|
|
1870
|
+
compile: () => compile,
|
|
1871
|
+
compileWithPrettier: () => compileWithPrettier,
|
|
1872
|
+
formatWithPrettier: () => formatWithPrettier,
|
|
1873
|
+
traceLineToNode: () => traceLineToNode
|
|
1874
|
+
});
|
|
1841
1875
|
import { Project } from "ts-morph";
|
|
1842
1876
|
function compile(ir, options) {
|
|
1843
1877
|
const pluginRegistry = createPluginRegistry();
|
|
@@ -1916,7 +1950,7 @@ function compile(ir, options) {
|
|
|
1916
1950
|
indentSize: 2,
|
|
1917
1951
|
convertTabsToSpaces: true
|
|
1918
1952
|
});
|
|
1919
|
-
|
|
1953
|
+
let code = sourceFile.getFullText();
|
|
1920
1954
|
const filePath = platform.getOutputFilePath(trigger);
|
|
1921
1955
|
collectRequiredPackages(workingIR, context);
|
|
1922
1956
|
const sourceMap = buildSourceMap(code, workingIR, filePath);
|
|
@@ -1933,6 +1967,28 @@ function compile(ir, options) {
|
|
|
1933
1967
|
sourceMap
|
|
1934
1968
|
};
|
|
1935
1969
|
}
|
|
1970
|
+
async function formatWithPrettier(code, options) {
|
|
1971
|
+
try {
|
|
1972
|
+
const prettier = await import("prettier");
|
|
1973
|
+
return await prettier.format(code, {
|
|
1974
|
+
parser: "typescript",
|
|
1975
|
+
printWidth: options?.printWidth ?? 100,
|
|
1976
|
+
tabWidth: 2,
|
|
1977
|
+
singleQuote: options?.singleQuote ?? true,
|
|
1978
|
+
trailingComma: "all",
|
|
1979
|
+
semi: true
|
|
1980
|
+
});
|
|
1981
|
+
} catch {
|
|
1982
|
+
return code;
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
async function compileWithPrettier(ir, options) {
|
|
1986
|
+
const result = compile(ir, options);
|
|
1987
|
+
if (!result.success || !result.code) return result;
|
|
1988
|
+
const formatted = await formatWithPrettier(result.code);
|
|
1989
|
+
const sourceMap = result.sourceMap ? buildSourceMap(formatted, ir, result.filePath ?? "formatted.ts") : void 0;
|
|
1990
|
+
return { ...result, code: formatted, sourceMap };
|
|
1991
|
+
}
|
|
1936
1992
|
function generateCode(sourceFile, trigger, context) {
|
|
1937
1993
|
const { platform } = context;
|
|
1938
1994
|
platform.generateImports(sourceFile, trigger, {
|
|
@@ -2350,6 +2406,9 @@ var init_compiler = __esm({
|
|
|
2350
2406
|
init_builtin();
|
|
2351
2407
|
init_type_inference();
|
|
2352
2408
|
init_symbol_table();
|
|
2409
|
+
registerPlatform("nextjs", () => new NextjsPlatform());
|
|
2410
|
+
registerPlatform("express", () => new ExpressPlatform());
|
|
2411
|
+
registerPlatform("cloudflare", () => new CloudflarePlatform());
|
|
2353
2412
|
CONTROL_FLOW_PORT_MAP = {
|
|
2354
2413
|
["if_else" /* IF_ELSE */]: /* @__PURE__ */ new Set(["true", "false"]),
|
|
2355
2414
|
["for_loop" /* FOR_LOOP */]: /* @__PURE__ */ new Set(["body"]),
|
|
@@ -3740,7 +3799,7 @@ Return ONLY valid JSON (no markdown, no explanation). The JSON must conform to t
|
|
|
3740
3799
|
// src/server/handlers.ts
|
|
3741
3800
|
import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
3742
3801
|
import { join as join2, dirname as dirname2, resolve } from "path";
|
|
3743
|
-
function handleCompile(body, projectRoot) {
|
|
3802
|
+
async function handleCompile(body, projectRoot) {
|
|
3744
3803
|
try {
|
|
3745
3804
|
const ir = body.ir;
|
|
3746
3805
|
const shouldWrite = body.write !== false;
|
|
@@ -3754,8 +3813,16 @@ function handleCompile(body, projectRoot) {
|
|
|
3754
3813
|
body: { success: false, error: result.errors?.join("\n") }
|
|
3755
3814
|
};
|
|
3756
3815
|
}
|
|
3816
|
+
let finalCode = result.code;
|
|
3817
|
+
if (finalCode) {
|
|
3818
|
+
try {
|
|
3819
|
+
const { formatWithPrettier: formatWithPrettier2 } = await Promise.resolve().then(() => (init_compiler(), compiler_exports));
|
|
3820
|
+
finalCode = await formatWithPrettier2(finalCode);
|
|
3821
|
+
} catch {
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3757
3824
|
let writtenPath = null;
|
|
3758
|
-
if (shouldWrite && result.filePath &&
|
|
3825
|
+
if (shouldWrite && result.filePath && finalCode) {
|
|
3759
3826
|
const fullPath = resolve(join2(projectRoot, result.filePath));
|
|
3760
3827
|
const resolvedRoot = resolve(projectRoot);
|
|
3761
3828
|
const sep = resolvedRoot.endsWith("/") || resolvedRoot.endsWith("\\") ? "" : process.platform === "win32" ? "\\" : "/";
|
|
@@ -3769,7 +3836,7 @@ function handleCompile(body, projectRoot) {
|
|
|
3769
3836
|
if (!existsSync2(dir)) {
|
|
3770
3837
|
mkdirSync2(dir, { recursive: true });
|
|
3771
3838
|
}
|
|
3772
|
-
writeFileSync2(fullPath,
|
|
3839
|
+
writeFileSync2(fullPath, finalCode, "utf-8");
|
|
3773
3840
|
writtenPath = fullPath;
|
|
3774
3841
|
if (result.sourceMap) {
|
|
3775
3842
|
const mapPath = fullPath.replace(/\.ts$/, ".flow.map.json");
|
|
@@ -3796,7 +3863,7 @@ function handleCompile(body, projectRoot) {
|
|
|
3796
3863
|
status: 200,
|
|
3797
3864
|
body: {
|
|
3798
3865
|
success: true,
|
|
3799
|
-
code:
|
|
3866
|
+
code: finalCode,
|
|
3800
3867
|
filePath: result.filePath,
|
|
3801
3868
|
writtenTo: writtenPath,
|
|
3802
3869
|
dependencies: result.dependencies,
|
|
@@ -4141,7 +4208,7 @@ async function handleRequest(req, res, staticDir, projectRoot) {
|
|
|
4141
4208
|
return;
|
|
4142
4209
|
}
|
|
4143
4210
|
if (pathname === "/api/compile") {
|
|
4144
|
-
const result = handleCompile(body, projectRoot);
|
|
4211
|
+
const result = await handleCompile(body, projectRoot);
|
|
4145
4212
|
sendJson(res, result.status, result.body);
|
|
4146
4213
|
return;
|
|
4147
4214
|
}
|
package/dist/compiler.cjs
CHANGED
|
@@ -281,14 +281,38 @@ function validateFlowIR(ir) {
|
|
|
281
281
|
});
|
|
282
282
|
}
|
|
283
283
|
}
|
|
284
|
+
for (const edge of workingIR.edges) {
|
|
285
|
+
const sourceNode = workingNodeMap.get(edge.sourceNodeId);
|
|
286
|
+
const targetNode = workingNodeMap.get(edge.targetNodeId);
|
|
287
|
+
if (!sourceNode || !targetNode) continue;
|
|
288
|
+
const sourcePort = sourceNode.outputs?.find((p) => p.id === edge.sourcePortId);
|
|
289
|
+
const targetPort = targetNode.inputs?.find((p) => p.id === edge.targetPortId);
|
|
290
|
+
if (!sourcePort || !targetPort) continue;
|
|
291
|
+
if (!isTypeCompatible(sourcePort.dataType, targetPort.dataType)) {
|
|
292
|
+
errors.push({
|
|
293
|
+
code: "TYPE_MISMATCH",
|
|
294
|
+
message: `Type mismatch on edge "${edge.id}": output "${sourcePort.label}" (${sourcePort.dataType}) \u2192 input "${targetPort.label}" (${targetPort.dataType})`,
|
|
295
|
+
edgeId: edge.id,
|
|
296
|
+
severity: "warning"
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
284
300
|
return {
|
|
285
|
-
valid: errors.length === 0,
|
|
301
|
+
valid: errors.filter((e) => e.severity !== "warning" && e.severity !== "info").length === 0,
|
|
286
302
|
errors,
|
|
287
303
|
migrated,
|
|
288
304
|
migratedIR: migrated ? workingIR : void 0,
|
|
289
305
|
migrationLog
|
|
290
306
|
};
|
|
291
307
|
}
|
|
308
|
+
function isTypeCompatible(source, target) {
|
|
309
|
+
if (source === target) return true;
|
|
310
|
+
if (source === "any" || target === "any") return true;
|
|
311
|
+
if (target === "object" && source === "array") return true;
|
|
312
|
+
if (target === "string" && source === "number") return true;
|
|
313
|
+
if (target === "number" && source === "string") return true;
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
292
316
|
function detectCycles(nodes, edges) {
|
|
293
317
|
const errors = [];
|
|
294
318
|
const adjacency = /* @__PURE__ */ new Map();
|
|
@@ -1848,6 +1872,9 @@ var RESERVED_WORDS = /* @__PURE__ */ new Set([
|
|
|
1848
1872
|
]);
|
|
1849
1873
|
|
|
1850
1874
|
// src/lib/compiler/compiler.ts
|
|
1875
|
+
registerPlatform("nextjs", () => new NextjsPlatform());
|
|
1876
|
+
registerPlatform("express", () => new ExpressPlatform());
|
|
1877
|
+
registerPlatform("cloudflare", () => new CloudflarePlatform());
|
|
1851
1878
|
function compile(ir, options) {
|
|
1852
1879
|
const pluginRegistry = createPluginRegistry();
|
|
1853
1880
|
pluginRegistry.registerAll(builtinPlugins);
|
|
@@ -1925,7 +1952,7 @@ function compile(ir, options) {
|
|
|
1925
1952
|
indentSize: 2,
|
|
1926
1953
|
convertTabsToSpaces: true
|
|
1927
1954
|
});
|
|
1928
|
-
|
|
1955
|
+
let code = sourceFile.getFullText();
|
|
1929
1956
|
const filePath = platform.getOutputFilePath(trigger);
|
|
1930
1957
|
collectRequiredPackages(workingIR, context);
|
|
1931
1958
|
const sourceMap = buildSourceMap(code, workingIR, filePath);
|
|
@@ -3375,6 +3402,7 @@ function withFlowTrace(handler, options) {
|
|
|
3375
3402
|
formatTraceResults(err, traces, sourceMap.generatedFile)
|
|
3376
3403
|
);
|
|
3377
3404
|
}
|
|
3405
|
+
options.onTrace?.(traces, err);
|
|
3378
3406
|
}
|
|
3379
3407
|
throw err;
|
|
3380
3408
|
}
|
|
@@ -3383,6 +3411,7 @@ function withFlowTrace(handler, options) {
|
|
|
3383
3411
|
}
|
|
3384
3412
|
function installFlowTracer(options) {
|
|
3385
3413
|
const { sourceMap, ir, editorUrl = "http://localhost:3001", log = true } = options;
|
|
3414
|
+
const { onTrace } = options;
|
|
3386
3415
|
const handleError = (err) => {
|
|
3387
3416
|
if (!(err instanceof Error)) return;
|
|
3388
3417
|
const traces = traceError(err, sourceMap, ir, editorUrl);
|
|
@@ -3391,6 +3420,7 @@ function installFlowTracer(options) {
|
|
|
3391
3420
|
formatTraceResults(err, traces, sourceMap.generatedFile)
|
|
3392
3421
|
);
|
|
3393
3422
|
}
|
|
3423
|
+
onTrace?.(traces, err);
|
|
3394
3424
|
};
|
|
3395
3425
|
process.on("uncaughtException", handleError);
|
|
3396
3426
|
process.on("unhandledRejection", handleError);
|
package/dist/compiler.d.cts
CHANGED
|
@@ -548,6 +548,8 @@ interface CompileOptions {
|
|
|
548
548
|
platform?: PlatformName;
|
|
549
549
|
/** Additional Node Plugins */
|
|
550
550
|
plugins?: NodePlugin[];
|
|
551
|
+
/** Format output with Prettier (default: true) */
|
|
552
|
+
prettier?: boolean;
|
|
551
553
|
}
|
|
552
554
|
/**
|
|
553
555
|
* Compiles FlowIR into TypeScript source code.
|
|
@@ -598,6 +600,8 @@ interface ValidationError {
|
|
|
598
600
|
message: string;
|
|
599
601
|
nodeId?: NodeId;
|
|
600
602
|
edgeId?: string;
|
|
603
|
+
/** Severity level (default: "error") */
|
|
604
|
+
severity?: "error" | "warning" | "info";
|
|
601
605
|
}
|
|
602
606
|
interface ValidationResult {
|
|
603
607
|
valid: boolean;
|
|
@@ -909,6 +913,8 @@ interface TracerOptions {
|
|
|
909
913
|
ir?: FlowIR;
|
|
910
914
|
/** Whether to print a readable error message to console (default: true) */
|
|
911
915
|
log?: boolean;
|
|
916
|
+
/** Callback invoked when trace results are available (push to UI store for live badges) */
|
|
917
|
+
onTrace?: (results: TraceResult[], error: Error) => void;
|
|
912
918
|
}
|
|
913
919
|
/**
|
|
914
920
|
* Extract all matching line numbers from an Error object's stack trace
|
package/dist/compiler.d.ts
CHANGED
|
@@ -548,6 +548,8 @@ interface CompileOptions {
|
|
|
548
548
|
platform?: PlatformName;
|
|
549
549
|
/** Additional Node Plugins */
|
|
550
550
|
plugins?: NodePlugin[];
|
|
551
|
+
/** Format output with Prettier (default: true) */
|
|
552
|
+
prettier?: boolean;
|
|
551
553
|
}
|
|
552
554
|
/**
|
|
553
555
|
* Compiles FlowIR into TypeScript source code.
|
|
@@ -598,6 +600,8 @@ interface ValidationError {
|
|
|
598
600
|
message: string;
|
|
599
601
|
nodeId?: NodeId;
|
|
600
602
|
edgeId?: string;
|
|
603
|
+
/** Severity level (default: "error") */
|
|
604
|
+
severity?: "error" | "warning" | "info";
|
|
601
605
|
}
|
|
602
606
|
interface ValidationResult {
|
|
603
607
|
valid: boolean;
|
|
@@ -909,6 +913,8 @@ interface TracerOptions {
|
|
|
909
913
|
ir?: FlowIR;
|
|
910
914
|
/** Whether to print a readable error message to console (default: true) */
|
|
911
915
|
log?: boolean;
|
|
916
|
+
/** Callback invoked when trace results are available (push to UI store for live badges) */
|
|
917
|
+
onTrace?: (results: TraceResult[], error: Error) => void;
|
|
912
918
|
}
|
|
913
919
|
/**
|
|
914
920
|
* Extract all matching line numbers from an Error object's stack trace
|
package/dist/compiler.js
CHANGED
|
@@ -214,14 +214,38 @@ function validateFlowIR(ir) {
|
|
|
214
214
|
});
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
|
+
for (const edge of workingIR.edges) {
|
|
218
|
+
const sourceNode = workingNodeMap.get(edge.sourceNodeId);
|
|
219
|
+
const targetNode = workingNodeMap.get(edge.targetNodeId);
|
|
220
|
+
if (!sourceNode || !targetNode) continue;
|
|
221
|
+
const sourcePort = sourceNode.outputs?.find((p) => p.id === edge.sourcePortId);
|
|
222
|
+
const targetPort = targetNode.inputs?.find((p) => p.id === edge.targetPortId);
|
|
223
|
+
if (!sourcePort || !targetPort) continue;
|
|
224
|
+
if (!isTypeCompatible(sourcePort.dataType, targetPort.dataType)) {
|
|
225
|
+
errors.push({
|
|
226
|
+
code: "TYPE_MISMATCH",
|
|
227
|
+
message: `Type mismatch on edge "${edge.id}": output "${sourcePort.label}" (${sourcePort.dataType}) \u2192 input "${targetPort.label}" (${targetPort.dataType})`,
|
|
228
|
+
edgeId: edge.id,
|
|
229
|
+
severity: "warning"
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
217
233
|
return {
|
|
218
|
-
valid: errors.length === 0,
|
|
234
|
+
valid: errors.filter((e) => e.severity !== "warning" && e.severity !== "info").length === 0,
|
|
219
235
|
errors,
|
|
220
236
|
migrated,
|
|
221
237
|
migratedIR: migrated ? workingIR : void 0,
|
|
222
238
|
migrationLog
|
|
223
239
|
};
|
|
224
240
|
}
|
|
241
|
+
function isTypeCompatible(source, target) {
|
|
242
|
+
if (source === target) return true;
|
|
243
|
+
if (source === "any" || target === "any") return true;
|
|
244
|
+
if (target === "object" && source === "array") return true;
|
|
245
|
+
if (target === "string" && source === "number") return true;
|
|
246
|
+
if (target === "number" && source === "string") return true;
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
225
249
|
function detectCycles(nodes, edges) {
|
|
226
250
|
const errors = [];
|
|
227
251
|
const adjacency = /* @__PURE__ */ new Map();
|
|
@@ -1781,6 +1805,9 @@ var RESERVED_WORDS = /* @__PURE__ */ new Set([
|
|
|
1781
1805
|
]);
|
|
1782
1806
|
|
|
1783
1807
|
// src/lib/compiler/compiler.ts
|
|
1808
|
+
registerPlatform("nextjs", () => new NextjsPlatform());
|
|
1809
|
+
registerPlatform("express", () => new ExpressPlatform());
|
|
1810
|
+
registerPlatform("cloudflare", () => new CloudflarePlatform());
|
|
1784
1811
|
function compile(ir, options) {
|
|
1785
1812
|
const pluginRegistry = createPluginRegistry();
|
|
1786
1813
|
pluginRegistry.registerAll(builtinPlugins);
|
|
@@ -1858,7 +1885,7 @@ function compile(ir, options) {
|
|
|
1858
1885
|
indentSize: 2,
|
|
1859
1886
|
convertTabsToSpaces: true
|
|
1860
1887
|
});
|
|
1861
|
-
|
|
1888
|
+
let code = sourceFile.getFullText();
|
|
1862
1889
|
const filePath = platform.getOutputFilePath(trigger);
|
|
1863
1890
|
collectRequiredPackages(workingIR, context);
|
|
1864
1891
|
const sourceMap = buildSourceMap(code, workingIR, filePath);
|
|
@@ -3311,6 +3338,7 @@ function withFlowTrace(handler, options) {
|
|
|
3311
3338
|
formatTraceResults(err, traces, sourceMap.generatedFile)
|
|
3312
3339
|
);
|
|
3313
3340
|
}
|
|
3341
|
+
options.onTrace?.(traces, err);
|
|
3314
3342
|
}
|
|
3315
3343
|
throw err;
|
|
3316
3344
|
}
|
|
@@ -3319,6 +3347,7 @@ function withFlowTrace(handler, options) {
|
|
|
3319
3347
|
}
|
|
3320
3348
|
function installFlowTracer(options) {
|
|
3321
3349
|
const { sourceMap, ir, editorUrl = "http://localhost:3001", log = true } = options;
|
|
3350
|
+
const { onTrace } = options;
|
|
3322
3351
|
const handleError = (err) => {
|
|
3323
3352
|
if (!(err instanceof Error)) return;
|
|
3324
3353
|
const traces = traceError(err, sourceMap, ir, editorUrl);
|
|
@@ -3327,6 +3356,7 @@ function installFlowTracer(options) {
|
|
|
3327
3356
|
formatTraceResults(err, traces, sourceMap.generatedFile)
|
|
3328
3357
|
);
|
|
3329
3358
|
}
|
|
3359
|
+
onTrace?.(traces, err);
|
|
3330
3360
|
};
|
|
3331
3361
|
process.on("uncaughtException", handleError);
|
|
3332
3362
|
process.on("unhandledRejection", handleError);
|
package/dist/server.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export interface ServerOptions {
|
|
|
21
21
|
onReady?: (url: string) => void;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export declare function handleCompile(body: CompileRequest, projectRoot: string): ApiResponse
|
|
24
|
+
export declare function handleCompile(body: CompileRequest, projectRoot: string): Promise<ApiResponse>;
|
|
25
25
|
export declare function handleGenerate(body: { prompt?: string }): Promise<ApiResponse>;
|
|
26
26
|
export declare function handleImportOpenAPI(body: { spec?: unknown; filter?: { tags?: string[]; paths?: string[] } }): ApiResponse;
|
|
27
27
|
export declare function startServer(options?: ServerOptions): Server;
|