Repository: pgflow-dev/pgflow
Pull request #60: Add compileFlow method to DSL package
Author: login: jumski
Created: 2025-04-19T11:44:38Z | Merged: 2025-04-19T11:46:59Z



<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **New Features**
  - Introduced the ability to compile flow definitions into SQL statements for PostgreSQL using a new function.
- **Tests**
  - Added comprehensive tests to ensure accurate SQL generation from flow definitions.
- **Refactor**
  - Made the step order of flows publicly accessible while keeping it read-only for improved transparency.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/.changeset/soft-peaches-dig.md b/.changeset/soft-peaches-dig.md
new file mode 100644
index 000000000..449f54583
--- /dev/null
+++ b/.changeset/soft-peaches-dig.md
@@ -0,0 +1,5 @@
+---
+'@pgflow/dsl': patch
+---
+
+Add compileFlow function
diff --git a/pkgs/dsl/__tests__/runtime/compile-flow.test.ts b/pkgs/dsl/__tests__/runtime/compile-flow.test.ts
new file mode 100644
index 000000000..6d395eaf5
--- /dev/null
+++ b/pkgs/dsl/__tests__/runtime/compile-flow.test.ts
@@ -0,0 +1,113 @@
+import { describe, it, expect } from 'vitest';
+import { Flow } from '../../src/dsl.js';
+import { compileFlow } from '../../src/compile-flow.js';
+import { AnalyzeWebsite } from '../../src/example-flow.js';
+
+describe('compileFlow', () => {
+  it('should compile a simple flow with no steps', () => {
+    const flow = new Flow({ slug: 'test_flow' });
+    const statements = compileFlow(flow);
+
+    expect(statements).toHaveLength(1);
+    expect(statements[0]).toBe("SELECT pgflow.create_flow('test_flow');");
+  });
+
+  it('should compile a flow with runtime options', () => {
+    const flow = new Flow({
+      slug: 'test_flow',
+      maxAttempts: 5,
+      baseDelay: 10,
+      timeout: 120,
+    });
+
+    const statements = compileFlow(flow);
+
+    expect(statements).toHaveLength(1);
+    expect(statements[0]).toBe(
+      "SELECT pgflow.create_flow('test_flow', max_attempts => 5, base_delay => 10, timeout => 120);"
+    );
+  });
+
+  it('should compile a flow with steps', () => {
+    const flow = new Flow({ slug: 'test_flow' })
+      .step({ slug: 'step1' }, () => 'result1')
+      .step({ slug: 'step2', dependsOn: ['step1'] }, () => 'result2');
+
+    const statements = compileFlow(flow);
+
+    expect(statements).toHaveLength(3);
+    expect(statements[0]).toBe("SELECT pgflow.create_flow('test_flow');");
+    expect(statements[1]).toBe("SELECT pgflow.add_step('test_flow', 'step1');");
+    expect(statements[2]).toBe(
+      "SELECT pgflow.add_step('test_flow', 'step2', ARRAY['step1']);"
+    );
+  });
+
+  it('should compile a flow with steps that have runtime options', () => {
+    const flow = new Flow({ slug: 'test_flow' })
+      .step({ slug: 'step1', maxAttempts: 3, baseDelay: 5 }, () => 'result1')
+      .step(
+        { slug: 'step2', dependsOn: ['step1'], timeout: 30 },
+        () => 'result2'
+      );
+
+    const statements = compileFlow(flow);
+
+    expect(statements).toHaveLength(3);
+    expect(statements[0]).toBe("SELECT pgflow.create_flow('test_flow');");
+    expect(statements[1]).toBe(
+      "SELECT pgflow.add_step('test_flow', 'step1', max_attempts => 3, base_delay => 5);"
+    );
+    expect(statements[2]).toBe(
+      "SELECT pgflow.add_step('test_flow', 'step2', ARRAY['step1'], timeout => 30);"
+    );
+  });
+
+  it('should compile a complex flow with multiple steps and dependencies', () => {
+    const flow = new Flow({
+      slug: 'complex_flow',
+      maxAttempts: 5,
+    })
+      .step({ slug: 'step1' }, () => 'result1')
+      .step({ slug: 'step2' }, () => 'result2')
+      .step({ slug: 'step3', dependsOn: ['step1', 'step2'] }, () => 'result3')
+      .step(
+        { slug: 'step4', dependsOn: ['step3'], timeout: 60 },
+        () => 'result4'
+      );
+
+    const statements = compileFlow(flow);
+
+    expect(statements).toHaveLength(5);
+    expect(statements[0]).toBe(
+      "SELECT pgflow.create_flow('complex_flow', max_attempts => 5);"
+    );
+    expect(statements[1]).toBe(
+      "SELECT pgflow.add_step('complex_flow', 'step1');"
+    );
+    expect(statements[2]).toBe(
+      "SELECT pgflow.add_step('complex_flow', 'step2');"
+    );
+    expect(statements[3]).toBe(
+      "SELECT pgflow.add_step('complex_flow', 'step3', ARRAY['step1', 'step2']);"
+    );
+    expect(statements[4]).toBe(
+      "SELECT pgflow.add_step('complex_flow', 'step4', ARRAY['step3'], timeout => 60);"
+    );
+  });
+
+  it('should compile the example AnalyzeWebsite flow with snapshot testing', () => {
+    const statements = compileFlow(AnalyzeWebsite);
+
+    // Use snapshot testing to verify the entire array of SQL statements
+    expect(statements).toMatchInlineSnapshot(`
+      [
+        "SELECT pgflow.create_flow('analyze_website', max_attempts => 3, base_delay => 5, timeout => 10);",
+        "SELECT pgflow.add_step('analyze_website', 'website');",
+        "SELECT pgflow.add_step('analyze_website', 'sentiment', ARRAY['website'], max_attempts => 5, timeout => 30);",
+        "SELECT pgflow.add_step('analyze_website', 'summary', ARRAY['website']);",
+        "SELECT pgflow.add_step('analyze_website', 'saveToDb', ARRAY['sentiment', 'summary']);",
+      ]
+    `);
+  });
+});
diff --git a/pkgs/dsl/src/compile-flow.ts b/pkgs/dsl/src/compile-flow.ts
new file mode 100644
index 000000000..42c3daeb9
--- /dev/null
+++ b/pkgs/dsl/src/compile-flow.ts
@@ -0,0 +1,56 @@
+import { AnyFlow, RuntimeOptions } from './dsl.js';
+
+/**
+ * Compiles a Flow object into an array of SQL statements
+ * that can be executed to create the flow and its steps in PostgreSQL
+ *
+ * @param flow The Flow object to compile
+ * @returns Array of SQL statements
+ */
+export function compileFlow(flow: AnyFlow): string[] {
+  const statements: string[] = [];
+
+  // Create the flow
+  const flowOptions = formatRuntimeOptions(flow.options);
+  statements.push(`SELECT pgflow.create_flow('${flow.slug}'${flowOptions});`);
+
+  // Add steps in the order they were defined
+  for (const stepSlug of flow.stepOrder) {
+    const step = flow.getStepDefinition(stepSlug);
+    const stepOptions = formatRuntimeOptions(step.options);
+
+    // Format dependencies array if it exists
+    let depsClause = '';
+    if (step.dependencies.length > 0) {
+      const depsArray = step.dependencies.map((dep) => `'${dep}'`).join(', ');
+      depsClause = `, ARRAY[${depsArray}]`;
+    }
+
+    statements.push(
+      `SELECT pgflow.add_step('${flow.slug}', '${step.slug}'${depsClause}${stepOptions});`
+    );
+  }
+
+  return statements;
+}
+
+/**
+ * Formats runtime options into SQL parameter string
+ */
+function formatRuntimeOptions(options: RuntimeOptions): string {
+  const parts: string[] = [];
+
+  if (options.maxAttempts !== undefined) {
+    parts.push(`max_attempts => ${options.maxAttempts}`);
+  }
+
+  if (options.baseDelay !== undefined) {
+    parts.push(`base_delay => ${options.baseDelay}`);
+  }
+
+  if (options.timeout !== undefined) {
+    parts.push(`timeout => ${options.timeout}`);
+  }
+
+  return parts.length > 0 ? `, ${parts.join(', ')}` : '';
+}
diff --git a/pkgs/dsl/src/dsl.ts b/pkgs/dsl/src/dsl.ts
index b3cd1ee7a..74e3c85fb 100644
--- a/pkgs/dsl/src/dsl.ts
+++ b/pkgs/dsl/src/dsl.ts
@@ -161,7 +161,7 @@ export class Flow<
    * Type safety is enforced at the method level when adding or retrieving steps.
    */
   private stepDefinitions: Record<string, StepDefinition<AnyInput, AnyOutput>>;
