@timo9378/flow2code 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +15 -2
  3. package/dist/cli.js +101998 -816
  4. package/dist/compiler.cjs +66 -10
  5. package/dist/compiler.d.cts +6 -0
  6. package/dist/compiler.d.ts +6 -0
  7. package/dist/compiler.js +66 -10
  8. package/dist/server.d.ts +1 -1
  9. package/dist/server.js +102689 -1378
  10. package/out/404.html +1 -1
  11. package/out/__next.__PAGE__.txt +4 -4
  12. package/out/__next._full.txt +13 -13
  13. package/out/__next._head.txt +4 -4
  14. package/out/__next._index.txt +6 -6
  15. package/out/__next._tree.txt +2 -2
  16. package/out/_next/static/chunks/05328cd26bdc795c.js +176 -0
  17. package/out/_next/static/chunks/06e01c846ae01892.js +1 -0
  18. package/out/_next/static/chunks/1011f174944c0ca2.js +70 -0
  19. package/out/_next/static/chunks/1570e9ba5f1b44ed.js +5 -0
  20. package/out/_next/static/chunks/6167fccccde2e675.css +1 -0
  21. package/out/_next/static/chunks/{b112c2f519e4b429.js → 7cd04052abfadac1.js} +1 -1
  22. package/out/_next/static/chunks/8091c1216a95d294.js +1 -0
  23. package/out/_next/static/chunks/98d53aae29c36c6b.js +1 -0
  24. package/out/_next/static/chunks/a6dad97d9634a72d.js.map +1 -1
  25. package/out/_next/static/chunks/{b163b5d7cccbcf42.js → b05daf00cdc6058f.js} +1 -1
  26. package/out/_next/static/chunks/b3419ee3e3a616d9.js +1 -0
  27. package/out/_next/static/chunks/{acf223168ac429f7.js → be40d79540010a0d.js} +1 -1
  28. package/out/_next/static/chunks/{turbopack-576234c945ffdc44.js → turbopack-9da9810f42c97265.js} +1 -1
  29. package/out/_not-found/__next._full.txt +11 -11
  30. package/out/_not-found/__next._head.txt +4 -4
  31. package/out/_not-found/__next._index.txt +6 -6
  32. package/out/_not-found/__next._not-found/__PAGE__.txt +2 -2
  33. package/out/_not-found/__next._not-found.txt +3 -3
  34. package/out/_not-found/__next._tree.txt +2 -2
  35. package/out/_not-found.html +1 -1
  36. package/out/_not-found.txt +11 -11
  37. package/out/index.html +2 -2
  38. package/out/index.txt +13 -13
  39. package/package.json +130 -124
  40. package/out/_next/static/chunks/06054f68c210e89c.js +0 -125
  41. package/out/_next/static/chunks/0bc0a50347ee5f3c.js +0 -51
  42. package/out/_next/static/chunks/6b84376656bd9887.js +0 -1
  43. package/out/_next/static/chunks/83ab8820627f8bfe.css +0 -1
  44. package/out/_next/static/chunks/ab8888d4b78b94be.js +0 -5
  45. package/out/_next/static/chunks/b6e8711267bccbbd.js +0 -1
  46. package/out/_next/static/chunks/fbca595129527827.js +0 -1
  47. package/scripts/publish-all.sh +0 -56
  48. /package/out/_next/static/{EFK7prtbW4K3cbFdFFkDA → hNOHolNViTvQRpVwfaIx-}/_buildManifest.js +0 -0
  49. /package/out/_next/static/{EFK7prtbW4K3cbFdFFkDA → hNOHolNViTvQRpVwfaIx-}/_clientMiddlewareManifest.json +0 -0
  50. /package/out/_next/static/{EFK7prtbW4K3cbFdFFkDA → hNOHolNViTvQRpVwfaIx-}/_ssgManifest.js +0 -0
package/dist/compiler.cjs CHANGED
@@ -238,6 +238,17 @@ function validateFlowIR(ir) {
238
238
  }
239
239
  idSet.add(node.id);
240
240
  }
