@timo9378/flow2code 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.
@@ -0,0 +1,1230 @@
1
+ import { SourceFile, CodeBlockWriter } from 'ts-morph';
2
+
3
+ /**
4
+ * Flow2Code Intermediate Representation (IR) Schema
5
+ *
6
+ * This is the sole contract between the visual canvas and the AST compiler.
7
+ * All node types, edges, and parameters are defined here.
8
+ *
9
+ * Design principles:
10
+ * 1. Fully decoupled from UI — IR contains no coordinate or style information
11
+ * 2. Type-safe — each node type has strictly typed parameters
12
+ * 3. Serializable — entire IR can be directly JSON.stringify/parse
13
+ */
14
+ /** Current supported IR version number */
15
+ declare const CURRENT_IR_VERSION = "1.0.0";
16
+ /** Node unique identifier */
17
+ type NodeId = string;
18
+ /** Port identifier */
19
+ type PortId = string;
20
+ /** Supported data types */
21
+ type FlowDataType = "string" | "number" | "boolean" | "object" | "array" | "any" | "void" | "Response";
22
+ /** Input port */
23
+ interface InputPort {
24
+ id: PortId;
25
+ label: string;
26
+ dataType: FlowDataType;
27
+ required: boolean;
28
+ /** Default value (JSON serialized) */
29
+ defaultValue?: string;
30
+ }
31
+ /** Output port */
32
+ interface OutputPort {
33
+ id: PortId;
34
+ label: string;
35
+ dataType: FlowDataType;
36
+ }
37
+ /** Data flow connection between nodes */
38
+ interface FlowEdge {
39
+ id: string;
40
+ /** Source node ID */
41
+ sourceNodeId: NodeId;
42
+ /** Source port ID */
43
+ sourcePortId: PortId;
44
+ /** Target node ID */
45
+ targetNodeId: NodeId;
46
+ /** Target port ID */
47
+ targetPortId: PortId;
48
+ }
49
+ declare enum NodeCategory {
50
+ /** Trigger: workflow entry point */
51
+ TRIGGER = "trigger",
52
+ /** Action: side-effect producing operation */
53
+ ACTION = "action",
54
+ /** Logic: branching, loops, exception handling */
55
+ LOGIC = "logic",
56
+ /** Variable: define or transform data */
57
+ VARIABLE = "variable",
58
+ /** Output: return result */
59
+ OUTPUT = "output"
60
+ }
61
+ declare enum TriggerType {
62
+ HTTP_WEBHOOK = "http_webhook",
63
+ CRON_JOB = "cron_job",
64
+ MANUAL = "manual"
65
+ }
66
+ declare enum ActionType {
67
+ FETCH_API = "fetch_api",
68
+ SQL_QUERY = "sql_query",
69
+ REDIS_CACHE = "redis_cache",
70
+ CUSTOM_CODE = "custom_code",
71
+ CALL_SUBFLOW = "call_subflow"
72
+ }
73
+ declare enum LogicType {
74
+ IF_ELSE = "if_else",
75
+ FOR_LOOP = "for_loop",
76
+ TRY_CATCH = "try_catch",
77
+ PROMISE_ALL = "promise_all"
78
+ }
79
+ declare enum VariableType {
80
+ DECLARE = "declare",
81
+ TRANSFORM = "transform"
82
+ }
83
+ declare enum OutputType {
84
+ RETURN_RESPONSE = "return_response"
85
+ }
86
+ /** HTTP Webhook trigger parameters */
87
+ interface HttpWebhookParams {
88
+ method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
89
+ /** Route path, e.g. "/api/users" */
90
+ routePath: string;
91
+ /** Whether to parse JSON body */
92
+ parseBody: boolean;
93
+ /** Query parameter definitions */
94
+ queryParams?: Array<{
95
+ name: string;
96
+ type: FlowDataType;
97
+ required: boolean;
98
+ }>;
99
+ }
100
+ /** Cron Job trigger parameters */
101
+ interface CronJobParams {
102
+ /** Cron expression */
103
+ schedule: string;
104
+ /** Function name */
105
+ functionName: string;
106
+ }
107
+ /** Manual trigger parameters */
108
+ interface ManualTriggerParams {
109
+ /** Exported function name */
110
+ functionName: string;
111
+ /** Function parameter definitions */
112
+ args: Array<{
113
+ name: string;
114
+ type: FlowDataType;
115
+ }>;
116
+ }
117
+ /** Fetch API action parameters */
118
+ interface FetchApiParams {
119
+ /** Request URL (supports env var references like ${ENV_VAR}) */
120
+ url: string;
121
+ method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
122
+ /** Request headers */
123
+ headers?: Record<string, string>;
124
+ /** Request body (JSON string or reference expression) */
125
+ body?: string;
126
+ /** Whether to auto-parse JSON response */
127
+ parseJson: boolean;
128
+ }
129
+ /** SQL Query action parameters */
130
+ interface SqlQueryParams {
131
+ /** ORM type */
132
+ orm: "drizzle" | "prisma" | "raw";
133
+ /** SQL or ORM query statement */
134
+ query: string;
135
+ /** Query parameter bindings */
136
+ params?: Array<{
137
+ name: string;
138
+ value: string;
139
+ }>;
140
+ }
141
+ /** Redis Cache action parameters */
142
+ interface RedisCacheParams {
143
+ operation: "get" | "set" | "del";
144
+ key: string;
145
+ /** Value for set operation */
146
+ value?: string;
147
+ /** TTL (seconds) */
148
+ ttl?: number;
149
+ }
150
+ /** Custom code parameters */
151
+ interface CustomCodeParams {
152
+ /** TypeScript code snippet */
153
+ code: string;
154
+ /** Name of the return variable */
155
+ returnVariable?: string;
156
+ /** Explicitly specify the return type (for type inference, e.g. "User[]" or "{ count: number }") */
157
+ returnType?: string;
158
+ }
159
+ /** Subflow call parameters */
160
+ interface CallSubflowParams {
161
+ /** Import path of the subflow (combined with compiler-generated file) */
162
+ flowPath: string;
163
+ /** Exported function name of the subflow */
164
+ functionName: string;
165
+ /** Input parameter mapping: param name → expression (supports template syntax) */
166
+ inputMapping: Record<string, string>;
167
+ /** Explicit subflow return type (optional, defaults to TypeScript inference) */
168
+ returnType?: string;
169
+ }
170
+ /** If/Else logic control parameters */
171
+ interface IfElseParams {
172
+ /** Condition expression (TypeScript expression string) */
173
+ condition: string;
174
+ }
175
+ /** For Loop logic control parameters */
176
+ interface ForLoopParams {
177
+ /** Iteration target (variable reference expression) */
178
+ iterableExpression: string;
179
+ /** Iterator variable name */
180
+ itemVariable: string;
181
+ /** Index variable name (optional) */
182
+ indexVariable?: string;
183
+ }
184
+ /** Try/Catch logic control parameters */
185
+ interface TryCatchParams {
186
+ /** Error variable name */
187
+ errorVariable: string;
188
+ }
189
+ /** Promise.all concurrency control parameters */
190
+ interface PromiseAllParams {
191
+ /** No additional params. Concurrent tasks are determined by connected child nodes */
192
+ [key: string]: never;
193
+ }
194
+ /** Variable declaration parameters */
195
+ interface DeclareVariableParams {
196
+ /** Variable name */
197
+ name: string;
198
+ /** Variable type */
199
+ dataType: FlowDataType;
200
+ /** Initial value expression */
201
+ initialValue?: string;
202
+ /** Whether const */
203
+ isConst: boolean;
204
+ }
205
+ /** Data transform parameters */
206
+ interface TransformParams {
207
+ /** Transform expression (TypeScript expression) */
208
+ expression: string;
209
+ }
210
+ /** Return Response parameters */
211
+ interface ReturnResponseParams {
212
+ /** HTTP status code */
213
+ statusCode: number;
214
+ /** Response body expression */
215
+ bodyExpression: string;
216
+ /** Custom response headers */
217
+ headers?: Record<string, string>;
218
+ }
219
+ interface NodeParamsMap {
220
+ [TriggerType.HTTP_WEBHOOK]: HttpWebhookParams;
221
+ [TriggerType.CRON_JOB]: CronJobParams;
222
+ [TriggerType.MANUAL]: ManualTriggerParams;
223
+ [ActionType.FETCH_API]: FetchApiParams;
224
+ [ActionType.SQL_QUERY]: SqlQueryParams;
225
+ [ActionType.REDIS_CACHE]: RedisCacheParams;
226
+ [ActionType.CUSTOM_CODE]: CustomCodeParams;
227
+ [ActionType.CALL_SUBFLOW]: CallSubflowParams;
228
+ [LogicType.IF_ELSE]: IfElseParams;
229
+ [LogicType.FOR_LOOP]: ForLoopParams;
230
+ [LogicType.TRY_CATCH]: TryCatchParams;
231
+ [LogicType.PROMISE_ALL]: PromiseAllParams;
232
+ [VariableType.DECLARE]: DeclareVariableParams;
233
+ [VariableType.TRANSFORM]: TransformParams;
234
+ [OutputType.RETURN_RESPONSE]: ReturnResponseParams;
235
+ }
236
+ type NodeType = keyof NodeParamsMap;
237
+ /** Generic node, params type inferred from nodeType */
238
+ interface FlowNode<T extends NodeType = NodeType> {
239
+ /** Node unique identifier */
240
+ id: NodeId;
241
+ /** Node type */
242
+ nodeType: T;
243
+ /** Category */
244
+ category: NodeCategory;
245
+ /** User-defined node label */
246
+ label: string;
247
+ /** Node parameters */
248
+ params: NodeParamsMap[T];
249
+ /** Input ports */
250
+ inputs: InputPort[];
251
+ /** Output ports */
252
+ outputs: OutputPort[];
253
+ }
254
+ /** Flow2Code IR Document — the sole contract between canvas and compiler */
255
+ interface FlowIR {
256
+ /** IR version */
257
+ version: string;
258
+ /** Workflow summary */
259
+ meta: {
260
+ name: string;
261
+ description?: string;
262
+ createdAt: string;
263
+ updatedAt: string;
264
+ };
265
+ /** All nodes */
266
+ nodes: FlowNode[];
267
+ /** All edges */
268
+ edges: FlowEdge[];
269
+ }
270
+
271
+ /**
272
+ * Flow2Code Symbol Table
273
+ *
274
+ * Maps node IDs to human-readable variable names (derived from node labels).
275
+ * Enables "ejectable" code generation — generated TypeScript looks like hand-written code.
276
+ *
277
+ * Naming strategy:
278
+ * "GET /api/hello" → getApiHello
279
+ * "Fetch Available Models" → fetchAvailableModels
280
+ * "Return Hello" → returnHello
281
+ * "Check Valid" → checkValid
282
+ * "Call External API" → callExternalApi
283
+ *
284
+ * Conflict resolution: duplicate variable names auto-suffixed with _2, _3 ...
285
+ * Reserved word protection: JS/TS reserved words + common identifiers in generated code auto-suffixed with Result
286
+ */
287
+
288
+ interface SymbolTable {
289
+ /** Get the human-readable variable name for a node */
290
+ getVarName(nodeId: NodeId): string;
291
+ /** Check if a node has a named variable */
292
+ hasVar(nodeId: NodeId): boolean;
293
+ /** Get all mappings */
294
+ getAllMappings(): ReadonlyMap<NodeId, string>;
295
+ }
296
+
297
+ /**
298
+ * Platform Adapter Interface
299
+ *
300
+ * Abstracts HTTP framework implementations so the compiler is no longer coupled to Next.js.
301
+ * Developers can choose different target platforms to generate corresponding code.
302
+ *
303
+ * Supported platforms:
304
+ * - nextjs (default) Next.js App Router
305
+ * - express Express.js
306
+ * - cloudflare Cloudflare Workers
307
+ * - generic Generic TypeScript (pure functions, framework-agnostic)
308
+ */
309
+
310
+ interface PlatformAdapter {
311
+ /** Platform name */
312
+ readonly name: string;
313
+ /**
314
+ * Generate import statements.
315
+ * e.g. Next.js: `import { NextResponse } from "next/server"`
316
+ */
317
+ generateImports(sourceFile: SourceFile, trigger: FlowNode, context: PlatformContext): void;
318
+ /**
319
+ * Generate the exported main function (including signature, parameters, outer try/catch).
320
+ * The bodyGenerator callback is responsible for filling in the function body logic.
321
+ */
322
+ generateFunction(sourceFile: SourceFile, trigger: FlowNode, context: PlatformContext, bodyGenerator: (writer: CodeBlockWriter) => void): void;
323
+ /**
324
+ * Generate code that returns a Response.
325
+ */
326
+ generateResponse(writer: CodeBlockWriter, bodyExpr: string, statusCode: number, headers?: Record<string, string>): void;
327
+ /**
328
+ * Generate the error response code for the global error handler.
329
+ */
330
+ generateErrorResponse(writer: CodeBlockWriter): void;
331
+ /**
332
+ * Generate initialization code based on trigger type (parse request body/query, etc.).
333
+ * Each platform uses its own HTTP API (Next.js / Express / Cloudflare Workers).
334
+ */
335
+ generateTriggerInit(writer: CodeBlockWriter, trigger: FlowNode, context: TriggerInitContext): void;
336
+ /**
337
+ * Get the output file path.
338
+ */
339
+ getOutputFilePath(trigger: FlowNode): string;
340
+ /**
341
+ * Implicit npm dependencies for this platform.
342
+ */
343
+ getImplicitDependencies(): string[];
344
+ }
345
+ interface PlatformContext {
346
+ ir: FlowIR;
347
+ nodeMap: Map<NodeId, FlowNode>;
348
+ envVars: Set<string>;
349
+ imports: Map<string, Set<string>>;
350
+ }
351
+ interface TriggerInitContext {
352
+ symbolTable: SymbolTable;
353
+ }
354
+ /**
355
+ * Built-in platform names (extensible: third parties can register any string via registerPlatform).
356
+ */
357
+ type BuiltinPlatformName = "nextjs" | "express" | "cloudflare";
358
+ type PlatformName = BuiltinPlatformName | (string & {});
359
+ /**
360
+ * Register a custom platform adapter.
361
+ *
362
+ * Allows users to extend the compiler's supported target platforms (e.g. Fastify, Koa, etc.).
363
+ * The factory function is lazily invoked on each compile to avoid global state pollution.
364
+ *
365
+ * @param name - Platform name, can use a built-in name or a custom string
366
+ * @param factory - Platform adapter factory function
367
+ *
368
+ * @example
369
+ * ```ts
370
+ * import { registerPlatform } from "flow2code";
371
+ *
372
+ * registerPlatform("fastify", () => ({
373
+ * imports: () => ['import Fastify from "fastify";'],
374
+ * routeWrapper: (method, path, body) => `app.${method}("${path}", async (req, reply) => {\n${body}\n});`,
375
+ * response: (statusExpr, bodyExpr) => `reply.status(${statusExpr}).send(${bodyExpr});`,
376
+ * appSetup: () => 'const app = Fastify();',
377
+ * listen: (port) => `app.listen({ port: ${port} });`,
378
+ * }));
379
+ * ```
380
+ */
381
+ declare function registerPlatform(name: PlatformName, factory: () => PlatformAdapter): void;
382
+ declare function getPlatform(name: PlatformName): PlatformAdapter;
383
+ declare function getAvailablePlatforms(): string[];
384
+
385
+ /**
386
+ * Node Plugin Interface
387
+ *
388
+ * Allows developers to register custom node code generators without modifying compiler source code.
389
+ *
390
+ * Usage example:
391
+ * ```ts
392
+ * import { registerPlugin } from "flow2code/compiler";
393
+ *
394
+ * registerPlugin({
395
+ * nodeType: "aws_ses_email",
396
+ * generate(node, writer, context) {
397
+ * writer.writeLine(`await ses.sendEmail({ ... });`);
398
+ * writer.writeLine(`flowState['${node.id}'] = { sent: true };`);
399
+ * },
400
+ * getRequiredPackages: () => ["@aws-sdk/client-ses"],
401
+ * getOutputType: () => "{ sent: boolean }",
402
+ * });
403
+ * ```
404
+ */
405
+
406
+ interface PluginContext {
407
+ ir: FlowIR;
408
+ nodeMap: Map<NodeId, FlowNode>;
409
+ envVars: Set<string>;
410
+ imports: Map<string, Set<string>>;
411
+ requiredPackages: Set<string>;
412
+ /** Resolve template expressions (automatically uses expression parser) */
413
+ resolveExpression: (expr: string, currentNodeId?: NodeId) => string;
414
+ /** Resolve environment variable references */
415
+ resolveEnvVars: (url: string) => string;
416
+ /** Generate child node code (used for branching nodes like if/else, try/catch) */
417
+ generateChildNode: (writer: CodeBlockWriter, node: FlowNode) => void;
418
+ /** Get the human-readable variable name for a node (provided by Symbol Table) */
419
+ getVarName: (nodeId: NodeId) => string;
420
+ /**
421
+ * Push a local scope layer.
422
+ * Within this scope, all expression references to nodeId will resolve to scopeVar.
423
+ */
424
+ pushScope: (nodeId: NodeId, scopeVar: string) => void;
425
+ /**
426
+ * Pop the innermost local scope.
427
+ */
428
+ popScope: () => void;
429
+ }
430
+ interface NodePlugin {
431
+ /** Node type identifier (corresponds to FlowNode.nodeType) */
432
+ readonly nodeType: string;
433
+ /**
434
+ * Generate TypeScript code for this node.
435
+ */
436
+ generate(node: FlowNode, writer: CodeBlockWriter, context: PluginContext): void;
437
+ /**
438
+ * Declare npm packages required by this node.
439
+ * @returns Array of package names, e.g. ["@aws-sdk/client-ses"]
440
+ */
441
+ getRequiredPackages?(node: FlowNode): string[];
442
+ /**
443
+ * Infer the TypeScript type of this node's output value.
444
+ * Used to generate a typed flowState interface.
445
+ * @returns TypeScript type string, e.g. "{ sent: boolean }"
446
+ */
447
+ getOutputType?(node: FlowNode): string;
448
+ }
449
+ /**
450
+ * Plugin Registry instance.
451
+ * Each `compile()` call can create an independent registry to avoid global state pollution.
452
+ */
453
+ interface PluginRegistry {
454
+ register(plugin: NodePlugin): void;
455
+ registerAll(plugins: NodePlugin[]): void;
456
+ get(nodeType: string): NodePlugin | undefined;
457
+ has(nodeType: string): boolean;
458
+ getAll(): Map<string, NodePlugin>;
459
+ clear(): void;
460
+ }
461
+ /**
462
+ * Create a new Plugin Registry instance.
463
+ */
464
+ declare function createPluginRegistry(): PluginRegistry;
465
+ /**
466
+ * Register a node plugin (global).
467
+ * If a plugin with the same name already exists, it will be overwritten (allows developers to replace built-in plugins).
468
+ */
469
+ declare function registerPlugin(plugin: NodePlugin): void;
470
+ /**
471
+ * Batch-register multiple plugins (global).
472
+ */
473
+ declare function registerPlugins(plugins: NodePlugin[]): void;
474
+ /**
475
+ * Get the plugin for a specific node type (global).
476
+ */
477
+ declare function getPlugin(nodeType: string): NodePlugin | undefined;
478
+ /**
479
+ * Get all registered plugins (global).
480
+ */
481
+ declare function getAllPlugins(): Map<string, NodePlugin>;
482
+ /**
483
+ * Clear all registered plugins (global, for testing).
484
+ */
485
+ declare function clearPlugins(): void;
486
+ /**
487
+ * Check if a specific node type is registered (global).
488
+ */
489
+ declare function hasPlugin(nodeType: string): boolean;
490
+
491
+ /**
492
+ * Built-in Node Plugins
493
+ *
494
+ * All built-in node code generators extracted from the compiler core.
495
+ * Each generator follows the NodePlugin interface and can be overridden by external plugins.
496
+ */
497
+
498
+ declare const builtinPlugins: NodePlugin[];
499
+
500
+ /**
501
+ * Flow2Code AST Compiler Core (v2)
502
+ *
503
+ * Architecture:
504
+ * 1. Platform Adapter — Decoupled from Next.js, supports Express / Cloudflare Workers
505
+ * 2. Plugin System — Node generation logic is externally registerable
506
+ * 3. Expression Parser — Recursive Descent Parser replaces regex
507
+ * 4. Type Inference — Generates typed FlowState interface instead of Record<string, any>
508
+ * 5. Scoped State — Loops/try-catch use local scope to prevent variable shadowing
509
+ *
510
+ * Public API (backward-compatible):
511
+ * - compile(ir) → CompileResult (default: nextjs platform)
512
+ * - compile(ir, { platform }) → CompileResult (specified platform)
513
+ * - traceLineToNode(sourceMap, line) → Reverse lookup node from line number
514
+ */
515
+
516
+ interface CompileResult {
517
+ success: boolean;
518
+ code?: string;
519
+ errors?: string[];
520
+ /** Generated file path (relative) */
521
+ filePath?: string;
522
+ /** Dependency report */
523
+ dependencies?: DependencyReport;
524
+ /** Source Map (nodeId ↔ line number mapping) */
525
+ sourceMap?: SourceMap;
526
+ }
527
+ /** Dependency report */
528
+ interface DependencyReport {
529
+ /** All required packages */
530
+ all: string[];
531
+ /** Missing packages (compared with package.json) */
532
+ missing: string[];
533
+ /** Suggested install command */
534
+ installCommand?: string;
535
+ }
536
+ /** Source Map: line number ↔ node ID mapping */
537
+ interface SourceMap {
538
+ version: 1;
539
+ generatedFile: string;
540
+ /** nodeId → { startLine, endLine } */
541
+ mappings: Record<string, {
542
+ startLine: number;
543
+ endLine: number;
544
+ }>;
545
+ }
546
+ interface CompileOptions {
547
+ /** Target platform (default: "nextjs") */
548
+ platform?: PlatformName;
549
+ /** Additional Node Plugins */
550
+ plugins?: NodePlugin[];
551
+ }
552
+ /**
553
+ * Compiles FlowIR into TypeScript source code.
554
+ *
555
+ * Pipeline stages:
556
+ * 1. Validate IR structure
557
+ * 2. Topological sort + concurrency detection
558
+ * 3. Platform adaptation (Next.js / Express / Cloudflare etc.)
559
+ * 4. Plugin-based node code generation
560
+ * 5. IDE-friendly Source Map output
561
+ *
562
+ * @param ir - FlowIR input document
563
+ * @param options - Compile options (platform, plugins, output path etc.)
564
+ * @returns Compile result containing code, filePath, sourceMap, dependencies
565
+ *
566
+ * @example
567
+ * ```ts
568
+ * import { compile } from "flow2code";
569
+ *
570
+ * const result = compile(ir, { platform: "hono" });
571
+ * if (result.success) {
572
+ * console.log(result.code);
573
+ * }
574
+ * ```
575
+ */
576
+ declare function compile(ir: FlowIR, options?: CompileOptions): CompileResult;
577
+ /**
578
+ * Given a line number, reverse-lookup the corresponding nodeId.
579
+ */
580
+ declare function traceLineToNode(sourceMap: SourceMap, line: number): {
581
+ nodeId: string;
582
+ startLine: number;
583
+ endLine: number;
584
+ } | null;
585
+
586
+ /**
587
+ * Flow IR Validator
588
+ *
589
+ * Validates structural correctness of an IR document before compilation:
590
+ * 1. Must have exactly one Trigger node
591
+ * 2. All Edge source/target must reference existing nodes
592
+ * 3. No orphan nodes (except Trigger)
593
+ * 4. Graph must be a DAG (no cycles)
594
+ */
595
+
596
+ interface ValidationError {
597
+ code: string;
598
+ message: string;
599
+ nodeId?: NodeId;
600
+ edgeId?: string;
601
+ }
602
+ interface ValidationResult {
603
+ valid: boolean;
604
+ errors: ValidationError[];
605
+ /** If the IR was auto-migrated, records the migration path */
606
+ migrated?: boolean;
607
+ migratedIR?: FlowIR;
608
+ migrationLog?: string[];
609
+ }
610
+ /**
611
+ * Validates the structural correctness of a FlowIR document.
612
+ * Auto-migrates older versions before validation if needed.
613
+ */
614
+ declare function validateFlowIR(ir: FlowIR): ValidationResult;
615
+
616
+ /**
617
+ * AI-Generated IR Security Check
618
+ *
619
+ * When IR is produced by AI/LLM, custom_code nodes may contain malicious code.
620
+ * This module intercepts at the "load/import" stage, earlier than the compile-time `DANGEROUS_CODE_PATTERNS`.
621
+ *
622
+ * @example
623
+ * ```ts
624
+ * import { validateIRSecurity } from "flow2code/compiler";
625
+ *
626
+ * const result = validateIRSecurity(aiGeneratedIR);
627
+ * if (!result.safe) {
628
+ * console.warn("⚠️ Dangerous patterns:", result.findings);
629
+ * }
630
+ * ```
631
+ */
632
+
633
+ interface SecurityFinding {
634
+ /** Severity level */
635
+ severity: "critical" | "warning" | "info";
636
+ /** Node ID */
637
+ nodeId: NodeId;
638
+ /** Node label */
639
+ nodeLabel: string;
640
+ /** Description of the detected pattern */
641
+ pattern: string;
642
+ /** Matched code snippet (truncated to 80 characters) */
643
+ match: string;
644
+ }
645
+ interface SecurityCheckResult {
646
+ /** Whether it is safe (no critical findings) */
647
+ safe: boolean;
648
+ /** All detection results */
649
+ findings: SecurityFinding[];
650
+ /** Number of nodes scanned */
651
+ nodesScanned: number;
652
+ }
653
+ /**
654
+ * Validate the security of all nodes in a FlowIR
655
+ *
656
+ * Specifically targets AI/LLM-generated IR, detecting malicious patterns at the load/import stage.
657
+ * Scan scope: custom_code, transform, if_else, return_response, fetch_api, and all other fields containing code/expressions.
658
+ *
659
+ * @param ir - The FlowIR to check
660
+ * @returns SecurityCheckResult - Contains safety verdict, all detection findings, and scan statistics
661
+ */
662
+ declare function validateIRSecurity(ir: FlowIR): SecurityCheckResult;
663
+ /**
664
+ * Format security check results into a human-readable string
665
+ */
666
+ declare function formatSecurityReport(result: SecurityCheckResult): string;
667
+
668
+ /**
669
+ * Topological Sort and Concurrency Detection Algorithm
670
+ *
671
+ * Features:
672
+ * 1. Compute node execution priority (topological sort)
673
+ * 2. Detect groups of concurrently executable nodes (for generating Promise.all)
674
+ * 3. Build Execution Plan
675
+ */
676
+
677
+ /** Single execution step */
678
+ interface ExecutionStep {
679
+ /** Step index */
680
+ index: number;
681
+ /**
682
+ * List of node IDs to execute in this step.
683
+ * If length > 1, they can be executed concurrently with Promise.all.
684
+ */
685
+ nodeIds: NodeId[];
686
+ /** Whether it can execute concurrently */
687
+ concurrent: boolean;
688
+ }
689
+ /** Complete execution plan */
690
+ interface ExecutionPlan {
691
+ /** All steps (topologically sorted) */
692
+ steps: ExecutionStep[];
693
+ /** Topological sort result (flat) */
694
+ sortedNodeIds: NodeId[];
695
+ /** Incoming nodes for each node (dependency sources) */
696
+ dependencies: Map<NodeId, Set<NodeId>>;
697
+ /** Outgoing nodes for each node (dependency targets) */
698
+ dependents: Map<NodeId, Set<NodeId>>;
699
+ }
700
+ /**
701
+ * Perform topological sort using Kahn's algorithm,
702
+ * grouping nodes at the same level (indegree reaches zero simultaneously)
703
+ * to detect concurrently executable nodes.
704
+ */
705
+ declare function topologicalSort(ir: FlowIR): ExecutionPlan;
706
+
707
+ /**
708
+ * Flow2Code Expression Parser
709
+ *
710
+ * Replaces fragile Regex parsing with a Recursive Descent Parser for template expressions.
711
+ *
712
+ * Supported syntax:
713
+ * {{nodeId}} → flowState['nodeId']
714
+ * {{nodeId.path.to.value}} → flowState['nodeId'].path.to.value
715
+ * {{nodeId.arr[0].name}} → flowState['nodeId'].arr[0].name
716
+ * {{$input}} → auto-resolves upstream non-trigger node
717
+ * {{$input.data.items}} → same, with sub-path
718
+ * {{$trigger}} → trigger node's flowState
719
+ * {{$trigger.body.userId}} → flowState['triggerId'].body.userId
720
+ *
721
+ * Differences from Regex version:
722
+ * - Correctly handles nested brackets e.g. {{node.arr[items[0]]}}
723
+ * - Correctly handles whitespace e.g. {{ $input.data }}
724
+ * - Clear error messages, no silent fallthrough
725
+ * - Supports escape sequences \\{{ to output literal {{
726
+ */
727
+
728
+ /**
729
+ * Scope Entry: describes the current scope during code generation.
730
+ * For example, inside a for-loop, the loop node's ID should resolve
731
+ * to _loopScope instead of flowState.
732
+ */
733
+ interface ScopeEntry {
734
+ /** Node ID mapped by this scope */
735
+ nodeId: NodeId;
736
+ /** Variable name in generated code (e.g. _loopScope) */
737
+ scopeVar: string;
738
+ }
739
+ interface ExpressionContext {
740
+ /** Current IR */
741
+ ir: FlowIR;
742
+ /** Node ID → FlowNode mapping */
743
+ nodeMap: Map<NodeId, FlowNode>;
744
+ /** Current node ID (used for resolving $input) */
745
+ currentNodeId?: NodeId;
746
+ /** Symbol Table: when enabled, expressions use named variables instead of flowState['nodeId'] */
747
+ symbolTable?: SymbolTable;
748
+ /**
749
+ * Scope Stack: outermost to innermost scope entries.
750
+ * When a reference's base matches a scope's nodeId,
751
+ * it resolves to scopeVar['nodeId'] instead of flowState['nodeId'].
752
+ */
753
+ scopeStack?: ScopeEntry[];
754
+ /**
755
+ * Block-scoped node IDs: these nodes are generated inside sub-blocks
756
+ * (if/else, try/catch, for-loop body), and their Symbol Table aliases
757
+ * are not visible in the outer scope.
758
+ * Expression parsing must fallback to flowState['nodeId'].
759
+ */
760
+ blockScopedNodeIds?: Set<NodeId>;
761
+ }
762
+ /**
763
+ * Parses all {{...}} references in an expression string and replaces them
764
+ * with the corresponding flowState access expressions.
765
+ *
766
+ * @param expr - Raw expression (may contain {{...}} template syntax)
767
+ * @param context - Compiler context
768
+ * @returns Parsed TypeScript expression
769
+ */
770
+ declare function parseExpression(expr: string, context: ExpressionContext): string;
771
+
772
+ /**
773
+ * Flow2Code Type Inference Engine
774
+ *
775
+ * Infers TypeScript types for flowState based on FlowIR node definitions and port types.
776
+ * Replaces the crude `Record<string, any>` declaration to provide real type safety in generated code.
777
+ *
778
+ * Inference strategies:
779
+ * 1. Trigger nodes: inferred from trigger type and parameters (e.g. HTTP body, query, etc.)
780
+ * 2. Action nodes: inferred from the plugin's getOutputType()
781
+ * 3. Manual specification: uses the port's dataType as a fallback
782
+ * 4. Auto-narrowing: $input references are narrowed based on upstream node types
783
+ */
784
+
785
+ interface FlowStateTypeInfo {
786
+ /** Complete TypeScript interface source code */
787
+ interfaceCode: string;
788
+ /** TypeScript type corresponding to each node ID */
789
+ nodeTypes: Map<string, string>;
790
+ }
791
+ /**
792
+ * Infer the output types of all nodes in a FlowIR and generate the corresponding TypeScript interface.
793
+ *
794
+ * @param ir - Flow IR
795
+ * @returns FlowStateTypeInfo containing the generated interface and per-node type mappings
796
+ */
797
+ declare function inferFlowStateTypes(ir: FlowIR, registry?: PluginRegistry): FlowStateTypeInfo;
798
+
799
+ /**
800
+ * Flow2Code Universal Decompiler — TypeScript → FlowIR
801
+ *
802
+ * Reverse-parses **any** TypeScript source code into FlowIR intermediate representation.
803
+ * Designed for AI Code Audit: accepts AI-generated TypeScript and visualizes its logic flow.
804
+ *
805
+ * Strategy:
806
+ * 1. AST Analysis (primary): ts-morph parses TS AST, pattern-matches known constructs
807
+ * 2. Data Flow Tracking: variable def-use chains build real edges (not linear chaining)
808
+ * 3. Source Map Hints (optional): if `// --- label [nodeType] [nodeId] ---` markers exist,
809
+ * they enhance node labeling but are NOT required
810
+ *
811
+ * Supported patterns:
812
+ * - Export functions / arrow functions / class methods → Triggers
813
+ * - `await fetch(...)` → Fetch API nodes
814
+ * - `if/else` → If/Else logic nodes
815
+ * - `for...of` / `for...in` / `for` → Loop nodes
816
+ * - `try/catch` → TryCatch nodes
817
+ * - Variable declarations → Variable nodes
818
+ * - Generic `await expr` → Custom code action nodes
819
+ * - Return / Response statements → Output nodes
820
+ *
821
+ * Edge building:
822
+ * - Data flow edges: tracks which variables are produced and consumed by each node
823
+ * - Control flow edges: if/else branches, loop bodies, try/catch blocks
824
+ * - Sequential edges: statements in the same scope with no explicit data dependency
825
+ */
826
+
827
+ interface DecompileResult {
828
+ success: boolean;
829
+ ir?: FlowIR;
830
+ errors?: string[];
831
+ /** Confidence score 0-1 indicating decompilation accuracy */
832
+ confidence: number;
833
+ /** Audit hints: detected issues or notable patterns */
834
+ audit?: AuditHint[];
835
+ }
836
+ /** Audit hint: a detected issue or notable pattern in the code */
837
+ interface AuditHint {
838
+ nodeId: string;
839
+ severity: "info" | "warning" | "error";
840
+ message: string;
841
+ /** Source line number (1-indexed) */
842
+ line?: number;
843
+ }
844
+ interface DecompileOptions {
845
+ /** File name or full file path (used for route inference) */
846
+ fileName?: string;
847
+ /** Target function name to decompile (omit to auto-detect) */
848
+ functionName?: string;
849
+ /** Enable audit hints (default: true) */
850
+ audit?: boolean;
851
+ }
852
+ /**
853
+ * Decompiles any TypeScript source code into FlowIR.
854
+ *
855
+ * @param code - TypeScript source code (arbitrary, not limited to flow2code output)
856
+ * @param options - Optional settings
857
+ * @returns DecompileResult with IR, confidence, and optional audit hints
858
+ */
859
+ declare function decompile(code: string, options?: DecompileOptions): DecompileResult;
860
+
861
+ /**
862
+ * Flow2Code Runtime Error Tracer
863
+ *
864
+ * Solves the "debugging experience gap" problem:
865
+ * When a compiled API Route throws a runtime error,
866
+ * it automatically intercepts the error stack, reverse-lookups the Source Map,
867
+ * and prints a clickable deep link to jump directly to the error node on the Flow2Code canvas.
868
+ *
869
+ * Usage:
870
+ * ```ts
871
+ * // 1. Next.js middleware / wrap handler
872
+ * import { withFlowTrace } from "flow2code/compiler";
873
+ *
874
+ * export const GET = withFlowTrace(handler, { flowFile: "my-flow.flow.json" });
875
+ *
876
+ * // 2. Global install (intercept uncaught errors)
877
+ * import { installFlowTracer } from "flow2code/compiler";
878
+ *
879
+ * installFlowTracer({ port: 3001 });
880
+ * ```
881
+ *
882
+ * Output example:
883
+ * ```
884
+ * ❌ Runtime Error in generated.ts:45
885
+ * → Flow2Code Node: [fetch_api_1] "Fetch User Data"
886
+ * → 🔗 http://localhost:3001?highlight=fetch_api_1
887
+ * ```
888
+ */
889
+
890
+ interface TraceResult {
891
+ /** Matched node ID */
892
+ nodeId: string;
893
+ /** Node label */
894
+ nodeLabel: string;
895
+ /** Node type */
896
+ nodeType: string;
897
+ /** Source code line number range */
898
+ startLine: number;
899
+ endLine: number;
900
+ /** Clickable deep link (opens canvas and highlights node) */
901
+ deepLink: string;
902
+ }
903
+ interface TracerOptions {
904
+ /** Flow2Code editor URL (default: http://localhost:3001) */
905
+ editorUrl?: string;
906
+ /** Source Map object (if already in memory) */
907
+ sourceMap?: SourceMap;
908
+ /** FlowIR object (used to retrieve node labels) */
909
+ ir?: FlowIR;
910
+ /** Whether to print a readable error message to console (default: true) */
911
+ log?: boolean;
912
+ }
913
+ /**
914
+ * Extract all matching line numbers from an Error object's stack trace
915
+ * and reverse-lookup the corresponding Flow nodes via Source Map.
916
+ *
917
+ * @param error - Original Error object
918
+ * @param sourceMap - Source Map generated at compile time
919
+ * @param ir - FlowIR (used to retrieve label / nodeType)
920
+ * @param editorUrl - Editor URL
921
+ * @returns All matching TraceResults (in stack order)
922
+ */
923
+ declare function traceError(error: Error, sourceMap: SourceMap, ir?: FlowIR, editorUrl?: string): TraceResult[];
924
+ /**
925
+ * Format TraceResults into a readable console message.
926
+ */
927
+ declare function formatTraceResults(error: Error, traces: TraceResult[], generatedFile: string): string;
928
+ type AsyncHandler = (...args: unknown[]) => Promise<unknown>;
929
+ /**
930
+ * Wrap an API handler to automatically intercept runtime errors and print Flow node traces.
931
+ *
932
+ * @example
933
+ * ```ts
934
+ * import { withFlowTrace } from "flow2code/compiler";
935
+ * import sourceMap from "./my-flow.flow.map.json";
936
+ *
937
+ * async function handler(req: Request) {
938
+ * // ... generated code
939
+ * }
940
+ *
941
+ * export const GET = withFlowTrace(handler, { sourceMap });
942
+ * ```
943
+ */
944
+ declare function withFlowTrace<T extends AsyncHandler>(handler: T, options: TracerOptions): T;
945
+ /**
946
+ * Install the Flow2Code error tracer at the process level.
947
+ * Listens for `uncaughtException` and `unhandledRejection`,
948
+ * automatically printing deep links for errors matching the Source Map.
949
+ *
950
+ * @param options - TracerOptions (must include sourceMap)
951
+ * @returns Cleanup function (uninstall tracer)
952
+ *
953
+ * @example
954
+ * ```ts
955
+ * import { installFlowTracer } from "flow2code/compiler";
956
+ * import sourceMap from "./my-flow.flow.map.json";
957
+ *
958
+ * const uninstall = installFlowTracer({ sourceMap, editorUrl: "http://localhost:3001" });
959
+ *
960
+ * // Later, stop tracing
961
+ * uninstall();
962
+ * ```
963
+ */
964
+ declare function installFlowTracer(options: TracerOptions & {
965
+ sourceMap: SourceMap;
966
+ }): () => void;
967
+
968
+ /**
969
+ * Flow2Code Dynamic Node Registry
970
+ *
971
+ * Solves the "core and nodes over-coupling" problem:
972
+ * Node definitions are no longer hardcoded in switch statements,
973
+ * but independently described via `NodeDefinition` and dynamically registered.
974
+ *
975
+ * Community extensions simply need to:
976
+ * ```ts
977
+ * import { nodeRegistry } from "flow2code/compiler";
978
+ *
979
+ * nodeRegistry.register({
980
+ * nodeType: "action:s3_upload",
981
+ * category: NodeCategory.ACTION,
982
+ * label: "AWS S3 Upload",
983
+ * icon: "☁️",
984
+ * description: "Upload file to AWS S3 Bucket",
985
+ * defaultPorts: {
986
+ * inputs: [{ id: "file", label: "File", dataType: "object", required: true }],
987
+ * outputs: [{ id: "url", label: "URL", dataType: "string" }],
988
+ * },
989
+ * defaultParams: { bucket: "", region: "us-east-1" },
990
+ * });
991
+ * ```
992
+ */
993
+
994
+ interface NodeDefinition {
995
+ /** Node type identifier (corresponds to IR's nodeType) */
996
+ nodeType: string;
997
+ /** Node category */
998
+ category: NodeCategory;
999
+ /** Display label */
1000
+ label: string;
1001
+ /** Emoji / icon (for NodeLibrary) */
1002
+ icon: string;
1003
+ /** Node description (tooltip) */
1004
+ description?: string;
1005
+ /** Default ports */
1006
+ defaultPorts: {
1007
+ inputs: InputPort[];
1008
+ outputs: OutputPort[];
1009
+ };
1010
+ /** Default parameters */
1011
+ defaultParams: Record<string, unknown>;
1012
+ /** Group name displayed in NodeLibrary; defaults to category group if omitted */
1013
+ group?: string;
1014
+ /** Sort weight (lower = higher priority), default 100 */
1015
+ order?: number;
1016
+ }
1017
+ declare class NodeRegistry {
1018
+ private definitions;
1019
+ /** Register a single node definition */
1020
+ register(def: NodeDefinition): void;
1021
+ /** Batch register */
1022
+ registerAll(defs: NodeDefinition[]): void;
1023
+ /** Get a single node definition */
1024
+ get(nodeType: string): NodeDefinition | undefined;
1025
+ /** Get all node definitions */
1026
+ getAll(): NodeDefinition[];
1027
+ /** Get node definitions by category */
1028
+ getByCategory(category: NodeCategory): NodeDefinition[];
1029
+ /** Get all registered nodeType strings */
1030
+ getRegisteredTypes(): string[];
1031
+ /** Check if registered */
1032
+ has(nodeType: string): boolean;
1033
+ /** Remove (for testing or hot reload) */
1034
+ unregister(nodeType: string): boolean;
1035
+ /** Clear all (for testing) */
1036
+ clear(): void;
1037
+ /** Get node default ports (backward-compatible with getDefaultPorts) */
1038
+ getDefaultPorts(nodeType: string): {
1039
+ inputs: InputPort[];
1040
+ outputs: OutputPort[];
1041
+ };
1042
+ /** Get node default parameters (backward-compatible with getDefaultParams) */
1043
+ getDefaultParams(nodeType: string): Record<string, unknown>;
1044
+ /** Get node default label (backward-compatible with getDefaultLabel) */
1045
+ getDefaultLabel(nodeType: string): string;
1046
+ /** Infer category from node type (backward-compatible with getCategoryForType) */
1047
+ getCategoryForType(nodeType: string): NodeCategory;
1048
+ /**
1049
+ * Group nodes by group (for NodeLibrary UI)
1050
+ * Return format is compatible with the original NodeLibrary.tsx nodeTemplates
1051
+ */
1052
+ getGroupedDefinitions(): Record<string, {
1053
+ icon: string;
1054
+ color: string;
1055
+ templates: Array<{
1056
+ nodeType: string;
1057
+ label: string;
1058
+ icon: string;
1059
+ category: NodeCategory;
1060
+ }>;
1061
+ }>;
1062
+ }
1063
+ declare const nodeRegistry: NodeRegistry;
1064
+
1065
+ /**
1066
+ * Flow2Code Split Storage
1067
+ *
1068
+ * Solves keypoint.md #2 "JSON Diff Hell" problem.
1069
+ *
1070
+ * Splits a single .flow.json into a directory structure:
1071
+ *
1072
+ * my-flow/
1073
+ * ├── meta.yaml ← Workflow metadata
1074
+ * ├── edges.yaml ← All edges
1075
+ * └── nodes/
1076
+ * ├── trigger_1.yaml
1077
+ * ├── fetch_1.yaml
1078
+ * └── response_1.yaml
1079
+ *
1080
+ * Each node is an independent file, greatly reducing Git merge conflict probability.
1081
+ */
1082
+
1083
+ /** Split file structure */
1084
+ interface SplitFiles {
1085
+ /** meta.yaml content */
1086
+ meta: string;
1087
+ /** edges.yaml content */
1088
+ edges: string;
1089
+ /** Node files: filename → yaml content */
1090
+ nodes: Map<string, string>;
1091
+ }
1092
+ /**
1093
+ * Split FlowIR into multiple YAML files
1094
+ */
1095
+ declare function splitIR(ir: FlowIR): SplitFiles;
1096
+ /**
1097
+ * Merge YAML files into FlowIR
1098
+ */
1099
+ declare function mergeIR(files: SplitFiles): FlowIR;
1100
+
1101
+ /**
1102
+ * Flow2Code FlowProject — Unified Flow load / save interface
1103
+ *
1104
+ * Solves the "Git version control workaround" problem:
1105
+ * Developers don't need to manually run split / merge;
1106
+ * all load / save operations auto-detect format and default to split YAML directory storage.
1107
+ *
1108
+ * Supports two formats:
1109
+ * 1. **Split YAML (default)** — `my-flow/` directory (`meta.yaml` + `edges.yaml` + `nodes/*.yaml`)
1110
+ * 2. **Single JSON (backward compatible)** — `my-flow.flow.json`
1111
+ *
1112
+ * @example
1113
+ * ```ts
1114
+ * import { loadFlowProject, saveFlowProject } from "flow2code/compiler";
1115
+ *
1116
+ * // Load (auto-detect format)
1117
+ * const ir = loadFlowProject("./flows/my-flow");
1118
+ *
1119
+ * // Save (defaults to split YAML directory)
1120
+ * saveFlowProject(ir, "./flows/my-flow");
1121
+ *
1122
+ * // Force save as .flow.json
1123
+ * saveFlowProject(ir, "./flows/my-flow.flow.json", { format: "json" });
1124
+ * ```
1125
+ */
1126
+
1127
+ type FlowProjectFormat = "split" | "json";
1128
+ interface SaveOptions {
1129
+ /** Storage format, default "split" (YAML directory) */
1130
+ format?: FlowProjectFormat;
1131
+ /** Whether to clean orphaned node files in directory (default true) */
1132
+ cleanOrphanNodes?: boolean;
1133
+ }
1134
+ interface FlowProjectInfo {
1135
+ /** Actual loaded path */
1136
+ path: string;
1137
+ /** Detected format */
1138
+ format: FlowProjectFormat;
1139
+ /** Loaded IR */
1140
+ ir: FlowIR;
1141
+ }
1142
+ /**
1143
+ * Detect flow project format from path
1144
+ *
1145
+ * Detection order:
1146
+ * 1. Path ends with `.flow.json` → json
1147
+ * 2. Path is a directory containing `meta.yaml` → split
1148
+ * 3. Path + `.flow.json` file exists → json
1149
+ * 4. Path is a directory (assumed split) → split
1150
+ */
1151
+ declare function detectFormat(inputPath: string): {
1152
+ resolvedPath: string;
1153
+ format: FlowProjectFormat;
1154
+ };
1155
+ /**
1156
+ * Load Flow project (auto-detect format)
1157
+ */
1158
+ declare function loadFlowProject(inputPath: string): FlowProjectInfo;
1159
+ /**
1160
+ * Save Flow project
1161
+ *
1162
+ * @param format - Default "split". Pass "json" to save as single .flow.json.
1163
+ */
1164
+ declare function saveFlowProject(ir: FlowIR, outputPath: string, options?: SaveOptions): string[];
1165
+ /**
1166
+ * Migrate .flow.json to split YAML directory
1167
+ * Returns list of written files. The original .flow.json is not deleted.
1168
+ */
1169
+ declare function migrateToSplit(jsonPath: string): string[];
1170
+
1171
+ /**
1172
+ * Flow2Code Semantic Diff Engine
1173
+ *
1174
+ * Compares two FlowIR versions, producing human-readable semantic diff descriptions.
1175
+ * Replaces the noise of raw JSON diffs, letting PR reviews see "what logic was added" instead of "which bytes changed".
1176
+ *
1177
+ * Comparison levels:
1178
+ * 1. Meta: name, description changes
1179
+ * 2. Nodes: added / removed / modified nodes
1180
+ * 3. Edges: added / removed edges
1181
+ * 4. Params: node parameter detail changes
1182
+ */
1183
+
1184
+ type ChangeType = "added" | "removed" | "modified";
1185
+ type ChangeCategory = "meta" | "node" | "edge";
1186
+ interface SemanticChange {
1187
+ /** Change type */
1188
+ type: ChangeType;
1189
+ /** Change category */
1190
+ category: ChangeCategory;
1191
+ /** Affected ID (nodeId or edgeId) */
1192
+ id: string;
1193
+ /** Human-readable description */
1194
+ description: string;
1195
+ /** Detailed field differences for modifications */
1196
+ details?: FieldDiff[];
1197
+ }
1198
+ interface FieldDiff {
1199
+ /** Field path, e.g. "params.url" or "label" */
1200
+ field: string;
1201
+ /** Old value */
1202
+ before: unknown;
1203
+ /** New value */
1204
+ after: unknown;
1205
+ }
1206
+ interface DiffSummary {
1207
+ /** All changes */
1208
+ changes: SemanticChange[];
1209
+ /** Quick statistics */
1210
+ stats: {
1211
+ added: number;
1212
+ removed: number;
1213
+ modified: number;
1214
+ total: number;
1215
+ };
1216
+ }
1217
+ /**
1218
+ * Compute semantic differences between two FlowIRs
1219
+ *
1220
+ * @param before - IR before changes
1221
+ * @param after - IR after changes
1222
+ * @returns Semantic diff summary
1223
+ */
1224
+ declare function semanticDiff(before: FlowIR, after: FlowIR): DiffSummary;
1225
+ /**
1226
+ * Format diff summary into a human-readable text report
1227
+ */
1228
+ declare function formatDiff(summary: DiffSummary): string;
1229
+
1230
+ export { ActionType, type AuditHint, type BuiltinPlatformName, CURRENT_IR_VERSION, type CallSubflowParams, type CompileOptions, type CompileResult, type CronJobParams, type CustomCodeParams, type DeclareVariableParams, type DecompileOptions, type DecompileResult, type DependencyReport, type ExecutionPlan, type ExecutionStep, type FetchApiParams, type FlowDataType, type FlowEdge, type FlowIR, type FlowNode, type FlowProjectFormat, type FlowProjectInfo, type ForLoopParams, type HttpWebhookParams, type IfElseParams, type InputPort, LogicType, type ManualTriggerParams, NodeCategory, type NodeDefinition, type NodeId, type NodePlugin, NodeRegistry, type NodeType, type OutputPort, OutputType, type PlatformAdapter, type PlatformContext, type PlatformName, type PluginContext, type PluginRegistry, type PortId, type RedisCacheParams, type ReturnResponseParams, type SaveOptions, type SecurityCheckResult, type SecurityFinding, type SourceMap, type SplitFiles, type SqlQueryParams, type TraceResult, type TracerOptions, type TransformParams, type TriggerInitContext, TriggerType, type TryCatchParams, VariableType, builtinPlugins, clearPlugins, compile, createPluginRegistry, decompile, detectFormat, formatDiff, formatSecurityReport, formatTraceResults, getAllPlugins, getAvailablePlatforms, getPlatform, getPlugin, hasPlugin, inferFlowStateTypes, installFlowTracer, loadFlowProject, mergeIR, migrateToSplit, nodeRegistry, parseExpression, registerPlatform, registerPlugin, registerPlugins, saveFlowProject, semanticDiff, splitIR, topologicalSort, traceError, traceLineToNode, validateFlowIR as validate, validateFlowIR, validateIRSecurity, withFlowTrace };