-  private stepOrder: string[];
+  public readonly stepOrder: string[];
   public readonly slug: string;
   public readonly options: RuntimeOptions;
 
diff --git a/pkgs/dsl/src/index.ts b/pkgs/dsl/src/index.ts
index f06b72274..05fd04c3b 100644
--- a/pkgs/dsl/src/index.ts
+++ b/pkgs/dsl/src/index.ts
@@ -1 +1,2 @@
 export * from './dsl.js';
+export * from './compile-flow.js';


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>pkgs/dsl/src/compile-flow.ts (1)</summary><blockquote>

`37-56`: **Consider validating runtime option values.**

While the function correctly formats runtime options, it doesn't validate that the values are reasonable or valid. For example, `maxAttempts` should typically be a positive integer.


Consider adding validation:

```diff
function formatRuntimeOptions(options: RuntimeOptions): string {
  const parts: string[] = [];

  if (options.maxAttempts !== undefined) {
+   if (options.maxAttempts <= 0 || !Number.isInteger(options.maxAttempts)) {
+     throw new Error('maxAttempts must be a positive integer');
+   }
    parts.push(`max_attempts => ${options.maxAttempts}`);
  }

  if (options.baseDelay !== undefined) {
+   if (options.baseDelay < 0) {
+     throw new Error('baseDelay must be non-negative');
+   }
    parts.push(`base_delay => ${options.baseDelay}`);
  }

  if (options.timeout !== undefined) {
+   if (options.timeout <= 0) {
+     throw new Error('timeout must be positive');
+   }
    parts.push(`timeout => ${options.timeout}`);
  }

  return parts.length > 0 ? `, ${parts.join(', ')}` : '';
}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 42bc71c80f2a7ebb3d3156fdedf52d0d43b9d039 and 2c556c841f804d98a0375c5a3f11eace5d2684b6.

</details>

<details>
<summary>📒 Files selected for processing (5)</summary>

* `.changeset/soft-peaches-dig.md` (1 hunks)
* `pkgs/dsl/__tests__/runtime/compile-flow.test.ts` (1 hunks)
* `pkgs/dsl/src/compile-flow.ts` (1 hunks)
* `pkgs/dsl/src/dsl.ts` (1 hunks)
* `pkgs/dsl/src/index.ts` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (2)</summary>

<details>
<summary>pkgs/dsl/__tests__/runtime/compile-flow.test.ts (3)</summary><blockquote>

<details>
<summary>pkgs/dsl/src/dsl.ts (1)</summary>

* `Flow` (151-318)

</details>
<details>
<summary>pkgs/dsl/src/compile-flow.ts (1)</summary>

* `compileFlow` (10-35)

</details>
<details>
<summary>pkgs/dsl/src/example-flow.ts (1)</summary>

* `AnalyzeWebsite` (8-36)

</details>

</blockquote></details>
<details>
<summary>pkgs/dsl/src/compile-flow.ts (1)</summary><blockquote>

<details>
<summary>pkgs/dsl/src/dsl.ts (3)</summary>

* `AnyFlow` (50-50)
* `step` (221-317)
* `RuntimeOptions` (130-134)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (2)</summary>

* GitHub Check: main
* GitHub Check: Cloudflare Pages

</details>

<details>
<summary>🔇 Additional comments (13)</summary><blockquote>

<details>
<summary>pkgs/dsl/src/dsl.ts (1)</summary>

`164-164`: **Good change: exposing stepOrder as read-only**

Making the `stepOrder` property `public readonly` is the right approach. It gives `compileFlow` access to the step sequence while preventing modification, maintaining the integrity of step ordering.

</details>
<details>
<summary>.changeset/soft-peaches-dig.md (1)</summary>

`1-6`: **LGTM: Proper changeset for the new functionality**

The changeset correctly indicates a patch release for the new `compileFlow` function, which is appropriate since this is an additive, non-breaking change.

</details>
<details>
<summary>pkgs/dsl/src/index.ts (1)</summary>

`2-2`: **LGTM: Clean export of the new functionality**

Properly re-exporting the new `compileFlow` function makes it accessible as part of the public API.

</details>
<details>
<summary>pkgs/dsl/__tests__/runtime/compile-flow.test.ts (7)</summary>

`1-5`: **Properly structured imports**

The imports are well-organized, correctly importing test utilities from vitest and the necessary components from the dsl package.

---

`6-14`: **LGTM: Basic flow test**

Good test for the simplest case - a flow with no steps. This establishes the baseline functionality.

---

`15-29`: **LGTM: Flow with runtime options test**

The test properly verifies that runtime options are correctly included in the SQL statement.

---

`31-44`: **LGTM: Flow with steps and dependencies test**

This test correctly verifies that steps and their dependencies are properly represented in the SQL.

---

`46-64`: **LGTM: Steps with runtime options test**

The test correctly verifies that step-level runtime options are properly included in the SQL.

---

`66-97`: **LGTM: Complex flow test**

Good test coverage for a more complex flow with multiple steps and different dependency relationships.

---

`99-112`: **Good use of snapshot testing**

Using snapshot testing for the example flow is an efficient way to verify the entire output at once, especially for complex flows.


The snapshot test provides complete validation of a real-world example, ensuring all aspects of the SQL generation work together correctly.

</details>
<details>
<summary>pkgs/dsl/src/compile-flow.ts (3)</summary>

`1-9`: **Well-structured module with clear documentation.**

The import statements and JSDoc comments clearly explain the purpose and interface of the `compileFlow` function. The documentation effectively communicates that this function converts a Flow object into executable SQL statements for PostgreSQL.

---

`18-20`: **Good use of flow's step ordering.**

The code correctly uses the `stepOrder` property to iterate through steps in the exact order they were defined in the flow, ensuring that the SQL statements maintain the same logical sequence as the original flow definition.

---

`23-27`: **Well-handled optional dependencies.**

The code correctly formats the dependencies array only when dependencies exist, preventing empty arrays from being unnecessarily included in the SQL.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-19T11:47:24Z

Review comments:
diff_hunk: @@ -0,0 +1,56 @@
+import { AnyFlow, RuntimeOptions } from './dsl.js';
+
+/**
+ * Compiles a Flow object into an array of SQL statements
+ * that can be executed to create the flow and its steps in PostgreSQL
+ *
+ * @param flow The Flow object to compile
+ * @returns Array of SQL statements
+ */
+export function compileFlow(flow: AnyFlow): string[] {
+  const statements: string[] = [];
+
+  // Create the flow
+  const flowOptions = formatRuntimeOptions(flow.options);
+  statements.push(`SELECT pgflow.create_flow('${flow.slug}'${flowOptions});`);
+
+  // Add steps in the order they were defined
+  for (const stepSlug of flow.stepOrder) {
+    const step = flow.getStepDefinition(stepSlug);
+    const stepOptions = formatRuntimeOptions(step.options);
+
+    // Format dependencies array if it exists
+    let depsClause = '';
+    if (step.dependencies.length > 0) {
+      const depsArray = step.dependencies.map((dep) => `'${dep}'`).join(', ');
+      depsClause = `, ARRAY[${depsArray}]`;
+    }
+
+    statements.push(
+      `SELECT pgflow.add_step('${flow.slug}', '${step.slug}'${depsClause}${stepOptions});`
+    );
+  }
+
+  return statements;
+}
+
+/**
+ * Formats runtime options into SQL parameter string
+ */
+function formatRuntimeOptions(options: RuntimeOptions): string {
+  const parts: string[] = [];
+
+  if (options.maxAttempts !== undefined) {
+    parts.push(`max_attempts => ${options.maxAttempts}`);
+  }
+
+  if (options.baseDelay !== undefined) {
+    parts.push(`base_delay => ${options.baseDelay}`);
+  }
+
+  if (options.timeout !== undefined) {
+    parts.push(`timeout => ${options.timeout}`);
+  }
+
+  return parts.length > 0 ? `, ${parts.join(', ')}` : '';
+}
path: pkgs/dsl/src/compile-flow.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Consider adding SQL value escaping for numeric inputs.**

Similar to the string values, numeric values in runtime options should be properly validated to prevent SQL injection through manipulated objects.


While numbers are generally safer than strings for SQL injection, it's still good practice to validate that they are actually numbers:

```diff
if (options.maxAttempts !== undefined) {
+  if (typeof options.maxAttempts !== 'number' || isNaN(options.maxAttempts)) {
+    throw new Error('maxAttempts must be a valid number');
+  }
  parts.push(`max_attempts => ${options.maxAttempts}`);
}
```

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T11:47:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,56 @@
+import { AnyFlow, RuntimeOptions } from './dsl.js';
+
+/**
+ * Compiles a Flow object into an array of SQL statements
+ * that can be executed to create the flow and its steps in PostgreSQL
+ *
+ * @param flow The Flow object to compile
+ * @returns Array of SQL statements
+ */
+export function compileFlow(flow: AnyFlow): string[] {
+  const statements: string[] = [];
+
+  // Create the flow
+  const flowOptions = formatRuntimeOptions(flow.options);
+  statements.push(`SELECT pgflow.create_flow('${flow.slug}'${flowOptions});`);
+
+  // Add steps in the order they were defined
+  for (const stepSlug of flow.stepOrder) {
+    const step = flow.getStepDefinition(stepSlug);
+    const stepOptions = formatRuntimeOptions(step.options);
+
+    // Format dependencies array if it exists
+    let depsClause = '';
+    if (step.dependencies.length > 0) {
+      const depsArray = step.dependencies.map((dep) => `'${dep}'`).join(', ');
+      depsClause = `, ARRAY[${depsArray}]`;
+    }
+
+    statements.push(
+      `SELECT pgflow.add_step('${flow.slug}', '${step.slug}'${depsClause}${stepOptions});`
+    );
+  }
+
+  return statements;
+}
path: pkgs/dsl/src/compile-flow.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Address potential SQL injection vulnerability.**

The function builds SQL statements using direct string interpolation with values from the Flow object. While this may be safe if Flow objects are trusted and internally generated, it could pose a security risk if any part of these objects could contain user input.


Consider implementing proper SQL escaping or using parameterized queries to prevent SQL injection:

```diff
- statements.push(`SELECT pgflow.create_flow('${flow.slug}'${flowOptions});`);
+ statements.push(`SELECT pgflow.create_flow(${escapeSqlString(flow.slug)}${flowOptions});`);