241
+ const edgeIdSet = /* @__PURE__ */ new Set();
242
+ for (const edge of workingIR.edges) {
243
+ if (edgeIdSet.has(edge.id)) {
244
+ errors.push({
245
+ code: "DUPLICATE_EDGE_ID",
246
+ message: `Duplicate edge ID: ${edge.id}`,
247
+ edgeId: edge.id
248
+ });
249
+ }
250
+ edgeIdSet.add(edge.id);
251
+ }
241
252
  for (const edge of workingIR.edges) {
242
253
  if (!workingNodeMap.has(edge.sourceNodeId)) {
243
254
  errors.push({
@@ -270,14 +281,38 @@ function validateFlowIR(ir) {
270
281
  });
271
282
  }
272
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
+ }
273
300
  return {
274
- valid: errors.length === 0,
301
+ valid: errors.filter((e) => e.severity !== "warning" && e.severity !== "info").length === 0,
275
302
  errors,
276
303
  migrated,
277
304
  migratedIR: migrated ? workingIR : void 0,
278
305
  migrationLog
279
306
  };
280
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
+ }
281
316
  function detectCycles(nodes, edges) {
282
317
  const errors = [];
283
318
  const adjacency = /* @__PURE__ */ new Map();
@@ -463,7 +498,7 @@ function tokenize(input) {
463
498
  return tokens;
464
499
  }
465
500
  function parseReference(ref) {
466
- const match = ref.match(/^(\$?\w+)((?:\.[\w]+|\[.+?\])*)$/);
501
+ const match = ref.match(/^(\$?[\w-]+)((?:\.[\w]+|\[.+?\])*)$/);
467
502
  if (!match) {
468
503
  const dotIndex = ref.indexOf(".");
469
504
  const bracketIndex = ref.indexOf("[");
@@ -1837,6 +1872,9 @@ var RESERVED_WORDS = /* @__PURE__ */ new Set([
1837
1872
  ]);
1838
1873
 
1839
1874
  // src/lib/compiler/compiler.ts
1875
+ registerPlatform("nextjs", () => new NextjsPlatform());
1876
+ registerPlatform("express", () => new ExpressPlatform());
1877
+ registerPlatform("cloudflare", () => new CloudflarePlatform());
1840
1878
  function compile(ir, options) {
1841
1879
  const pluginRegistry = createPluginRegistry();
1842
1880
  pluginRegistry.registerAll(builtinPlugins);
@@ -1914,7 +1952,7 @@ function compile(ir, options) {
1914
1952
  indentSize: 2,
1915
1953
  convertTabsToSpaces: true
1916
1954
  });
1917
- const code = sourceFile.getFullText();
1955
+ let code = sourceFile.getFullText();
1918
1956
  const filePath = platform.getOutputFilePath(trigger);
1919
1957
  collectRequiredPackages(workingIR, context);
1920
1958
  const sourceMap = buildSourceMap(code, workingIR, filePath);
@@ -2255,13 +2293,15 @@ function sanitizeId(id) {
2255
2293
  function resolveEnvVars(url, context) {
2256
2294
  const hasEnvVar = /\$\{(\w+)\}/.test(url);
2257
2295
  if (hasEnvVar) {
2258
- return "`" + url.replace(/\$\{(\w+)\}/g, (_match, varName) => {
2296
+ const escaped = url.replace(/\\/g, "\\\\").replace(/`/g, "\\`");
2297
+ return "`" + escaped.replace(/\$\{(\w+)\}/g, (_match, varName) => {
2259
2298
  context.envVars.add(varName);
2260
2299
  return "${process.env." + varName + "}";
2261
2300
  }) + "`";
2262
2301
  }
2263
2302
  if (url.includes("${")) {
2264
- return "`" + url + "`";
2303
+ const escaped = url.replace(/\\/g, "\\\\").replace(/`/g, "\\`");
2304
+ return "`" + escaped + "`";
2265
2305
  }
2266
2306
  return `"${url}"`;
2267
2307
  }
@@ -2323,9 +2363,18 @@ function buildSourceMap(code, ir, filePath) {
2323
2363
  };
2324
2364
  }
2325
2365
  function traceLineToNode(sourceMap, line) {
2326
- for (const [nodeId, range] of Object.entries(sourceMap.mappings)) {
2327
- if (line >= range.startLine && line <= range.endLine) {
2328
- return { nodeId, ...range };
2366
+ const entries = Object.entries(sourceMap.mappings).map(([nodeId, range]) => ({ nodeId, ...range })).sort((a, b) => a.startLine - b.startLine);
2367
+ let lo = 0;
2368
+ let hi = entries.length - 1;
2369
+ while (lo <= hi) {
2370
+ const mid = lo + hi >>> 1;
2371
+ const e = entries[mid];
2372
+ if (line < e.startLine) {
2373
+ hi = mid - 1;
2374
+ } else if (line > e.endLine) {
2375
+ lo = mid + 1;
2376
+ } else {
2377
+ return e;
2329
2378
  }
2330
2379
  }
2331
2380
  return null;
@@ -3353,6 +3402,7 @@ function withFlowTrace(handler, options) {
3353
3402
  formatTraceResults(err, traces, sourceMap.generatedFile)
3354
3403
  );
3355
3404
  }
3405
+ options.onTrace?.(traces, err);
3356
3406
  }
3357
3407
  throw err;
3358
3408
  }
@@ -3361,6 +3411,7 @@ function withFlowTrace(handler, options) {
3361
3411
  }
3362
3412
  function installFlowTracer(options) {
3363
3413
  const { sourceMap, ir, editorUrl = "http://localhost:3001", log = true } = options;
3414
+ const { onTrace } = options;
3364
3415
  const handleError = (err) => {
3365
3416
  if (!(err instanceof Error)) return;
3366
3417
  const traces = traceError(err, sourceMap, ir, editorUrl);
@@ -3369,6 +3420,7 @@ function installFlowTracer(options) {
3369
3420
  formatTraceResults(err, traces, sourceMap.generatedFile)
3370
3421
  );
3371
3422
  }
3423
+ onTrace?.(traces, err);
3372
3424
  };
3373
3425
  process.on("uncaughtException", handleError);
3374
3426
  process.on("unhandledRejection", handleError);
@@ -3763,8 +3815,12 @@ function mergeIR(files) {
3763
3815
  }
3764
3816
  const edgesRaw = (0, import_yaml.parse)(files.edges);
3765
3817
  const edges = (edgesRaw ?? []).map((e) => {
3766
- const [sourceNodeId, sourcePortId] = e.source.split(":");
3767
- const [targetNodeId, targetPortId] = e.target.split(":");
3818
+ const srcIdx = e.source.indexOf(":");
3819
+ const tgtIdx = e.target.indexOf(":");
3820
+ const sourceNodeId = srcIdx > 0 ? e.source.slice(0, srcIdx) : e.source;
3821
+ const sourcePortId = srcIdx > 0 ? e.source.slice(srcIdx + 1) : "output";
3822
+ const targetNodeId = tgtIdx > 0 ? e.target.slice(0, tgtIdx) : e.target;
3823
+ const targetPortId = tgtIdx > 0 ? e.target.slice(tgtIdx + 1) : "input";
3768
3824
  return {
3769
3825
  id: e.id,
3770
3826
  sourceNodeId,
@@ -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
@@ -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
@@ -171,6 +171,17 @@ function validateFlowIR(ir) {
171
171
  }
172
172
  idSet.add(node.id);
173
173
  }
174
+ const edgeIdSet = /* @__PURE__ */ new Set();
175
+ for (const edge of workingIR.edges) {
176
+ if (edgeIdSet.has(edge.id)) {
177
+ errors.push({
178
+ code: "DUPLICATE_EDGE_ID",
179
+ message: `Duplicate edge ID: ${edge.id}`,
180
+ edgeId: edge.id
181
+ });
182
+ }
183
+ edgeIdSet.add(edge.id);
184
+ }
174
185
  for (const edge of workingIR.edges) {
175
186
  if (!workingNodeMap.has(edge.sourceNodeId)) {
176
187
  errors.push({
@@ -203,14 +214,38 @@ function validateFlowIR(ir) {
203
214
  });
204
215
  }
205
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
+ }
206
233
  return {
207
- valid: errors.length === 0,
234
+ valid: errors.filter((e) => e.severity !== "warning" && e.severity !== "info").length === 0,
208
235
  errors,
209
236
  migrated,
210
237
  migratedIR: migrated ? workingIR : void 0,
211
238
  migrationLog
212
239
  };
213
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
+ }
214
249
  function detectCycles(nodes, edges) {
215
250
  const errors = [];
216
251
  const adjacency = /* @__PURE__ */ new Map();
@@ -396,7 +431,7 @@ function tokenize(input) {
396
431
  return tokens;
397
432
  }
398
433
  function parseReference(ref) {
399
- const match = ref.match(/^(\$?\w+)((?:\.[\w]+|\[.+?\])*)$/);
434
+ const match = ref.match(/^(\$?[\w-]+)((?:\.[\w]+|\[.+?\])*)$/);
400
435
  if (!match) {
401
436
  const dotIndex = ref.indexOf(".");
402
437
  const bracketIndex = ref.indexOf("[");
@@ -1770,6 +1805,9 @@ var RESERVED_WORDS = /* @__PURE__ */ new Set([
1770
1805
  ]);
1771
1806
 
1772
1807
  // src/lib/compiler/compiler.ts
1808
+ registerPlatform("nextjs", () => new NextjsPlatform());
1809
+ registerPlatform("express", () => new ExpressPlatform());
1810
+ registerPlatform("cloudflare", () => new CloudflarePlatform());
1773
1811
  function compile(ir, options) {
1774
1812
  const pluginRegistry = createPluginRegistry();
1775
1813
  pluginRegistry.registerAll(builtinPlugins);
@@ -1847,7 +1885,7 @@ function compile(ir, options) {
1847
1885
  indentSize: 2,
1848
1886
  convertTabsToSpaces: true
1849
1887
  });
1850
- const code = sourceFile.getFullText();
1888
+ let code = sourceFile.getFullText();
1851
1889
  const filePath = platform.getOutputFilePath(trigger);
1852
1890
  collectRequiredPackages(workingIR, context);
1853
1891
  const sourceMap = buildSourceMap(code, workingIR, filePath);
@@ -2188,13 +2226,15 @@ function sanitizeId(id) {
2188
2226
  function resolveEnvVars(url, context) {
2189
2227
  const hasEnvVar = /\$\{(\w+)\}/.test(url);
2190
2228
  if (hasEnvVar) {
2191
- return "`" + url.replace(/\$\{(\w+)\}/g, (_match, varName) => {
2229
+ const escaped = url.replace(/\\/g, "\\\\").replace(/`/g, "\\`");
2230
+ return "`" + escaped.replace(/\$\{(\w+)\}/g, (_match, varName) => {
2192
2231
  context.envVars.add(varName);
2193
2232
  return "${process.env." + varName + "}";
2194
2233
  }) + "`";
2195
2234
  }
2196
2235
  if (url.includes("${")) {
2197
- return "`" + url + "`";
2236
+ const escaped = url.replace(/\\/g, "\\\\").replace(/`/g, "\\`");
2237
+ return "`" + escaped + "`";
2198
2238
  }
2199
2239
  return `"${url}"`;
2200
2240
  }
@@ -2256,9 +2296,18 @@ function buildSourceMap(code, ir, filePath) {
2256
2296
  };
2257
2297
  }
2258
2298
  function traceLineToNode(sourceMap, line) {
2259
- for (const [nodeId, range] of Object.entries(sourceMap.mappings)) {
2260
- if (line >= range.startLine && line <= range.endLine) {
2261
- return { nodeId, ...range };
2299
+ const entries = Object.entries(sourceMap.mappings).map(([nodeId, range]) => ({ nodeId, ...range })).sort((a, b) => a.startLine - b.startLine);
2300
+ let lo = 0;
2301
+ let hi = entries.length - 1;
2302
+ while (lo <= hi) {
2303
+ const mid = lo + hi >>> 1;
2304
+ const e = entries[mid];
2305
+ if (line < e.startLine) {
2306
+ hi = mid - 1;
2307
+ } else if (line > e.endLine) {
2308
+ lo = mid + 1;
2309
+ } else {
2310
+ return e;
2262
2311
  }
2263
2312
  }
2264
2313
  return null;
@@ -3289,6 +3338,7 @@ function withFlowTrace(handler, options) {
3289
3338
  formatTraceResults(err, traces, sourceMap.generatedFile)
3290
3339
  );
3291
3340
  }
3341
+ options.onTrace?.(traces, err);
3292
3342
  }
3293
3343
  throw err;
3294
3344
  }
@@ -3297,6 +3347,7 @@ function withFlowTrace(handler, options) {
3297
3347
  }
3298
3348
  function installFlowTracer(options) {
3299
3349
  const { sourceMap, ir, editorUrl = "http://localhost:3001", log = true } = options;
3350
+ const { onTrace } = options;
3300
3351
  const handleError = (err) => {
3301
3352
  if (!(err instanceof Error)) return;
3302
3353
  const traces = traceError(err, sourceMap, ir, editorUrl);
@@ -3305,6 +3356,7 @@ function installFlowTracer(options) {
3305
3356
  formatTraceResults(err, traces, sourceMap.generatedFile)
3306
3357
  );
3307
3358
  }
3359
+ onTrace?.(traces, err);
3308
3360
  };
3309
3361
  process.on("uncaughtException", handleError);
3310
3362
  process.on("unhandledRejection", handleError);
@@ -3699,8 +3751,12 @@ function mergeIR(files) {
3699
3751
  }
3700
3752
  const edgesRaw = parse(files.edges);
3701
3753
  const edges = (edgesRaw ?? []).map((e) => {
3702
- const [sourceNodeId, sourcePortId] = e.source.split(":");
3703
- const [targetNodeId, targetPortId] = e.target.split(":");
3754
+ const srcIdx = e.source.indexOf(":");
3755
+ const tgtIdx = e.target.indexOf(":");
3756
+ const sourceNodeId = srcIdx > 0 ? e.source.slice(0, srcIdx) : e.source;
3757
+ const sourcePortId = srcIdx > 0 ? e.source.slice(srcIdx + 1) : "output";
3758
+ const targetNodeId = tgtIdx > 0 ? e.target.slice(0, tgtIdx) : e.target;
3759
+ const targetPortId = tgtIdx > 0 ? e.target.slice(tgtIdx + 1) : "input";
3704
3760
  return {
3705
3761
  id: e.id,
3706
3762
  sourceNodeId,
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;