@timo9378/flow2code 0.1.4 → 0.1.6
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 +34 -0
- package/dist/cli.js +215 -103
- package/dist/compiler.cjs +48 -34
- package/dist/compiler.js +48 -34
- package/dist/server.js +134 -52
- package/out/404.html +1 -1
- package/out/__next.__PAGE__.txt +2 -2
- package/out/__next._full.txt +2 -2
- package/out/__next._head.txt +1 -1
- package/out/__next._index.txt +1 -1
- package/out/__next._tree.txt +1 -1
- package/out/_next/static/chunks/{2be31674b47c1089.js → bfa275b7488d8b70.js} +1 -1
- package/out/_not-found/__next._full.txt +1 -1
- package/out/_not-found/__next._head.txt +1 -1
- package/out/_not-found/__next._index.txt +1 -1
- package/out/_not-found/__next._not-found/__PAGE__.txt +1 -1
- package/out/_not-found/__next._not-found.txt +1 -1
- package/out/_not-found/__next._tree.txt +1 -1
- package/out/_not-found.html +1 -1
- package/out/_not-found.txt +1 -1
- package/out/index.html +2 -2
- package/out/index.txt +2 -2
- package/package.json +3 -3
- package/scripts/publish-all.sh +56 -0
- /package/out/_next/static/{OIo5LFjdea8u6u4evHHQo → wFX9tOZWtEdER7XrtbgHQ}/_buildManifest.js +0 -0
- /package/out/_next/static/{OIo5LFjdea8u6u4evHHQo → wFX9tOZWtEdER7XrtbgHQ}/_clientMiddlewareManifest.json +0 -0
- /package/out/_next/static/{OIo5LFjdea8u6u4evHHQo → wFX9tOZWtEdER7XrtbgHQ}/_ssgManifest.js +0 -0
package/dist/cli.js
CHANGED
|
@@ -1822,21 +1822,22 @@ function compile(ir, options) {
|
|
|
1822
1822
|
errors: validation.errors.map((e) => `[${e.code}] ${e.message}`)
|
|
1823
1823
|
};
|
|
1824
1824
|
}
|
|
1825
|
+
const workingIR = validation.migrated && validation.migratedIR ? validation.migratedIR : ir;
|
|
1825
1826
|
let plan;
|
|
1826
1827
|
try {
|
|
1827
|
-
plan = topologicalSort(
|
|
1828
|
+
plan = topologicalSort(workingIR);
|
|
1828
1829
|
} catch (err) {
|
|
1829
1830
|
return {
|
|
1830
1831
|
success: false,
|
|
1831
1832
|
errors: [err instanceof Error ? err.message : String(err)]
|
|
1832
1833
|
};
|
|
1833
1834
|
}
|
|
1834
|
-
const nodeMap = new Map(
|
|
1835
|
+
const nodeMap = new Map(workingIR.nodes.map((n) => [n.id, n]));
|
|
1835
1836
|
const platformName = options?.platform ?? "nextjs";
|
|
1836
1837
|
const platform = getPlatform(platformName);
|
|
1837
|
-
const symbolTable = buildSymbolTable(
|
|
1838
|
+
const symbolTable = buildSymbolTable(workingIR);
|
|
1838
1839
|
const context = {
|
|
1839
|
-
ir,
|
|
1840
|
+
ir: workingIR,
|
|
1840
1841
|
plan,
|
|
1841
1842
|
nodeMap,
|
|
1842
1843
|
envVars: /* @__PURE__ */ new Set(),
|
|
@@ -1853,8 +1854,8 @@ function compile(ir, options) {
|
|
|
1853
1854
|
generatedBlockNodeIds: /* @__PURE__ */ new Set(),
|
|
1854
1855
|
pluginRegistry
|
|
1855
1856
|
};
|
|
1856
|
-
const trigger =
|
|
1857
|
-
const preComputedBlockNodes = computeControlFlowDescendants(
|
|
1857
|
+
const trigger = workingIR.nodes.find((n) => n.category === "trigger" /* TRIGGER */);
|
|
1858
|
+
const preComputedBlockNodes = computeControlFlowDescendants(workingIR, trigger.id);
|
|
1858
1859
|
for (const nodeId of preComputedBlockNodes) {
|
|
1859
1860
|
context.childBlockNodeIds.add(nodeId);
|
|
1860
1861
|
context.symbolTableExclusions.add(nodeId);
|
|
@@ -1864,7 +1865,7 @@ function compile(ir, options) {
|
|
|
1864
1865
|
);
|
|
1865
1866
|
if (hasConcurrency) {
|
|
1866
1867
|
context.dagMode = true;
|
|
1867
|
-
for (const node of
|
|
1868
|
+
for (const node of workingIR.nodes) {
|
|
1868
1869
|
if (node.category !== "trigger" /* TRIGGER */) {
|
|
1869
1870
|
context.symbolTableExclusions.add(node.id);
|
|
1870
1871
|
}
|
|
@@ -1879,8 +1880,8 @@ function compile(ir, options) {
|
|
|
1879
1880
|
});
|
|
1880
1881
|
const code = sourceFile.getFullText();
|
|
1881
1882
|
const filePath = platform.getOutputFilePath(trigger);
|
|
1882
|
-
collectRequiredPackages(
|
|
1883
|
-
const sourceMap = buildSourceMap(code,
|
|
1883
|
+
collectRequiredPackages(workingIR, context);
|
|
1884
|
+
const sourceMap = buildSourceMap(code, workingIR, filePath);
|
|
1884
1885
|
const dependencies = {
|
|
1885
1886
|
all: [...context.requiredPackages].sort(),
|
|
1886
1887
|
missing: [...context.requiredPackages].sort(),
|
|
@@ -2079,7 +2080,7 @@ function generateNodeChainDAG(writer, triggerId, context) {
|
|
|
2079
2080
|
generateNodeBody(writer, node, context);
|
|
2080
2081
|
});
|
|
2081
2082
|
writer.writeLine(`)();`);
|
|
2082
|
-
writer.writeLine(`${promiseVar}.catch(() => {});`);
|
|
2083
|
+
writer.writeLine(`${promiseVar}.catch((err) => { console.error("[Flow2Code DAG Error]", err); });`);
|
|
2083
2084
|
writer.blankLine();
|
|
2084
2085
|
}
|
|
2085
2086
|
if (dagNodeIds.length > 0) {
|
|
@@ -2129,11 +2130,8 @@ function generateConcurrentNodes(writer, nodeIds, context) {
|
|
|
2129
2130
|
writer.writeLine(";");
|
|
2130
2131
|
}
|
|
2131
2132
|
writer.writeLine(
|
|
2132
|
-
`
|
|
2133
|
+
`await Promise.all([${taskNames.map((t) => `${t}()`).join(", ")}]);`
|
|
2133
2134
|
);
|
|
2134
|
-
activeNodeIds.forEach((nodeId, i) => {
|
|
2135
|
-
writer.writeLine(`flowState['${nodeId}'] = r${i};`);
|
|
2136
|
-
});
|
|
2137
2135
|
activeNodeIds.forEach((nodeId) => {
|
|
2138
2136
|
const varName = context.symbolTable.getVarName(nodeId);
|
|
2139
2137
|
writer.writeLine(`const ${varName} = flowState['${nodeId}'];`);
|
|
@@ -2152,7 +2150,18 @@ function generateNodeBody(writer, node, context) {
|
|
|
2152
2150
|
const plugin = context.pluginRegistry.get(node.nodeType);
|
|
2153
2151
|
if (plugin) {
|
|
2154
2152
|
const pluginCtx = createPluginContext(context);
|
|
2155
|
-
|
|
2153
|
+
try {
|
|
2154
|
+
plugin.generate(node, writer, pluginCtx);
|
|
2155
|
+
} catch (err) {
|
|
2156
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
2157
|
+
const stack = err instanceof Error ? err.stack : void 0;
|
|
2158
|
+
throw new Error(
|
|
2159
|
+
`[flow2code] Plugin "${node.nodeType}" threw an error while generating node "${node.label}" (${node.id}):
|
|
2160
|
+
${errMsg}` + (stack ? `
|
|
2161
|
+
Stack: ${stack}` : ""),
|
|
2162
|
+
{ cause: err }
|
|
2163
|
+
);
|
|
2164
|
+
}
|
|
2156
2165
|
} else {
|
|
2157
2166
|
throw new Error(
|
|
2158
2167
|
`[flow2code] Unsupported node type: "${node.nodeType}". Register a plugin via pluginRegistry.register() or use a built-in node type.`
|
|
@@ -2160,6 +2169,13 @@ function generateNodeBody(writer, node, context) {
|
|
|
2160
2169
|
}
|
|
2161
2170
|
}
|
|
2162
2171
|
function createPluginContext(context) {
|
|
2172
|
+
const pendingChildBlockIds = [];
|
|
2173
|
+
const applyChildBlockRegistration = (nodeId) => {
|
|
2174
|
+
pendingChildBlockIds.push(nodeId);
|
|
2175
|
+
context.childBlockNodeIds.add(nodeId);
|
|
2176
|
+
context.symbolTableExclusions.add(nodeId);
|
|
2177
|
+
context.generatedBlockNodeIds.add(nodeId);
|
|
2178
|
+
};
|
|
2163
2179
|
return {
|
|
2164
2180
|
ir: context.ir,
|
|
2165
2181
|
nodeMap: context.nodeMap,
|
|
@@ -2185,9 +2201,7 @@ function createPluginContext(context) {
|
|
|
2185
2201
|
return resolveEnvVars(url, context);
|
|
2186
2202
|
},
|
|
2187
2203
|
generateChildNode(writer, node) {
|
|
2188
|
-
|
|
2189
|
-
context.symbolTableExclusions.add(node.id);
|
|
2190
|
-
context.generatedBlockNodeIds.add(node.id);
|
|
2204
|
+
applyChildBlockRegistration(node.id);
|
|
2191
2205
|
writer.writeLine(`// --- ${node.label} (${node.nodeType}) [${node.id}] ---`);
|
|
2192
2206
|
generateNodeBody(writer, node, context);
|
|
2193
2207
|
generateBlockContinuation(writer, node.id, context);
|
|
@@ -2231,27 +2245,27 @@ function collectRequiredPackages(ir, context) {
|
|
|
2231
2245
|
function buildSourceMap(code, ir, filePath) {
|
|
2232
2246
|
const lines = code.split("\n");
|
|
2233
2247
|
const mappings = {};
|
|
2234
|
-
const
|
|
2248
|
+
const validNodeIds = new Set(ir.nodes.map((n) => n.id));
|
|
2235
2249
|
let currentNodeId = null;
|
|
2236
2250
|
let currentStartLine = 0;
|
|
2237
2251
|
for (let i = 0; i < lines.length; i++) {
|
|
2238
2252
|
const lineNum = i + 1;
|
|
2239
|
-
const
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
}
|
|
2252
|
-
currentNodeId = null;
|
|
2253
|
-
}
|
|
2253
|
+
const line = lines[i];
|
|
2254
|
+
const bracketOpen = line.indexOf("[");
|
|
2255
|
+
const bracketClose = line.indexOf("] ---", bracketOpen);
|
|
2256
|
+
if (bracketOpen === -1 || bracketClose === -1) continue;
|
|
2257
|
+
const trimmed = line.trimStart();
|
|
2258
|
+
if (!trimmed.startsWith("//")) continue;
|
|
2259
|
+
const candidateId = line.slice(bracketOpen + 1, bracketClose);
|
|
2260
|
+
if (!validNodeIds.has(candidateId)) continue;
|
|
2261
|
+
if (currentNodeId) {
|
|
2262
|
+
mappings[currentNodeId] = {
|
|
2263
|
+
startLine: currentStartLine,
|
|
2264
|
+
endLine: lineNum - 1
|
|
2265
|
+
};
|
|
2254
2266
|
}
|
|
2267
|
+
currentNodeId = candidateId;
|
|
2268
|
+
currentStartLine = lineNum;
|
|
2255
2269
|
}
|
|
2256
2270
|
if (currentNodeId) {
|
|
2257
2271
|
mappings[currentNodeId] = {
|
|
@@ -2302,6 +2316,72 @@ var init_compiler = __esm({
|
|
|
2302
2316
|
}
|
|
2303
2317
|
});
|
|
2304
2318
|
|
|
2319
|
+
// src/lib/logger.ts
|
|
2320
|
+
import pc from "picocolors";
|
|
2321
|
+
var LEVEL_ORDER, Logger, logger;
|
|
2322
|
+
var init_logger = __esm({
|
|
2323
|
+
"src/lib/logger.ts"() {
|
|
2324
|
+
"use strict";
|
|
2325
|
+
LEVEL_ORDER = {
|
|
2326
|
+
debug: 0,
|
|
2327
|
+
info: 1,
|
|
2328
|
+
warn: 2,
|
|
2329
|
+
error: 3,
|
|
2330
|
+
silent: 4
|
|
2331
|
+
};
|
|
2332
|
+
Logger = class {
|
|
2333
|
+
/** Minimum log level (default: "info"). Set to "silent" to suppress all output. */
|
|
2334
|
+
level = "info";
|
|
2335
|
+
/** Prefix for all log lines (default: "[flow2code]") */
|
|
2336
|
+
prefix = "[flow2code]";
|
|
2337
|
+
shouldLog(level) {
|
|
2338
|
+
return LEVEL_ORDER[level] >= LEVEL_ORDER[this.level];
|
|
2339
|
+
}
|
|
2340
|
+
debug(...args) {
|
|
2341
|
+
if (!this.shouldLog("debug")) return;
|
|
2342
|
+
console.log(pc.gray(`${this.prefix} ${pc.dim("DEBUG")}`), ...args);
|
|
2343
|
+
}
|
|
2344
|
+
info(...args) {
|
|
2345
|
+
if (!this.shouldLog("info")) return;
|
|
2346
|
+
console.log(pc.blue(`${this.prefix}`), ...args);
|
|
2347
|
+
}
|
|
2348
|
+
success(...args) {
|
|
2349
|
+
if (!this.shouldLog("info")) return;
|
|
2350
|
+
console.log(pc.green(`${this.prefix} \u2705`), ...args);
|
|
2351
|
+
}
|
|
2352
|
+
warn(...args) {
|
|
2353
|
+
if (!this.shouldLog("warn")) return;
|
|
2354
|
+
console.warn(pc.yellow(`${this.prefix} \u26A0\uFE0F`), ...args);
|
|
2355
|
+
}
|
|
2356
|
+
error(...args) {
|
|
2357
|
+
if (!this.shouldLog("error")) return;
|
|
2358
|
+
console.error(pc.red(`${this.prefix} \u274C`), ...args);
|
|
2359
|
+
}
|
|
2360
|
+
/** Print a blank line (respects silent mode) */
|
|
2361
|
+
blank() {
|
|
2362
|
+
if (!this.shouldLog("info")) return;
|
|
2363
|
+
console.log();
|
|
2364
|
+
}
|
|
2365
|
+
/** Print raw text without prefix (respects silent mode) */
|
|
2366
|
+
raw(...args) {
|
|
2367
|
+
if (!this.shouldLog("info")) return;
|
|
2368
|
+
console.log(...args);
|
|
2369
|
+
}
|
|
2370
|
+
/** Formatted key-value line for startup banners */
|
|
2371
|
+
kv(key, value) {
|
|
2372
|
+
if (!this.shouldLog("info")) return;
|
|
2373
|
+
console.log(` ${pc.dim("\u251C\u2500")} ${pc.bold(key)} ${value}`);
|
|
2374
|
+
}
|
|
2375
|
+
/** Last key-value line (uses └─) */
|
|
2376
|
+
kvLast(key, value) {
|
|
2377
|
+
if (!this.shouldLog("info")) return;
|
|
2378
|
+
console.log(` ${pc.dim("\u2514\u2500")} ${pc.bold(key)} ${value}`);
|
|
2379
|
+
}
|
|
2380
|
+
};
|
|
2381
|
+
logger = new Logger();
|
|
2382
|
+
}
|
|
2383
|
+
});
|
|
2384
|
+
|
|
2305
2385
|
// src/lib/compiler/decompiler.ts
|
|
2306
2386
|
var decompiler_exports = {};
|
|
2307
2387
|
__export(decompiler_exports, {
|
|
@@ -3625,7 +3705,9 @@ function handleCompile(body, projectRoot) {
|
|
|
3625
3705
|
let writtenPath = null;
|
|
3626
3706
|
if (shouldWrite && result.filePath && result.code) {
|
|
3627
3707
|
const fullPath = resolve(join2(projectRoot, result.filePath));
|
|
3628
|
-
|
|
3708
|
+
const resolvedRoot = resolve(projectRoot);
|
|
3709
|
+
const sep = resolvedRoot.endsWith("/") || resolvedRoot.endsWith("\\") ? "" : process.platform === "win32" ? "\\" : "/";
|
|
3710
|
+
if (!fullPath.startsWith(resolvedRoot + sep)) {
|
|
3629
3711
|
return {
|
|
3630
3712
|
status: 400,
|
|
3631
3713
|
body: { success: false, error: "Output path escapes project root" }
|
|
@@ -3865,7 +3947,7 @@ __export(server_exports, {
|
|
|
3865
3947
|
});
|
|
3866
3948
|
import { createServer } from "http";
|
|
3867
3949
|
import { readFile, stat } from "fs/promises";
|
|
3868
|
-
import { join as join3, extname as extname2, dirname as dirname3 } from "path";
|
|
3950
|
+
import { join as join3, extname as extname2, dirname as dirname3, resolve as resolve2 } from "path";
|
|
3869
3951
|
import { fileURLToPath } from "url";
|
|
3870
3952
|
import { existsSync as existsSync3 } from "fs";
|
|
3871
3953
|
function resolveStaticDir() {
|
|
@@ -3926,7 +4008,7 @@ function sendJson(res, status, body) {
|
|
|
3926
4008
|
res.end(JSON.stringify(body));
|
|
3927
4009
|
}
|
|
3928
4010
|
async function readBody(req) {
|
|
3929
|
-
return new Promise((
|
|
4011
|
+
return new Promise((resolve4, reject) => {
|
|
3930
4012
|
const chunks = [];
|
|
3931
4013
|
let totalSize = 0;
|
|
3932
4014
|
req.on("data", (chunk) => {
|
|
@@ -3938,7 +4020,7 @@ async function readBody(req) {
|
|
|
3938
4020
|
}
|
|
3939
4021
|
chunks.push(chunk);
|
|
3940
4022
|
});
|
|
3941
|
-
req.on("end", () =>
|
|
4023
|
+
req.on("end", () => resolve4(Buffer.concat(chunks).toString("utf-8")));
|
|
3942
4024
|
req.on("error", reject);
|
|
3943
4025
|
});
|
|
3944
4026
|
}
|
|
@@ -3947,7 +4029,13 @@ async function parseJsonBody(req) {
|
|
|
3947
4029
|
return JSON.parse(raw);
|
|
3948
4030
|
}
|
|
3949
4031
|
async function serveStatic(staticDir, pathname, res) {
|
|
3950
|
-
|
|
4032
|
+
const decodedPath = decodeURIComponent(pathname);
|
|
4033
|
+
let filePath = join3(staticDir, decodedPath === "/" ? "index.html" : decodedPath);
|
|
4034
|
+
const resolvedPath = resolve2(filePath);
|
|
4035
|
+
const resolvedStaticDir = resolve2(staticDir);
|
|
4036
|
+
if (!resolvedPath.startsWith(resolvedStaticDir + (resolvedStaticDir.endsWith("/") || resolvedStaticDir.endsWith("\\") ? "" : process.platform === "win32" ? "\\" : "/"))) {
|
|
4037
|
+
return false;
|
|
4038
|
+
}
|
|
3951
4039
|
if (!extname2(filePath)) {
|
|
3952
4040
|
filePath += ".html";
|
|
3953
4041
|
}
|
|
@@ -4035,7 +4123,7 @@ function startServer(options = {}) {
|
|
|
4035
4123
|
const projectRoot = options.projectRoot ?? process.cwd();
|
|
4036
4124
|
const server = createServer((req, res) => {
|
|
4037
4125
|
handleRequest(req, res, staticDir, projectRoot).catch((err) => {
|
|
4038
|
-
|
|
4126
|
+
logger.error("Internal error:", err);
|
|
4039
4127
|
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
4040
4128
|
res.end("Internal Server Error");
|
|
4041
4129
|
});
|
|
@@ -4046,20 +4134,20 @@ function startServer(options = {}) {
|
|
|
4046
4134
|
if (options.onReady) {
|
|
4047
4135
|
options.onReady(url);
|
|
4048
4136
|
} else {
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4137
|
+
logger.blank();
|
|
4138
|
+
logger.info("Flow2Code Dev Server");
|
|
4139
|
+
logger.kv("Local:", url);
|
|
4140
|
+
logger.kv("API:", `${url}/api/compile`);
|
|
4141
|
+
logger.kv("Static:", staticDir);
|
|
4142
|
+
logger.kvLast("Project:", projectRoot);
|
|
4055
4143
|
if (!hasUI) {
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4144
|
+
logger.blank();
|
|
4145
|
+
logger.warn("UI files not found (out/index.html missing).");
|
|
4146
|
+
logger.raw(" The API endpoints still work, but the visual editor won't load.");
|
|
4147
|
+
logger.raw(' To fix: run "pnpm build:ui" in the flow2code source directory,');
|
|
4148
|
+
logger.raw(" or reinstall from npm: npm i @timo9378/flow2code@latest");
|
|
4061
4149
|
}
|
|
4062
|
-
|
|
4150
|
+
logger.blank();
|
|
4063
4151
|
}
|
|
4064
4152
|
});
|
|
4065
4153
|
return server;
|
|
@@ -4069,6 +4157,7 @@ var init_server = __esm({
|
|
|
4069
4157
|
"src/server/index.ts"() {
|
|
4070
4158
|
"use strict";
|
|
4071
4159
|
init_handlers();
|
|
4160
|
+
init_logger();
|
|
4072
4161
|
init_handlers();
|
|
4073
4162
|
__filename = fileURLToPath(import.meta.url);
|
|
4074
4163
|
__dirname = dirname3(__filename);
|
|
@@ -4097,10 +4186,12 @@ var init_server = __esm({
|
|
|
4097
4186
|
|
|
4098
4187
|
// src/cli/index.ts
|
|
4099
4188
|
init_compiler();
|
|
4189
|
+
init_logger();
|
|
4100
4190
|
init_validator();
|
|
4101
4191
|
import { Command } from "commander";
|
|
4102
4192
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync4, readdirSync as readdirSync2, rmSync as rmSync2 } from "fs";
|
|
4103
|
-
import {
|
|
4193
|
+
import { readFile as readFile2, writeFile, mkdir } from "fs/promises";
|
|
4194
|
+
import { join as join4, dirname as dirname4, resolve as resolve3, basename as basename2 } from "path";
|
|
4104
4195
|
import { watch } from "chokidar";
|
|
4105
4196
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4106
4197
|
|
|
@@ -4726,7 +4817,7 @@ var pkgJson = JSON.parse(readFileSync3(join4(__cliDirname, "..", "package.json")
|
|
|
4726
4817
|
var program = new Command();
|
|
4727
4818
|
program.name("flow2code").description("Visual AST Compiler: Compile .flow.json into native TypeScript").version(pkgJson.version);
|
|
4728
4819
|
program.command("compile <file>").description("Compile .flow.json or YAML directory to TypeScript (auto-detects format)").option("-o, --output <path>", "Specify output path (overrides auto-detection)").option("--platform <name>", "Target platform: nextjs | express | cloudflare", "nextjs").option("--dry-run", "Display generated code without writing to file").option("--source-map", "Generate Source Map mapping file (.flow.map.json)").action((file, options) => {
|
|
4729
|
-
const filePath =
|
|
4820
|
+
const filePath = resolve3(file);
|
|
4730
4821
|
if (!existsSync4(filePath)) {
|
|
4731
4822
|
console.error(`\u274C File/directory not found: ${filePath}`);
|
|
4732
4823
|
process.exit(1);
|
|
@@ -4764,7 +4855,7 @@ program.command("compile <file>").description("Compile .flow.json or YAML direct
|
|
|
4764
4855
|
process.exit(1);
|
|
4765
4856
|
}
|
|
4766
4857
|
const outputPath = options.output ?? result.filePath;
|
|
4767
|
-
const fullOutputPath =
|
|
4858
|
+
const fullOutputPath = resolve3(outputPath);
|
|
4768
4859
|
const outputDir = dirname4(fullOutputPath);
|
|
4769
4860
|
if (!existsSync4(outputDir)) {
|
|
4770
4861
|
mkdirSync3(outputDir, { recursive: true });
|
|
@@ -4777,7 +4868,7 @@ program.command("compile <file>").description("Compile .flow.json or YAML direct
|
|
|
4777
4868
|
console.log(`\u{1F5FA}\uFE0F Source Map: ${mapPath}`);
|
|
4778
4869
|
}
|
|
4779
4870
|
if (result.dependencies && result.dependencies.all.length > 0) {
|
|
4780
|
-
const projectPkgPath =
|
|
4871
|
+
const projectPkgPath = resolve3("package.json");
|
|
4781
4872
|
if (existsSync4(projectPkgPath)) {
|
|
4782
4873
|
try {
|
|
4783
4874
|
const pkgJson2 = JSON.parse(readFileSync3(projectPkgPath, "utf-8"));
|
|
@@ -4800,7 +4891,7 @@ program.command("compile <file>").description("Compile .flow.json or YAML direct
|
|
|
4800
4891
|
});
|
|
4801
4892
|
program.command("audit <file>").description("Decompile any TypeScript file into a visual FlowIR for code auditing").option("-o, --output <path>", "Write IR JSON to file instead of stdout").option("--format <fmt>", "Output format: json | mermaid | summary", "summary").option("--function <name>", "Target function name to decompile").option("--no-audit-hints", "Disable audit hints").action(async (file, options) => {
|
|
4802
4893
|
const { decompile: decompile2 } = await Promise.resolve().then(() => (init_decompiler(), decompiler_exports));
|
|
4803
|
-
const filePath =
|
|
4894
|
+
const filePath = resolve3(file);
|
|
4804
4895
|
if (!existsSync4(filePath)) {
|
|
4805
4896
|
console.error(`\u274C File not found: ${filePath}`);
|
|
4806
4897
|
process.exit(1);
|
|
@@ -4820,7 +4911,7 @@ program.command("audit <file>").description("Decompile any TypeScript file into
|
|
|
4820
4911
|
if (fmt === "json") {
|
|
4821
4912
|
const output = JSON.stringify(result.ir, null, 2);
|
|
4822
4913
|
if (options.output) {
|
|
4823
|
-
writeFileSync3(
|
|
4914
|
+
writeFileSync3(resolve3(options.output), output, "utf-8");
|
|
4824
4915
|
console.log(`\u2705 IR written to: ${options.output}`);
|
|
4825
4916
|
} else {
|
|
4826
4917
|
console.log(output);
|
|
@@ -4828,7 +4919,7 @@ program.command("audit <file>").description("Decompile any TypeScript file into
|
|
|
4828
4919
|
} else if (fmt === "mermaid") {
|
|
4829
4920
|
const mermaidOutput = irToMermaid(result.ir);
|
|
4830
4921
|
if (options.output) {
|
|
4831
|
-
writeFileSync3(
|
|
4922
|
+
writeFileSync3(resolve3(options.output), mermaidOutput, "utf-8");
|
|
4832
4923
|
console.log(`\u2705 Mermaid diagram written to: ${options.output}`);
|
|
4833
4924
|
} else {
|
|
4834
4925
|
console.log(mermaidOutput);
|
|
@@ -4855,7 +4946,7 @@ program.command("audit <file>").description("Decompile any TypeScript file into
|
|
|
4855
4946
|
console.log("");
|
|
4856
4947
|
if (options.output) {
|
|
4857
4948
|
const irJson = JSON.stringify(result.ir, null, 2);
|
|
4858
|
-
writeFileSync3(
|
|
4949
|
+
writeFileSync3(resolve3(options.output), irJson, "utf-8");
|
|
4859
4950
|
console.log(`\u2705 IR written to: ${options.output}`);
|
|
4860
4951
|
}
|
|
4861
4952
|
}
|
|
@@ -4889,8 +4980,8 @@ function irToMermaid(ir) {
|
|
|
4889
4980
|
return lines.join("\n");
|
|
4890
4981
|
}
|
|
4891
4982
|
program.command("watch [dir]").description("Watch directory, auto-compile .flow.json and YAML directory changes").option("-p, --project <path>", "Next.js project root directory", ".").action((dir = ".", options) => {
|
|
4892
|
-
const watchDir =
|
|
4893
|
-
const projectRoot =
|
|
4983
|
+
const watchDir = resolve3(dir);
|
|
4984
|
+
const projectRoot = resolve3(options.project ?? ".");
|
|
4894
4985
|
console.log(`\u{1F440} Watching: ${watchDir}/**/*.flow.json + **/*.yaml`);
|
|
4895
4986
|
console.log(`\u{1F4C1} Output to: ${projectRoot}`);
|
|
4896
4987
|
console.log("Press Ctrl+C to stop\n");
|
|
@@ -4901,17 +4992,28 @@ program.command("watch [dir]").description("Watch directory, auto-compile .flow.
|
|
|
4901
4992
|
ignoreInitial: false
|
|
4902
4993
|
}
|
|
4903
4994
|
);
|
|
4995
|
+
const pendingTimers = /* @__PURE__ */ new Map();
|
|
4996
|
+
const DEBOUNCE_MS = 150;
|
|
4904
4997
|
const handleChange = (filePath) => {
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
} else if (filePath.endsWith(".yaml")) {
|
|
4998
|
+
let compileKey = filePath;
|
|
4999
|
+
if (filePath.endsWith(".yaml")) {
|
|
4908
5000
|
let dir2 = dirname4(filePath);
|
|
4909
5001
|
if (basename2(dir2) === "nodes") dir2 = dirname4(dir2);
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
5002
|
+
compileKey = dir2;
|
|
5003
|
+
}
|
|
5004
|
+
const existing = pendingTimers.get(compileKey);
|
|
5005
|
+
if (existing) clearTimeout(existing);
|
|
5006
|
+
pendingTimers.set(compileKey, setTimeout(() => {
|
|
5007
|
+
pendingTimers.delete(compileKey);
|
|
5008
|
+
if (filePath.endsWith(".flow.json")) {
|
|
5009
|
+
compileFileAsync(filePath, projectRoot);
|
|
5010
|
+
} else if (filePath.endsWith(".yaml")) {
|
|
5011
|
+
const metaPath = join4(compileKey, "meta.yaml");
|
|
5012
|
+
if (existsSync4(metaPath)) {
|
|
5013
|
+
compileFlowDirAsync(compileKey, projectRoot);
|
|
5014
|
+
}
|
|
4913
5015
|
}
|
|
4914
|
-
}
|
|
5016
|
+
}, DEBOUNCE_MS));
|
|
4915
5017
|
};
|
|
4916
5018
|
watcher.on("change", handleChange);
|
|
4917
5019
|
watcher.on("add", handleChange);
|
|
@@ -4920,7 +5022,7 @@ program.command("watch [dir]").description("Watch directory, auto-compile .flow.
|
|
|
4920
5022
|
});
|
|
4921
5023
|
});
|
|
4922
5024
|
program.command("init").description("Initialize Flow2Code in current project (Zero Pollution mode)").action(() => {
|
|
4923
|
-
const flow2codeDir =
|
|
5025
|
+
const flow2codeDir = resolve3(".flow2code");
|
|
4924
5026
|
const flowsDir = join4(flow2codeDir, "flows");
|
|
4925
5027
|
if (!existsSync4(flow2codeDir)) {
|
|
4926
5028
|
mkdirSync3(flow2codeDir, { recursive: true });
|
|
@@ -4990,7 +5092,7 @@ program.command("init").description("Initialize Flow2Code in current project (Ze
|
|
|
4990
5092
|
writeFileSync3(examplePath, JSON.stringify(exampleFlow, null, 2), "utf-8");
|
|
4991
5093
|
console.log(`\u{1F4C4} Created example: ${examplePath}`);
|
|
4992
5094
|
}
|
|
4993
|
-
const gitignorePath =
|
|
5095
|
+
const gitignorePath = resolve3(".gitignore");
|
|
4994
5096
|
if (existsSync4(gitignorePath)) {
|
|
4995
5097
|
const content = readFileSync3(gitignorePath, "utf-8");
|
|
4996
5098
|
if (!content.includes(".flow2code/")) {
|
|
@@ -5009,7 +5111,7 @@ program.command("init").description("Initialize Flow2Code in current project (Ze
|
|
|
5009
5111
|
console.log(` npx ${pkgJson.name} watch .flow2code/flows/`);
|
|
5010
5112
|
});
|
|
5011
5113
|
program.command("split <file>").description("Split .flow.json into a Git-friendly YAML directory structure").option("-o, --output <dir>", "Specify output directory (default: same name as file)").action((file, options) => {
|
|
5012
|
-
const filePath =
|
|
5114
|
+
const filePath = resolve3(file);
|
|
5013
5115
|
if (!existsSync4(filePath)) {
|
|
5014
5116
|
console.error(`\u274C File not found: ${filePath}`);
|
|
5015
5117
|
process.exit(1);
|
|
@@ -5025,7 +5127,7 @@ program.command("split <file>").description("Split .flow.json into a Git-friendl
|
|
|
5025
5127
|
const outputDir = options.output ?? filePath.replace(/\.flow\.json$|\.json$/, "");
|
|
5026
5128
|
const written = splitToFileSystem(
|
|
5027
5129
|
ir,
|
|
5028
|
-
|
|
5130
|
+
resolve3(outputDir),
|
|
5029
5131
|
{ mkdirSync: mkdirSync3, writeFileSync: (p, c) => writeFileSync3(p, c, "utf-8") },
|
|
5030
5132
|
{ join: join4 }
|
|
5031
5133
|
);
|
|
@@ -5033,7 +5135,7 @@ program.command("split <file>").description("Split .flow.json into a Git-friendl
|
|
|
5033
5135
|
written.forEach((f) => console.log(` \u{1F4C4} ${f}`));
|
|
5034
5136
|
});
|
|
5035
5137
|
program.command("merge <dir>").description("Merge YAML directory structure into a .flow.json").option("-o, --output <file>", "Specify output file path").action((dir, options) => {
|
|
5036
|
-
const dirPath =
|
|
5138
|
+
const dirPath = resolve3(dir);
|
|
5037
5139
|
if (!existsSync4(dirPath)) {
|
|
5038
5140
|
console.error(`\u274C Directory not found: ${dirPath}`);
|
|
5039
5141
|
process.exit(1);
|
|
@@ -5045,7 +5147,7 @@ program.command("merge <dir>").description("Merge YAML directory structure into
|
|
|
5045
5147
|
{ join: join4 }
|
|
5046
5148
|
);
|
|
5047
5149
|
const outputFile = options.output ?? `${dirPath}.flow.json`;
|
|
5048
|
-
writeFileSync3(
|
|
5150
|
+
writeFileSync3(resolve3(outputFile), JSON.stringify(ir, null, 2), "utf-8");
|
|
5049
5151
|
console.log(`\u2705 Merged to: ${outputFile}`);
|
|
5050
5152
|
console.log(` ${ir.nodes.length} nodes, ${ir.edges.length} edges`);
|
|
5051
5153
|
} catch (err) {
|
|
@@ -5054,7 +5156,7 @@ program.command("merge <dir>").description("Merge YAML directory structure into
|
|
|
5054
5156
|
}
|
|
5055
5157
|
});
|
|
5056
5158
|
program.command("migrate <file>").description("Migrate .flow.json to Git-friendly YAML directory (preserves original file)").option("--delete-json", "Delete original .flow.json after successful migration").action((file, options) => {
|
|
5057
|
-
const filePath =
|
|
5159
|
+
const filePath = resolve3(file);
|
|
5058
5160
|
if (!existsSync4(filePath)) {
|
|
5059
5161
|
console.error(`\u274C File not found: ${filePath}`);
|
|
5060
5162
|
process.exit(1);
|
|
@@ -5075,7 +5177,7 @@ program.command("migrate <file>").description("Migrate .flow.json to Git-friendl
|
|
|
5075
5177
|
}
|
|
5076
5178
|
});
|
|
5077
5179
|
program.command("trace <file> <line>").description("Trace a generated code line number back to its canvas node (Source Map)").action((file, lineStr) => {
|
|
5078
|
-
const filePath =
|
|
5180
|
+
const filePath = resolve3(file);
|
|
5079
5181
|
const lineNum = parseInt(lineStr, 10);
|
|
5080
5182
|
if (isNaN(lineNum) || lineNum < 1) {
|
|
5081
5183
|
console.error("\u274C Line number must be a positive integer");
|
|
@@ -5105,8 +5207,8 @@ program.command("trace <file> <line>").description("Trace a generated code line
|
|
|
5105
5207
|
}
|
|
5106
5208
|
});
|
|
5107
5209
|
program.command("diff <before> <after>").description("Compare semantic differences between two .flow.json files").action((beforeFile, afterFile) => {
|
|
5108
|
-
const beforePath =
|
|
5109
|
-
const afterPath =
|
|
5210
|
+
const beforePath = resolve3(beforeFile);
|
|
5211
|
+
const afterPath = resolve3(afterFile);
|
|
5110
5212
|
if (!existsSync4(beforePath)) {
|
|
5111
5213
|
console.error(`\u274C File not found: ${beforePath}`);
|
|
5112
5214
|
process.exit(1);
|
|
@@ -5128,8 +5230,8 @@ program.command("diff <before> <after>").description("Compare semantic differenc
|
|
|
5128
5230
|
console.log(formatDiff(summary));
|
|
5129
5231
|
});
|
|
5130
5232
|
program.command("env-check <file>").description("Validate that environment variables referenced in .flow.json are declared").option("-e, --env <envFile>", "Specify .env file path", ".env").action((file, options) => {
|
|
5131
|
-
const filePath =
|
|
5132
|
-
const envPath =
|
|
5233
|
+
const filePath = resolve3(file);
|
|
5234
|
+
const envPath = resolve3(options.env);
|
|
5133
5235
|
if (!existsSync4(filePath)) {
|
|
5134
5236
|
console.error(`\u274C File not found: ${filePath}`);
|
|
5135
5237
|
process.exit(1);
|
|
@@ -5146,7 +5248,7 @@ program.command("env-check <file>").description("Validate that environment varia
|
|
|
5146
5248
|
const envContent = readFileSync3(envPath, "utf-8");
|
|
5147
5249
|
declaredVars = parseEnvFile(envContent);
|
|
5148
5250
|
} else {
|
|
5149
|
-
const examplePath =
|
|
5251
|
+
const examplePath = resolve3(".env.example");
|
|
5150
5252
|
if (existsSync4(examplePath)) {
|
|
5151
5253
|
const envContent = readFileSync3(examplePath, "utf-8");
|
|
5152
5254
|
declaredVars = parseEnvFile(envContent);
|
|
@@ -5157,7 +5259,11 @@ program.command("env-check <file>").description("Validate that environment varia
|
|
|
5157
5259
|
`);
|
|
5158
5260
|
}
|
|
5159
5261
|
}
|
|
5160
|
-
const
|
|
5262
|
+
const systemEnvKeys = Object.keys(process.env).filter(
|
|
5263
|
+
(k) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(k)
|
|
5264
|
+
);
|
|
5265
|
+
const allDeclaredVars = [.../* @__PURE__ */ new Set([...declaredVars, ...systemEnvKeys])];
|
|
5266
|
+
const result = validateEnvVars(ir, allDeclaredVars);
|
|
5161
5267
|
console.log(formatEnvValidationReport(result));
|
|
5162
5268
|
if (!result.valid) {
|
|
5163
5269
|
process.exit(1);
|
|
@@ -5182,12 +5288,29 @@ async function startDevServer(options) {
|
|
|
5182
5288
|
}
|
|
5183
5289
|
});
|
|
5184
5290
|
}
|
|
5185
|
-
program.command("dev").description("Start Flow2Code visual editor (standalone dev server)").option("-p, --port <port>", "Server port", "3100").option("--no-open", "Do not auto-open browser").action(
|
|
5186
|
-
|
|
5187
|
-
|
|
5291
|
+
program.command("dev").description("Start Flow2Code visual editor (standalone dev server)").option("-p, --port <port>", "Server port", "3100").option("--no-open", "Do not auto-open browser").option("--silent", "Suppress non-error output (for CI/CD)").action((opts) => {
|
|
5292
|
+
if (opts.silent) logger.level = "silent";
|
|
5293
|
+
startDevServer(opts);
|
|
5294
|
+
});
|
|
5295
|
+
program.command("ui").description("Start Flow2Code visual editor (alias for dev)").option("-p, --port <port>", "Server port", "3100").option("--no-open", "Do not auto-open browser").option("--silent", "Suppress non-error output (for CI/CD)").action((opts) => {
|
|
5296
|
+
if (opts.silent) logger.level = "silent";
|
|
5297
|
+
startDevServer(opts);
|
|
5298
|
+
});
|
|
5299
|
+
function generateEnvExample() {
|
|
5300
|
+
const envExamplePath = resolve3(".env.example");
|
|
5301
|
+
if (!existsSync4(envExamplePath)) {
|
|
5302
|
+
writeFileSync3(
|
|
5303
|
+
envExamplePath,
|
|
5304
|
+
"# Flow2Code environment variables\n# Define API keys and sensitive information here\n",
|
|
5305
|
+
"utf-8"
|
|
5306
|
+
);
|
|
5307
|
+
console.log("\u{1F4DD} Generated .env.example");
|
|
5308
|
+
}
|
|
5309
|
+
}
|
|
5310
|
+
async function compileFileAsync(filePath, projectRoot) {
|
|
5188
5311
|
const startTime = Date.now();
|
|
5189
5312
|
try {
|
|
5190
|
-
const raw =
|
|
5313
|
+
const raw = await readFile2(filePath, "utf-8");
|
|
5191
5314
|
const ir = JSON.parse(raw);
|
|
5192
5315
|
const validation = validateFlowIR(ir);
|
|
5193
5316
|
if (!validation.valid) {
|
|
@@ -5208,9 +5331,9 @@ function compileFile(filePath, projectRoot) {
|
|
|
5208
5331
|
const outputPath = join4(projectRoot, result.filePath);
|
|
5209
5332
|
const outputDir = dirname4(outputPath);
|
|
5210
5333
|
if (!existsSync4(outputDir)) {
|
|
5211
|
-
|
|
5334
|
+
await mkdir(outputDir, { recursive: true });
|
|
5212
5335
|
}
|
|
5213
|
-
|
|
5336
|
+
await writeFile(outputPath, result.code, "utf-8");
|
|
5214
5337
|
const elapsed = Date.now() - startTime;
|
|
5215
5338
|
console.log(`\u2705 [${elapsed}ms] ${filePath} \u2192 ${outputPath}`);
|
|
5216
5339
|
} catch (err) {
|
|
@@ -5219,7 +5342,7 @@ function compileFile(filePath, projectRoot) {
|
|
|
5219
5342
|
);
|
|
5220
5343
|
}
|
|
5221
5344
|
}
|
|
5222
|
-
function
|
|
5345
|
+
async function compileFlowDirAsync(dirPath, projectRoot) {
|
|
5223
5346
|
const startTime = Date.now();
|
|
5224
5347
|
try {
|
|
5225
5348
|
const project = loadFlowProject(dirPath);
|
|
@@ -5243,9 +5366,9 @@ function compileFlowDir(dirPath, projectRoot) {
|
|
|
5243
5366
|
const outputPath = join4(projectRoot, result.filePath);
|
|
5244
5367
|
const outputDir = dirname4(outputPath);
|
|
5245
5368
|
if (!existsSync4(outputDir)) {
|
|
5246
|
-
|
|
5369
|
+
await mkdir(outputDir, { recursive: true });
|
|
5247
5370
|
}
|
|
5248
|
-
|
|
5371
|
+
await writeFile(outputPath, result.code, "utf-8");
|
|
5249
5372
|
const elapsed = Date.now() - startTime;
|
|
5250
5373
|
console.log(`\u2705 [${elapsed}ms] ${dirPath}/ \u2192 ${outputPath}`);
|
|
5251
5374
|
} catch (err) {
|
|
@@ -5254,17 +5377,6 @@ function compileFlowDir(dirPath, projectRoot) {
|
|
|
5254
5377
|
);
|
|
5255
5378
|
}
|
|
5256
5379
|
}
|
|
5257
|
-
function generateEnvExample() {
|
|
5258
|
-
const envExamplePath = resolve2(".env.example");
|
|
5259
|
-
if (!existsSync4(envExamplePath)) {
|
|
5260
|
-
writeFileSync3(
|
|
5261
|
-
envExamplePath,
|
|
5262
|
-
"# Flow2Code environment variables\n# Define API keys and sensitive information here\n",
|
|
5263
|
-
"utf-8"
|
|
5264
|
-
);
|
|
5265
|
-
console.log("\u{1F4DD} Generated .env.example");
|
|
5266
|
-
}
|
|
5267
|
-
}
|
|
5268
5380
|
if (process.argv.length <= 2) {
|
|
5269
5381
|
program.help();
|
|
5270
5382
|
}
|