- `SELECT pgflow.add_step('${flow.slug}', '${step.slug}'${depsClause}${stepOptions});`
+ `SELECT pgflow.add_step(${escapeSqlString(flow.slug)}, ${escapeSqlString(step.slug)}${depsClause}${stepOptions});`
```

You'd need to implement the `escapeSqlString` function to properly escape SQL string literals.


> Committable suggestion skipped: line range outside the PR's diff.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T11:47:23Z
author_association: CONTRIBUTOR

PR comments:
user: login: changeset-bot[bot]
created_at: 2025-04-19T11:44:42Z
body: ###  🦋  Changeset detected

Latest commit: 2c556c841f804d98a0375c5a3f11eace5d2684b6

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 6 packages</summary>

  | Name                  | Type  |
| --------------------- | ----- |
| @pgflow/dsl           | Patch |
| @pgflow/core          | Patch |
| @pgflow/edge-worker   | Patch |
| @pgflow/example-flows | Patch |
| pgflow                | Patch |
| @pgflow/website       | Patch |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/pgflow-dev/pgflow/new/dsl-to-sql?filename=.changeset/slimy-cars-eat.md&value=---%0A%22%40pgflow%2Fdsl%22%3A%20patch%0A---%0A%0AAdd%20compileFlow%20method%20to%20DSL%20package%0A)


author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-04-19T11:44:44Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

A new function, `compileFlow`, has been introduced to the `@pgflow/dsl` package, enabling the compilation of flow definitions into SQL statements for PostgreSQL. The implementation includes a new module, corresponding tests, and updates to the package exports. Additionally, the `stepOrder` property in the `Flow` class has been made publicly readable. The public API of the package is extended to include the new functionality, and comprehensive tests validate the correctness of the SQL generation.

## Changes

| File(s)                                                      | Change Summary |
|--------------------------------------------------------------|---------------|
| .changeset/soft-peaches-dig.md                               | Added a changeset describing the patch update for the new `compileFlow` function in `@pgflow/dsl`. |
| pkgs/dsl/src/compile-flow.ts                                 | New module implementing the `compileFlow` function, which converts a Flow object into SQL statements for PostgreSQL. Includes helper for formatting runtime options. |
| pkgs/dsl/src/index.ts                                        | Re-exported all exports from `./compile-flow.js`, making `compileFlow` available in the package's public API. |
| pkgs/dsl/src/dsl.ts                                          | Changed the `stepOrder` property in the `Flow` class from `private` to `public readonly`, allowing external read access while keeping it immutable. |
| pkgs/dsl/__tests__/runtime/compile-flow.test.ts              | Added a new test suite for `compileFlow`, covering various scenarios and validating the generated SQL statements using the `vitest` framework. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant compileFlow
    participant FlowObject
    participant SQLArray

    User->>compileFlow: Call with FlowObject
    compileFlow->>FlowObject: Read flow properties and steps
    compileFlow->>SQLArray: Generate SQL for flow creation
    loop For each step in FlowObject.stepOrder
        compileFlow->>SQLArray: Generate SQL for step (with dependencies and options)
    end
    compileFlow-->>User: Return array of SQL statements
```

