Skip to content

Fix: Promise.all visualization for dynamic maps & Pearl JSON response parsing#252

Open
Devil1716 wants to merge 7 commits intobubblelabai:mainfrom
Devil1716:main
Open

Fix: Promise.all visualization for dynamic maps & Pearl JSON response parsing#252
Devil1716 wants to merge 7 commits intobubblelabai:mainfrom
Devil1716:main

Conversation

@Devil1716
Copy link

@Devil1716 Devil1716 commented Jan 1, 2026

Summary

This PR implements two critical fixes for the BubbleLab workflow visualization and Pearl AI agent integration:

1. Promise.all Visualization for Dynamic Maps (Issue #241)

  • Enhanced workflowToSteps.ts to properly handle and visualize dynamic parallel execution from .map() operations
  • Implemented detection of isDynamic flag for parallel nodes created from mapped arrays
  • Added automatic labeling of parallel steps:
    • Single mapped element: "(Mapped)" suffix
    • Multiple elements: "Parallel execution over [sourceArray]" or "Parallel item from [sourceArray]"
  • Converts for-of loops to index-based iteration to support dynamic array tracking

2. Pearl JSON Response Parsing (Issue #238)

  • Fixed Legitimate Pearl response formatting in JSON blocks
  • Implemented parseJsonWithFallbacks() to handle:
    • Markdown code block wrapped JSON responses
    • Partial JSON failures during agent execution
    • Graceful fallback to answer-type responses when JSON parsing fails
  • Enhanced error recovery logic in pearl.ts to extract valid JSON even from failed agent executions

Implementation Details

Files Modified:

  • apps/bubble-studio/src/utils/workflowToSteps.ts - Dynamic parallel step visualization
  • apps/bubblelab-api/src/services/ai/pearl.ts - JSON parsing with fallbacks
  • packages/bubble-runtime/src/extraction/BubbleParser.ts - Dynamic array detection in Promise.all
  • packages/bubble-shared-schemas/src/bubble-definition-schema.ts - Schema updates for isDynamic and sourceArray
  • packages/bubble-runtime/src/extraction/BubbleParser.test.ts - Test cases for dynamic map detection

Related Issues

Fixes #238, Fixes #241

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Refactor
  • Other (please describe):

Checklist

  • My code follows the code style of this project
  • I have added appropriate tests for my changes
  • I have run pnpm check and all tests pass
  • I have tested my changes locally
  • I have linked relevant issues
  • I have added screenshots for UI changes (if applicable)

Summary by CodeRabbit

  • New Features

    • Parallel execution steps now include dynamic metadata and clearer labels for mapped/parallel items (e.g., "(Mapped)", "(Parallel item from …)").
  • Improvements

    • AI response parsing is more robust with automatic fallback and better handling of malformed agent outputs to reduce failed responses.
  • Tests

    • Added tests validating detection of dynamic parallel execution from mapped arrays.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 1, 2026

📝 Walkthrough

Walkthrough

Detects dynamic parallelism from map-based Promise.all, surfaces isDynamic/sourceArray through parser and schema, updates studio step labeling for mapped parallel children, adds a runtime test, and makes Pearl agent response parsing more robust to JSON-in-markdown formats.

Changes

Cohort / File(s) Summary
Parser: dynamic array detection
packages/bubble-runtime/src/extraction/BubbleParser.ts
findArrayElements now returns { elements, isDynamic, sourceArray? }; callers updated to use arrayInfo.elements; buildParallelExecutionNode and related logic propagate isDynamic/sourceArray into produced parallel_execution nodes and handle early returns/variableDeclaration metadata.
Runtime test
packages/bubble-runtime/src/extraction/BubbleParser.test.ts
New test asserting detection of dynamic Promise.all over .map(...): parallel_execution.isDynamic === true and sourceArray === "items".
Schema: parallel execution metadata
packages/bubble-shared-schemas/src/bubble-definition-schema.ts
Added optional isDynamic?: boolean and sourceArray?: string to ParallelExecutionWorkflowNode in TypeScript types and Zod schema.
Studio: step labeling for dynamic parallelism
apps/bubble-studio/src/utils/workflowToSteps.ts
Rewrote parallel_execution handling to use index-based iteration; detect dynamic mapped sources and modify functionName/description per child (e.g., append " (Mapped)" or "(Parallel execution over )" / "(Parallel item from )"); propagate updated values into created StepData.
API: resilient Pearl parsing
apps/bubblelab-api/src/services/ai/pearl.ts
Replace direct JSON.parse with parseJsonWithFallbacks and PearlAgentOutputSchema parsing; add recovery/fallback path that returns an answer-type response when structured parse fails, reducing unnecessary retries.

Sequence Diagram(s)

sequenceDiagram
    participant DevCode as Dev source (AST)
    participant Parser as BubbleParser
    participant Schema as Shared Schema
    participant Studio as Studio transformer
    participant UI as Visualization (StepData)

    DevCode->>Parser: parse code with Promise.all over .map(...)
    Parser->>Parser: findArrayElements -> { elements, isDynamic, sourceArray }
    Parser-->>Schema: emit `parallel_execution` node with isDynamic & sourceArray
    Schema-->>Studio: workflow JSON includes parallel_execution metadata
    Studio->>Studio: iterate children (index-based), adjust functionName/description per isDynamic/sourceArray
    Studio-->>UI: create StepData with updated labels/descriptions
    note right of UI: Dynamic items labelled "Mapped" or "Parallel item from <sourceArray>"
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped through maps and Promise.all,

Found arrays that grow and call,
I whisper names with mapped delight,
Parsers clean JSON out of sight,
Hooray—parallel steps now sparkle bright!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes both main changes: Promise.all visualization for dynamic maps and Pearl JSON response parsing, matching the PR objectives.
Linked Issues check ✅ Passed The PR implements all required objectives from both linked issues: dynamic parallel execution detection with isDynamic/sourceArray metadata [#241], automatic labeling for mapped arrays, and Pearl JSON response recovery with markdown parsing [#238].
Out of Scope Changes check ✅ Passed All code changes are directly scoped to the two linked issues: BubbleParser dynamic detection, schema updates, visualization labeling, and Pearl JSON parsing enhancements with no extraneous modifications.
✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

@bubblelab-pearl
Copy link
Contributor

Suggested PR title from Pearl

Title: feat: detect and label dynamic parallel execution from array maps

Body:

Changes

Dynamic Parallel Execution Detection

Adds capability to detect and properly label parallel execution patterns that originate from array map operations (e.g., items.map(item => bubble.action(item))).

Key improvements:

  • Parser now identifies when Promise.all arrays come from .map() calls
  • Tracks source array name and marks execution as dynamic
  • Enhances step names and descriptions with context:
    • Single mapped functions labeled as "(Mapped)"
    • Descriptions include source array reference (e.g., "Parallel execution over items")
  • Updates schema to include isDynamic and sourceArray metadata on parallel nodes

Pearl AI Error Recovery

Improves JSON parsing resilience in the Pearl AI service:

  • Attempts to recover valid JSON from failed agent executions using parseJsonWithFallbacks
  • Handles markdown code blocks and other common formatting issues
  • Falls back to answer type only when recovery is not possible

Code Quality

  • Consistent formatting and indentation across BubbleParser
  • Added test coverage for dynamic parallel execution detection

Modified Files

  • packages/bubble-runtime/src/extraction/BubbleParser.ts - Core detection logic
  • packages/bubble-shared-schemas/src/bubble-definition-schema.ts - Schema updates
  • apps/bubble-studio/src/utils/workflowToSteps.ts - Step labeling logic
  • apps/bubblelab-api/src/services/ai/pearl.ts - JSON parsing error recovery
  • packages/bubble-runtime/src/extraction/BubbleParser.test.ts - Test coverage

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/bubble-runtime/src/extraction/BubbleParser.ts (1)

4111-4215: Fix critical temporal dead zone error: move variableDeclaration before branches in buildParallelExecutionNode

At line 4151, the dynamic branch early-returns using variableDeclaration before it is declared at line 4189. Since variableDeclaration is a let binding, accessing it before initialization throws a ReferenceError at runtime: "Cannot access 'variableDeclaration' before initialization."

Move the variableDeclaration declaration and initialization block (currently lines 4188–4215) to the start of the function—immediately after const children: WorkflowNode[] = []; (line 4107)—so both the dynamic and non-dynamic returns reference the same initialized variable.

Proposed fix
  const code = this.bubbleScript.substring(stmt.range![0], stmt.range![1]);
  const children: WorkflowNode[] = [];

+  // Extract variable declaration info if this is part of a variable declaration
+  let variableDeclaration:
+    | {
+        variableNames: string[];
+        variableType: 'const' | 'let' | 'var';
+      }
+    | undefined;
+
+  if (
+    stmt.type === 'VariableDeclaration' &&
+    stmt.declarations.length > 0 &&
+    stmt.declarations[0].id.type === 'ArrayPattern'
+  ) {
+    const arrayPattern = stmt.declarations[0].id;
+    const variableNames: string[] = [];
+    for (const element of arrayPattern.elements) {
+      if (element && element.type === 'Identifier') {
+        variableNames.push(element.name);
+      }
+    }
+    variableDeclaration = {
+      variableNames,
+      variableType: stmt.kind as 'const' | 'let' | 'var',
+    };
+  }
+
   // Handle variable reference (e.g., Promise.all(exampleScrapers))
   if (promiseAllInfo.arrayExpr.type === 'Identifier' && this.cachedAST) {
     // ...existing code using variableDeclaration in early return...
   } else if (promiseAllInfo.arrayExpr.type === 'ArrayExpression') {
     // ...existing code...
   }
-
-  // Extract variable declaration info if this is part of a variable declaration
-  let variableDeclaration:
-    | {
-      variableNames: string[];
-      variableType: 'const' | 'let' | 'var';
-    }
-    | undefined;
-  // ...rest of block removed...
🧹 Nitpick comments (3)
apps/bubblelab-api/src/services/ai/pearl.ts (1)

572-605: Fallback on failed agent runs looks correct, with minor simplification possible

The recovery path for !result.success that:

  • first tries parseJsonWithFallbacks,
  • then validates via PearlAgentOutputSchema,
  • and finally falls back to an answer-type on schema failure or parsing failure

matches the issue goal of treating legitimate JSON blocks (and non‑Pearl JSON) as normal answers rather than hard errors.

You might simplify slightly by assigning result.data.response = recoveryParse.response (or caching recoveryParse.parsed) and reusing it in the main parse block to avoid re-running parseJsonWithFallbacks and PearlAgentOutputSchema.parse twice on the same string, but functionally this is already sound.

apps/bubble-studio/src/utils/workflowToSteps.ts (1)

338-385: Dynamic parallel labeling logic is consistent with the new metadata

The new handling for parallel_execution:

  • Reads isDynamic / sourceArray from the node.
  • For a single dynamic child, annotates the function name with " (Mapped)" and description with "Parallel execution over <sourceArray>".
  • For multiple dynamic children, appends "Parallel item from <sourceArray>" to each description.

This matches the intended UX difference between “one step representing a map over an array” and “individual parallel items” and doesn’t affect non‑dynamic flows.

packages/bubble-runtime/src/extraction/BubbleParser.ts (1)

3884-3897: New findArrayElements return shape and dynamic flags are coherent

Returning { elements, isDynamic, sourceArray? } from findArrayElements:

  • Keeps .push() patterns compatible by filling elements and leaving isDynamic false.
  • Detects .map()-based array construction by setting isDynamic = true and sourceArrayName when the source is an identifier.
  • Uses getSourceArrayElements to approximate multiplicity (replicating the callback expression per static element and using a single element when the length is unknown).

This gives buildParallelExecutionNode enough information to distinguish dynamic Promise.all flows from static ones without regressing existing patterns.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9391a3d and 8477957.

📒 Files selected for processing (5)
  • apps/bubble-studio/src/utils/workflowToSteps.ts
  • apps/bubblelab-api/src/services/ai/pearl.ts
  • packages/bubble-runtime/src/extraction/BubbleParser.test.ts
  • packages/bubble-runtime/src/extraction/BubbleParser.ts
  • packages/bubble-shared-schemas/src/bubble-definition-schema.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/packages/bubble-shared-schemas/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/api.mdc)

**/packages/bubble-shared-schemas/**/*.{ts,tsx}: Write shared schemas between frontend and backend in /packages/bubble-shared-schemas directory
Run pnpm build:core after modifying shared schemas since it is a separate package and types need to be regenerated

Files:

  • packages/bubble-shared-schemas/src/bubble-definition-schema.ts
🧠 Learnings (8)
📚 Learning: 2025-12-22T09:55:47.873Z
Learnt from: CR
Repo: bubblelabai/BubbleLab PR: 0
File: .cursor/rules/bubblelab.mdc:0-0
Timestamp: 2025-12-22T09:55:47.873Z
Learning: Applies to packages/bubble-shared-schemas/src/bubbleflow-generation-prompts.ts : Refer to packages/bubble-shared-schemas/src/bubbleflow-generation-prompts.ts for documentation on how bubble flow is supposed to be generated

Applied to files:

  • apps/bubblelab-api/src/services/ai/pearl.ts
  • packages/bubble-runtime/src/extraction/BubbleParser.test.ts
  • packages/bubble-shared-schemas/src/bubble-definition-schema.ts
📚 Learning: 2025-12-22T09:55:47.873Z
Learnt from: CR
Repo: bubblelabai/BubbleLab PR: 0
File: .cursor/rules/bubblelab.mdc:0-0
Timestamp: 2025-12-22T09:55:47.873Z
Learning: Applies to packages/bubble-runtime/src/extraction/README.md : Refer to packages/bubble-runtime/src/extraction/README.md for information about bubble parsing, dependency graphs, and per-invocation cloning

Applied to files:

  • packages/bubble-runtime/src/extraction/BubbleParser.test.ts
  • apps/bubble-studio/src/utils/workflowToSteps.ts
  • packages/bubble-runtime/src/extraction/BubbleParser.ts
📚 Learning: 2025-12-19T03:17:06.825Z
Learnt from: CR
Repo: bubblelabai/BubbleLab PR: 0
File: .cursor/rules/bundling.mdc:0-0
Timestamp: 2025-12-19T03:17:06.825Z
Learning: Applies to packages/bubble-core/dist/**/*.d.ts : The type bundling system for Monaco Editor must process TypeScript declaration files (.d.ts) and inline all dependencies into a single self-contained bundle in `packages/bubble-core/dist/bubble-bundle.d.ts`

Applied to files:

  • packages/bubble-runtime/src/extraction/BubbleParser.test.ts
  • packages/bubble-runtime/src/extraction/BubbleParser.ts
📚 Learning: 2025-12-19T03:17:06.825Z
Learnt from: CR
Repo: bubblelabai/BubbleLab PR: 0
File: .cursor/rules/bundling.mdc:0-0
Timestamp: 2025-12-19T03:17:06.825Z
Learning: Applies to packages/bubble-core/src/index.ts : All new types and classes added to bubble-core must be exported from `packages/bubble-core/src/index.ts` to ensure they are included in the generated bundle

Applied to files:

  • packages/bubble-runtime/src/extraction/BubbleParser.test.ts
  • packages/bubble-runtime/src/extraction/BubbleParser.ts
📚 Learning: 2025-12-19T03:16:48.801Z
Learnt from: CR
Repo: bubblelabai/BubbleLab PR: 0
File: .cursor/rules/api.mdc:0-0
Timestamp: 2025-12-19T03:16:48.801Z
Learning: Applies to **/bubblelab-api/**/*.test.{js,ts} : Use `pnpm bun test` command to run backend tests to ensure setup files are properly loaded

Applied to files:

  • packages/bubble-runtime/src/extraction/BubbleParser.test.ts
📚 Learning: 2025-12-19T03:17:06.825Z
Learnt from: CR
Repo: bubblelabai/BubbleLab PR: 0
File: .cursor/rules/bundling.mdc:0-0
Timestamp: 2025-12-19T03:17:06.825Z
Learning: Applies to packages/bubble-core/dist/**/*.d.ts : All external imports (except relative imports) must be removed from the bundled output to prevent circular processing and external dependency issues

Applied to files:

  • packages/bubble-runtime/src/extraction/BubbleParser.test.ts
  • packages/bubble-runtime/src/extraction/BubbleParser.ts
📚 Learning: 2025-12-19T03:16:48.801Z
Learnt from: CR
Repo: bubblelabai/BubbleLab PR: 0
File: .cursor/rules/api.mdc:0-0
Timestamp: 2025-12-19T03:16:48.801Z
Learning: Applies to **/bubblelab-api/**/*.test.{js,ts} : Reference webhook.test as the example for how backend tests should be written

Applied to files:

  • packages/bubble-runtime/src/extraction/BubbleParser.test.ts
📚 Learning: 2025-12-19T03:16:48.801Z
Learnt from: CR
Repo: bubblelabai/BubbleLab PR: 0
File: .cursor/rules/api.mdc:0-0
Timestamp: 2025-12-19T03:16:48.801Z
Learning: Applies to **/packages/bubble-shared-schemas/**/*.{ts,tsx} : Write shared schemas between frontend and backend in `/packages/bubble-shared-schemas` directory

Applied to files:

  • packages/bubble-shared-schemas/src/bubble-definition-schema.ts
🧬 Code graph analysis (1)
apps/bubblelab-api/src/services/ai/pearl.ts (3)
packages/bubble-core/src/index.ts (1)
  • parseJsonWithFallbacks (139-139)
packages/bubble-core/src/utils/json-parsing.ts (1)
  • parseJsonWithFallbacks (653-778)
packages/bubble-shared-schemas/src/pearl.ts (1)
  • PearlAgentOutputSchema (123-127)
🪛 Biome (2.1.2)
packages/bubble-runtime/src/extraction/BubbleParser.ts

[error] 4151-4152: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)

🔇 Additional comments (15)
apps/bubblelab-api/src/services/ai/pearl.ts (1)

614-621: Switch to parseJsonWithFallbacks in main path is a solid robustness win

Replacing direct JSON.parse with parseJsonWithFallbacks for responseText parsing correctly handles markdown code fences, embedded JSON, and minor formatting issues before validating with PearlAgentOutputSchema. No behavioural regressions are apparent in the surrounding control flow.

packages/bubble-shared-schemas/src/bubble-definition-schema.ts (2)

361-378: Dynamic metadata on ParallelExecutionWorkflowNode is wired correctly

Adding optional isDynamic and sourceArray to both the TypeScript interface and ParallelExecutionWorkflowNodeSchema keeps the public shape consistent and is backwards compatible for existing clients.

Per the guidelines for shared schemas, remember to run pnpm build:core so the regenerated types are up to date across packages.


547-562: Zod schema matches new interface fields

The additions of isDynamic: z.boolean().optional() and sourceArray: z.string().optional() correctly mirror the interface and will allow BubbleParser / studio to consume the new metadata without extra casting.

packages/bubble-runtime/src/extraction/BubbleParser.test.ts (1)

668-707: Dynamic parallel execution test covers the key behaviour

The new test cleanly exercises the .map(...) + Promise.all(tasks) pattern and validates:

  • presence of a parallel_execution node,
  • isDynamic === true,
  • sourceArray === 'items',
  • and non‑empty children.

This is exactly the regression surface described in the issue and should protect the new parser logic well.

apps/bubble-studio/src/utils/workflowToSteps.ts (4)

341-359: Index-based loop over parallel children looks safe

Switching to an index-based for over parallelNode.children (instead of for...of) is appropriate here, since you need i for dynamic labeling. Bubble id extraction and control‑flow collection remain identical in behaviour.


168-171: Formatting-only change around extractBubbleIdsByLineRange

The minor formatting tweak around the extractBubbleIdsByLineRange call doesn’t alter behaviour; usage is still correct for deriving bubbleIds from method definition locations.


485-487: Legacy extractStepsFromWorkflow remains consistent with new helper

The reflowed extractBubbleIdsByLineRange call here still mirrors the logic used in extractStepGraph, so legacy consumers of extractStepsFromWorkflow won’t see behavioural changes.


527-529: Parallel steps in legacy extractor unchanged semantically

In the legacy extractStepsFromWorkflow path, parallel children still derive their bubbleIds by either child bubbles or method ranges, with no logic changes beyond formatting. This keeps old views aligned with the new graph-based extractor.

packages/bubble-runtime/src/extraction/BubbleParser.ts (7)

320-348: Extended dependency metadata handling for bubbleDependenciesDetailed looks correct

The widened metadata type and subsequent handling of bubbleDependenciesDetailed (including nested tools for AI agents) in findDependenciesForBubble correctly prefers detailed specs when available and falls back to bubbleDependencies otherwise. This keeps the dependency graph richer without breaking existing metadata shapes.


1924-1988: Parameter location enrichment in extractFromNewExpression is consistent

Adding location with {startLine,startCol,endLine,endCol} for:

  • object-literal properties,
  • spread arguments,
  • and first-argument variables

aligns BubbleParameter with BubbleParameterSchema and will help downstream tooling (e.g., editors, hover, diagnostics) localize parameter usage precisely. The guards on loc being present before constructing the object are safe.


2199-2224: Stricter string checks for custom tool name/description are safe

The added typeof ... === 'string' checks when extracting toolName and description from custom tool definitions avoid accidental non‑string values while preserving existing behaviour for well-formed tool configs.


3022-3052: Refactored else-branch construction in buildIfNode preserves semantics

The small IIFE used to build elseBranch now:

  • handles else { ... }, else if (...), and single-statement else cases explicitly,
  • always returns an array (or []) when an alternate exists.

This keeps the shape of elseBranch consistent and doesn’t change how branchTerminates operates on the resulting tree.


3094-3099: buildForNode condition string construction remains correct

The expanded multi-line substring call for the update part of a ForStatement is equivalent to the previous implementation and still yields a human-readable init; test; update string for the loop condition.


4369-4373: Invocation-specific dependency graph cloning continues to look sound

The adjusted multi-line call to cloneDependencyGraphNodeForInvocation is purely formatting; the logic still clones dependency graph nodes with invocation-scoped uniqueId / variableId, consistent with the per-invocation bubbling behaviour described in the comments.


543-571: getPayloadJsonSchema now handles all supported handle entrypoint shapes

Updating the param extraction to treat FunctionExpression and ArrowFunctionExpression identically to FunctionDeclaration when building the payload schema ensures:

  • top-level handle functions,
  • exported handle variables with arrow/func expressions,
  • and class handle methods

all feed their first parameter's type into the JSON schema. This matches the shapes returned by findHandleFunctionNode and closes a previous gap for non-declaration forms.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8477957 and 5efa8e8.

📒 Files selected for processing (1)
  • packages/bubble-runtime/src/extraction/BubbleParser.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-22T09:55:47.873Z
Learnt from: CR
Repo: bubblelabai/BubbleLab PR: 0
File: .cursor/rules/bubblelab.mdc:0-0
Timestamp: 2025-12-22T09:55:47.873Z
Learning: Applies to packages/bubble-runtime/src/extraction/README.md : Refer to packages/bubble-runtime/src/extraction/README.md for information about bubble parsing, dependency graphs, and per-invocation cloning

Applied to files:

  • packages/bubble-runtime/src/extraction/BubbleParser.ts
📚 Learning: 2025-12-19T03:17:06.825Z
Learnt from: CR
Repo: bubblelabai/BubbleLab PR: 0
File: .cursor/rules/bundling.mdc:0-0
Timestamp: 2025-12-19T03:17:06.825Z
Learning: Applies to packages/bubble-core/dist/**/*.d.ts : All external imports (except relative imports) must be removed from the bundled output to prevent circular processing and external dependency issues

Applied to files:

  • packages/bubble-runtime/src/extraction/BubbleParser.ts
🔇 Additional comments (9)
packages/bubble-runtime/src/extraction/BubbleParser.ts (9)

322-326: LGTM: Type definition formatting improved.

The explicit type definition for bubbleDependenciesDetailed improves code readability and makes the expected structure clearer.


3946-3982: LGTM: Dynamic array detection is well-structured with intentional scope limitation.

The logic correctly identifies dynamic parallel execution from .map() operations and propagates the source array information. The conservative approach of only detecting Identifier sources (line 3952) is appropriate, as complex expressions like items.filter(...).map(...) would be harder to track reliably.

The loop at lines 3965-3967 creates multiple references to the same callbackExpr for visualization purposes, which correctly represents that each parallel execution runs the same code over different elements.


3022-3052: LGTM: Cleaner control flow structure.

The refactored else branch handling improves readability with explicit variable declarations and clearer if-else-if chain, while maintaining identical behavior.


3223-3238: LGTM: Consistent refactoring pattern applied.

The catch block handling follows the same improved structure as the else branch refactoring, providing consistency across the codebase.


4139-4181: LGTM: Dynamic parallel detection correctly integrated.

The logic properly distinguishes between dynamic (variable-sourced) and static (literal) parallel execution:

  • Variable references can be marked isDynamic when sourced from .map() operations
  • Literal array expressions remain static (as expected)
  • The variableDeclaration for array destructuring is correctly threaded through both paths

3889-3893: LGTM: Signature change properly implemented and all callers updated.

The structured return type makes the method's contract explicit and enables clean propagation of dynamic array metadata. All callers (line 4139 onwards) correctly consume the new structure.


928-932: LGTM: Consistent formatting improvements.

Multiple location extraction and formatting improvements throughout the file enhance readability and maintain consistent code style without any functional changes.

Also applies to: 949-953, 974-978, 3095-3097, 3394-3396, 3615-3618, 4369-4371


2207-2209: LGTM: Defensive type checking.

The literal value extraction properly validates both the AST node type and the runtime type of the value, ensuring type safety when extracting custom tool metadata.

Also applies to: 2222-2224


3884-3983: Overall assessment: Well-structured implementation of dynamic parallel detection.

The changes to findArrayElements and related methods successfully implement detection of dynamic parallel execution from .map() operations. The code is type-safe, handles the main use cases correctly, and includes appropriate defensive checks.

Key strengths:

  • Clear separation of dynamic (variable-sourced) vs static (literal) parallel execution
  • Conservative approach for detectability (Identifier sources only)
  • Proper propagation of metadata through structured return types
  • Type-safe implementation that will catch schema mismatches at compile time

The only minor limitation is the incomplete handling of rest elements in array destructuring (already noted in separate comment).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/bubble-runtime/src/extraction/BubbleParser.ts (1)

3889-3982: Schema is correct, but test coverage for non-dynamic cases should explicitly verify isDynamic = false, and RestElement handling needs explicit tests.

The ParallelExecutionWorkflowNode type in packages/bubble-shared-schemas/src/bubble-definition-schema.ts correctly declares both isDynamic?: boolean and sourceArray?: string as optional properties.

The test "should identify dynamic parallel execution from mapped array" properly verifies dynamic detection with expect(parallelNode.isDynamic).toBe(true) and expect(parallelNode.sourceArray).toBe('items'). However, the existing non-dynamic test cases (direct array literal, .push() pattern, .map() with literal array, etc.) do not explicitly assert that isDynamic remains false or undefined for non-dynamic scenarios. Additionally, RestElement handling in array destructuring is not explicitly tested to ensure the new dynamic properties behave correctly in this context.

Recommend adding assertions to non-dynamic test cases to verify isDynamic is false/undefined, and a test case for RestElement patterns in the context of dynamic parallel execution detection.

♻️ Duplicate comments (1)
packages/bubble-runtime/src/extraction/BubbleParser.ts (1)

4109-4138: Excellent! RestElement handling implemented as suggested.

The code now properly captures both regular identifiers and rest elements from array destructuring patterns in Promise.all declarations:

  • const [a, b] = await Promise.all([...]) → captures ['a', 'b']
  • const [a, ...rest] = await Promise.all([...]) → captures ['a', 'rest']

This directly addresses the previous review comment and implements the suggested enhancement. The implementation correctly checks for RestElement type and extracts the argument.name when the argument is an Identifier.

Based on learnings, this resolves the concern raised in the previous review about missing rest element handling.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5efa8e8 and dae2431.

📒 Files selected for processing (1)
  • packages/bubble-runtime/src/extraction/BubbleParser.ts
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-12-22T09:55:47.873Z
Learnt from: CR
Repo: bubblelabai/BubbleLab PR: 0
File: .cursor/rules/bubblelab.mdc:0-0
Timestamp: 2025-12-22T09:55:47.873Z
Learning: Applies to packages/bubble-runtime/src/extraction/README.md : Refer to packages/bubble-runtime/src/extraction/README.md for information about bubble parsing, dependency graphs, and per-invocation cloning

Applied to files:

  • packages/bubble-runtime/src/extraction/BubbleParser.ts
📚 Learning: 2025-12-19T03:17:06.825Z
Learnt from: CR
Repo: bubblelabai/BubbleLab PR: 0
File: .cursor/rules/bundling.mdc:0-0
Timestamp: 2025-12-19T03:17:06.825Z
Learning: Applies to packages/bubble-core/dist/**/*.d.ts : All external imports (except relative imports) must be removed from the bundled output to prevent circular processing and external dependency issues

Applied to files:

  • packages/bubble-runtime/src/extraction/BubbleParser.ts
📚 Learning: 2025-12-19T03:17:06.825Z
Learnt from: CR
Repo: bubblelabai/BubbleLab PR: 0
File: .cursor/rules/bundling.mdc:0-0
Timestamp: 2025-12-19T03:17:06.825Z
Learning: Applies to packages/bubble-core/dist/**/*.d.ts : The type bundling system for Monaco Editor must process TypeScript declaration files (.d.ts) and inline all dependencies into a single self-contained bundle in `packages/bubble-core/dist/bubble-bundle.d.ts`

Applied to files:

  • packages/bubble-runtime/src/extraction/BubbleParser.ts
🧬 Code graph analysis (1)
packages/bubble-runtime/src/extraction/BubbleParser.ts (2)
packages/bubble-shared-schemas/src/types.ts (1)
  • BubbleName (51-100)
packages/bubble-shared-schemas/src/bubble-definition-schema.ts (1)
  • WorkflowNode (410-419)
🔇 Additional comments (6)
packages/bubble-runtime/src/extraction/BubbleParser.ts (6)

322-327: LGTM! Type extension for detailed bubble dependencies.

The addition of the optional bubbleDependenciesDetailed property appropriately extends the metadata structure to support hierarchical dependency information with tools.


544-545: Also applies to: 1928-1932, 1949-1953, 1974-1978, 3095-3097, 3394-3396, 3615-3618, 4375-4377


2207-2207: LGTM! Proper type guards for Literal node values.

The type checking ensures nameProp.value.value and descProp.value.value are strings before assignment, preventing runtime type errors.

Also applies to: 2222-2222


3022-3052: LGTM! IIFE pattern improves branch handling readability.

The use of immediately-invoked function expressions for elseBranch and catchBlock construction maintains clean conditional logic while properly handling BlockStatement, nested IfStatement, and single-statement cases.

Also applies to: 3223-3238


4145-4150: LGTM! Dynamic metadata properly integrated into parallel execution nodes.

The implementation correctly:

  1. Captures the structured result from findArrayElements (line 4145)
  2. Iterates over arrayInfo.elements (line 4152)
  3. Returns early with isDynamic: true and sourceArray metadata when detected (lines 4177-4187)
  4. Falls through to standard parallel_execution for non-dynamic cases

The early return pattern ensures dynamic arrays get the additional metadata while static arrays remain unchanged. This aligns with the schema updates mentioned in the PR objectives.

Also applies to: 4152-4152, 4177-4187


3889-3893: Dynamic array detection correctly implements Identifier-based .map() pattern detection.

The implementation successfully detects Promise.all arrays sourced from simple .map() operations on identifiers (e.g., items.map(...)) and captures source array metadata. The return type structure { elements, isDynamic, sourceArray } properly propagates dynamic detection downstream.

The scope limitation to Identifier source types (excluding MemberExpression like this.items.map() or CallExpression like getItems().map()) aligns with actual usage patterns in the codebase, where all Promise.all examples use direct identifier references. This limitation is acceptable for the current requirements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/bubble-runtime/src/extraction/BubbleParser.ts (2)

3950-3955: Consider supporting MemberExpression sources for dynamic parallel detection.

Currently, only Identifier sources are marked as dynamic (e.g., items.map(...)). Sources like this.items.map(...) or obj.array.map(...) (which would have sourceArray.type === 'MemberExpression') won't be detected as dynamic, even though they represent the same pattern.

If this limitation is intentional for the MVP, consider documenting it. Otherwise, you may want to extend support:

🔎 Potential enhancement for MemberExpression sources
 const sourceArray = decl.init.callee.object;

 if (sourceArray.type === 'Identifier') {
   sourceArrayName = sourceArray.name;
   isDynamic = true;
+} else if (sourceArray.type === 'MemberExpression') {
+  // Extract the full member expression as source (e.g., "this.items")
+  sourceArrayName = this.bubbleScript.substring(
+    sourceArray.range![0],
+    sourceArray.range![1]
+  );
+  isDynamic = true;
 }

928-932: LGTM: Formatting improvements for location extraction.

The conversion of inline location object literals to multi-line format improves readability without changing functionality. These are pure refactoring changes.

Also applies to: 949-952, 974-977, 1928-1932, 1949-1953, 1974-1978, 3394-3396

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dae2431 and 4eaff40.

📒 Files selected for processing (1)
  • packages/bubble-runtime/src/extraction/BubbleParser.ts
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-12-22T09:55:47.873Z
Learnt from: CR
Repo: bubblelabai/BubbleLab PR: 0
File: .cursor/rules/bubblelab.mdc:0-0
Timestamp: 2025-12-22T09:55:47.873Z
Learning: Applies to packages/bubble-runtime/src/extraction/README.md : Refer to packages/bubble-runtime/src/extraction/README.md for information about bubble parsing, dependency graphs, and per-invocation cloning

Applied to files:

  • packages/bubble-runtime/src/extraction/BubbleParser.ts
📚 Learning: 2025-12-19T03:17:06.825Z
Learnt from: CR
Repo: bubblelabai/BubbleLab PR: 0
File: .cursor/rules/bundling.mdc:0-0
Timestamp: 2025-12-19T03:17:06.825Z
Learning: Applies to packages/bubble-core/dist/**/*.d.ts : The type bundling system for Monaco Editor must process TypeScript declaration files (.d.ts) and inline all dependencies into a single self-contained bundle in `packages/bubble-core/dist/bubble-bundle.d.ts`

Applied to files:

  • packages/bubble-runtime/src/extraction/BubbleParser.ts
📚 Learning: 2025-12-19T03:17:06.825Z
Learnt from: CR
Repo: bubblelabai/BubbleLab PR: 0
File: .cursor/rules/bundling.mdc:0-0
Timestamp: 2025-12-19T03:17:06.825Z
Learning: Applies to packages/bubble-core/dist/**/*.d.ts : All external imports (except relative imports) must be removed from the bundled output to prevent circular processing and external dependency issues

Applied to files:

  • packages/bubble-runtime/src/extraction/BubbleParser.ts
🧬 Code graph analysis (1)
packages/bubble-runtime/src/extraction/BubbleParser.ts (2)
packages/bubble-shared-schemas/src/types.ts (1)
  • BubbleName (51-100)
packages/bubble-shared-schemas/src/bubble-definition-schema.ts (1)
  • WorkflowNode (410-419)
🔇 Additional comments (2)
packages/bubble-runtime/src/extraction/BubbleParser.ts (2)

4124-4146: Well done addressing the rest element handling!

The array destructuring logic now correctly handles both simple identifiers and rest elements (like const [a, ...rest] = await Promise.all([...])), addressing the concern raised in the previous review.


4185-4195: Schema alignment verified for dynamic parallel execution fields.

The schema in packages/bubble-shared-schemas/src/bubble-definition-schema.ts correctly declares isDynamic?: boolean and sourceArray?: string as optional fields on ParallelExecutionWorkflowNode. The code at lines 4185-4195 properly returns these fields, and test coverage in BubbleParser.test.ts confirms downstream consumers correctly handle them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Promise.all on mapped dynamic objectes not visualized properly Legitment Pearl response formatted in json blocks

2 participants