## Poem

> In the warren, code flows anew,  
> With SQL strings and logic true.  
> Steps in order, dependencies tight,  
> Flows compiled from day to night.  
> Now public steps, and tests that gleam—  
> A rabbit’s hop toward a well-built dream!  
> 🐇✨

<!-- walkthrough_end -->
<!-- This is an auto-generated comment: all tool run failures by coderabbit.ai -->

> [!WARNING]
> There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.
> 
> <details>
> <summary>🔧 ESLint</summary>
> 
> > If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.
> 
> <details>
> <summary>pkgs/dsl/src/compile-flow.ts</summary>
> 
> 
> Oops! Something went wrong! :(
> 
> ESLint: 9.25.0
> 
> Error: Cannot find module 'jsonc-eslint-parser'
> Require stack:
> - /eslint.config.cjs
>     at Module._resolveFilename (node:internal/modules/cjs/loader:1248:15)
>     at Module._load (node:internal/modules/cjs/loader:1074:27)
>     at TracingChannel.traceSync (node:diagnostics_channel:315:14)
>     at wrapModuleLoad (node:internal/modules/cjs/loader:217:24)
>     at Module.require (node:internal/modules/cjs/loader:1339:12)
>     at require (node:internal/modules/helpers:135:16)
>     at Object.<anonymous> (/eslint.config.cjs:19:15)
>     at Module._compile (node:internal/modules/cjs/loader:1546:14)
>     at Module._extensions..js (node:internal/modules/cjs/loader:1691:10)
>     at Module.load (node:internal/modules/cjs/loader:1317:32)
> 
> </details>
> 
> <details>
> <summary>pkgs/dsl/src/dsl.ts</summary>
> 
> 
> Oops! Something went wrong! :(
> 
> ESLint: 9.25.0
> 
> Error: Cannot find module 'jsonc-eslint-parser'
> Require stack:
> - /eslint.config.cjs
>     at Module._resolveFilename (node:internal/modules/cjs/loader:1248:15)
>     at Module._load (node:internal/modules/cjs/loader:1074:27)
>     at TracingChannel.traceSync (node:diagnostics_channel:315:14)
>     at wrapModuleLoad (node:internal/modules/cjs/loader:217:24)
>     at Module.require (node:internal/modules/cjs/loader:1339:12)
>     at require (node:internal/modules/helpers:135:16)
>     at Object.<anonymous> (/eslint.config.cjs:19:15)
>     at Module._compile (node:internal/modules/cjs/loader:1546:14)
>     at Module._extensions..js (node:internal/modules/cjs/loader:1691:10)
>     at Module.load (node:internal/modules/cjs/loader:1317:32)
> 
> </details>
> 
> <details>
> <summary>pkgs/dsl/src/index.ts</summary>
> 
> 
> Oops! Something went wrong! :(
> 
> ESLint: 9.25.0
> 
> Error: Cannot find module 'jsonc-eslint-parser'
> Require stack:
> - /eslint.config.cjs
>     at Module._resolveFilename (node:internal/modules/cjs/loader:1248:15)
>     at Module._load (node:internal/modules/cjs/loader:1074:27)
>     at TracingChannel.traceSync (node:diagnostics_channel:315:14)
>     at wrapModuleLoad (node:internal/modules/cjs/loader:217:24)
>     at Module.require (node:internal/modules/cjs/loader:1339:12)
>     at require (node:internal/modules/helpers:135:16)
>     at Object.<anonymous> (/eslint.config.cjs:19:15)
>     at Module._compile (node:internal/modules/cjs/loader:1546:14)
>     at Module._extensions..js (node:internal/modules/cjs/loader:1691:10)
>     at Module.load (node:internal/modules/cjs/loader:1317:32)
> 
> </details>
> 
> </details>

<!-- end of auto-generated comment: all tool run failures by coderabbit.ai -->
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=pgflow-dev/pgflow&utm_content=60):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: cloudflare-workers-and-pages[bot]
created_at: 2025-04-19T11:45:25Z
body: ## Deploying pgflow with &nbsp;<a href="https://pages.dev"><img alt="Cloudflare Pages" src="https://user-images.githubusercontent.com/23264/106598434-9e719e00-654f-11eb-9e59-6167043cfa01.png" width="16"></a> &nbsp;Cloudflare Pages

<table><tr><td><strong>Latest commit:</strong> </td><td>
<code>2c556c8</code>
</td></tr>
<tr><td><strong>Status:</strong></td><td>&nbsp;✅&nbsp; Deploy successful!</td></tr>
<tr><td><strong>Preview URL:</strong></td><td>
<a href='https://37826acf.pgflow.pages.dev'>https://37826acf.pgflow.pages.dev</a>
</td></tr>
<tr><td><strong>Branch Preview URL:</strong></td><td>
<a href='https://dsl-to-sql.pgflow.pages.dev'>https://dsl-to-sql.pgflow.pages.dev</a>
</td></tr>
</table>

[View logs](https://dash.cloudflare.com/4b952df942e3d2f7591fe6bd09182151/pages/view/pgflow/37826acf-3c3e-4b02-9673-613f1d62fd45)

author_association: NONE
user: login: nx-cloud[bot]
created_at: 2025-04-19T11:46:42Z
body: 
View your [CI Pipeline Execution ↗](https://cloud.nx.app/cipes/68038cc67d6f71212d800497?utm_source=pull-request&utm_medium=comment) for commit 2c556c841f804d98a0375c5a3f11eace5d2684b6.

| Command | Status | Duration | Result |
|---------|--------|----------:|--------|
| `nx affected -t lint typecheck test build` | ✅ Succeeded | 1m 30s | [View ↗](https://cloud.nx.app/runs/j1NImEKZUP?utm_source=pull-request&utm_medium=comment) |

---

☁️ [Nx Cloud](https://cloud.nx.app?utm_source=pull-request&utm_medium=comment) last updated this comment at `2025-04-19 11:46:41` UTC

<!-- NX_CLOUD_APP_COMMENT_END -->
author_association: NONE