Skip to content

Conversation

@tbouffard
Copy link
Member

@tbouffard tbouffard commented Jul 20, 2025

The current implementation of the function applying mixins creates shared state for "object" or "array" properties.
For such properties, ensure they are all defined in AbstractGraph and add tests to validate that there is no global
states. Here, this includes a fix for 'alternateEdgeStyle' which is no longer a global state.
In some mixin type files, remove properties that are already defined in AbstractGraph.

Tasks

  • See fix: make Graph.options truly per-instance #751 that workaround similar problems in the past
  • add tests on other properties of AbstractGraph to ensure they are not a shared state
    • alternateEdgeStyle: object, currently on EdgeMixin, so probably a shared state
    • multiplicities: array
    • options: object
    • check all mixins, they shouldn't have properties that are object or array as the mixin application currently creates shared state in the case (no other objects initialized to no nullish value in mixin)
  • Verify that validation stories still work (for multiplicities)

Summary by CodeRabbit

  • New Features

    • Per-graph alternate edge style when toggling an edge’s control point.
    • Configurable graph folding options with defaults (collapsed/expanded images, collapse-to-preferred-size).
    • Support for multiplicity constraints on edges with import/export serialization.
  • Refactor

    • Reorganized graph-level properties (consolidation and relocation) without changing runtime behavior.
  • Documentation

    • Expanded guidance on mixins and risks of shared object/array state.
  • Tests

    • Added tests ensuring mixin-derived properties are per-instance and updated serialization tests.

@tbouffard tbouffard added the refactor Code refactoring label Jul 20, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jul 20, 2025

Walkthrough

Public API changes move folding options and alternateEdgeStyle onto AbstractGraph, converting some mixin-provided properties to class-level. Tests are updated to validate per-instance state and expanded serialization (including alternateEdgeStyle, multiplicities, and options). Documentation comments are revised; type-only imports introduced; no functional logic changes beyond property relocation.

Changes

Cohort / File(s) Summary of changes
AbstractGraph API updates
packages/core/src/view/AbstractGraph.ts
Added public alternateEdgeStyle: CellStyle = {}; introduced/relocated options: GraphFoldingOptions with defaults; converted ImageBundle to type-only import; updated variable grouping comments; removed duplicate bottom options declaration.
Edge mixin realignment
packages/core/src/view/mixins/EdgeMixin.ts, packages/core/src/view/mixins/EdgeMixin.type.ts
Moved alternateEdgeStyle from PartialEdge/Edge mixin to PartialGraph/graph-level; removed default alternateEdgeStyle from EdgeMixin export; removed alternateEdgeStyle from AbstractGraph interface in EdgeMixin types.
Image mixin type cleanup
packages/core/src/view/mixins/ImageMixin.type.ts
Removed imageBundles: ImageBundle[] augmentation from AbstractGraph; other image mixin APIs unchanged.
Internal utils docs
packages/core/src/internal/utils.ts
Expanded docblock for mixInto about complex properties and serialization caveats; added inline comments; no behavior change.
Serialization tests update
packages/core/__tests__/serialization/codecs/all-graph-classes.test.ts
Added Multiplicity to public import; used graph.multiplicities; updated expected XML to include alternateEdgeStyle, multiplicities entry, and options with images; ensured multiplicity is serialized.
BaseGraph instance-state tests
packages/core/__tests__/view/BaseGraph.test.ts
Added tests ensuring no shared global state for mixin-derived properties: alternateEdgeStyle, cells, imageBundles, mouseListeners, multiplicities, options; added imports for ImageBundle and Multiplicity.

Sequence Diagram(s)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title is concise, uses the Conventional Commits "refactor:" prefix, and accurately summarizes the primary intent of the changes (prevent mixins from creating shared object/array properties on AbstractGraph), so a reviewer can quickly understand the main change.
Description Check ✅ Passed The PR description clearly states the bug (mixins creating shared state), the intended refactor to move object/array properties into AbstractGraph, and enumerates completed tasks and tests that validate the change. It references prior work (#751) and the specific tests added for alternateEdgeStyle, multiplicities, and options. However, it does not strictly follow the repository template: the explicit PR Checklist block and the 'Overview'/'Notes' headings are missing and there is no explicit "closes #" line or a clear statement about breaking/public API changes.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/move_mulitiplicities_from_AbstractGraph_to_mixin

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


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.

❤️ Share

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

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: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between af6c3be and 3eabfae.

📒 Files selected for processing (3)
  • packages/core/src/view/AbstractGraph.ts (0 hunks)
  • packages/core/src/view/mixins/ValidationMixin.ts (1 hunks)
  • packages/core/src/view/mixins/ValidationMixin.type.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/core/src/view/AbstractGraph.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: tbouffard
PR: maxGraph/maxGraph#776
File: packages/core/src/view/AbstractGraph.ts:492-495
Timestamp: 2025-04-24T12:34:56.726Z
Learning: PR #776 focuses on refactoring to introduce AbstractGraph as a base class and moving code from Graph to AbstractGraph. Type improvements and other refinements should be addressed in future PRs to keep this one focused on the core architectural change.
Learnt from: tbouffard
PR: maxGraph/maxGraph#857
File: packages/core/src/view/plugins/SelectionHandler.ts:1081-1092
Timestamp: 2025-06-26T13:27:19.012Z
Learning: User tbouffard prefers to keep PRs focused on their main objective and defer implementation improvements or logic fixes to future dedicated PRs. For maxGraph, refactoring PRs like Dictionary→Map migration should not include unrelated logic improvements even when valid issues are identified in the migrated code.
Learnt from: tbouffard
PR: maxGraph/maxGraph#715
File: packages/html/stories/ZoomAndFit.stories.ts:0-0
Timestamp: 2025-03-14T06:01:22.326Z
Learning: For future improvements to the Graph component in maxGraph, tbouffard is interested in refactoring the `fit` method to use an object parameter pattern instead of multiple positional parameters, as the current approach makes the API harder to use when many parameters need to be specified with default values.
Learnt from: tbouffard
PR: maxGraph/maxGraph#593
File: .github/workflows/create-github-release.yml:42-42
Timestamp: 2025-01-28T16:22:25.804Z
Learning: In the GitHub release notes template for maxGraph, TODO comments are intentionally included to mark sections that must be reviewed by maintainers before publishing the release. These TODOs should not be removed or converted to validation steps as they are part of the release review process.
Learnt from: tbouffard
PR: maxGraph/maxGraph#740
File: packages/core/src/util/requestUtils.ts:79-106
Timestamp: 2025-03-31T08:32:17.055Z
Learning: PR #740 is focused on reorganizing code into namespaces (guiUtils and requestUtils) without changing any implementations. Implementation improvements should be suggested for future PRs.
Learnt from: tbouffard
PR: maxGraph/maxGraph#849
File: packages/html/stories/DragSource.stories.js:98-101
Timestamp: 2025-06-13T07:48:10.300Z
Learning: User tbouffard prefers answers in English; avoid switching to other languages in future replies.
packages/core/src/view/mixins/ValidationMixin.type.ts (4)
Learnt from: tbouffard
PR: maxGraph/maxGraph#791
File: packages/core/src/view/style/edge/Segment.ts:26-35
Timestamp: 2025-04-29T13:29:45.122Z
Learning: In the maxGraph project, JSDoc `{@link}` references to symbols don't need to be imported in the current file to work correctly. TypeDoc can resolve symbols across the entire project namespace, not just within the current file, which is common in modern documentation systems and IDEs.
Learnt from: tbouffard
PR: maxGraph/maxGraph#770
File: packages/core/src/view/cell/CellTracker.ts:99-101
Timestamp: 2025-04-18T07:02:31.931Z
Learning: In TypeScript, when implementing an interface, methods can have fewer parameters than declared in the interface due to TypeScript's structural typing system. Both omitting unused parameters entirely and prefixing them with underscore (e.g., _sender) are valid patterns in the maxGraph codebase.
Learnt from: tbouffard
PR: maxGraph/maxGraph#593
File: .github/workflows/create-github-release.yml:42-42
Timestamp: 2025-01-28T16:22:25.804Z
Learning: In the GitHub release notes template for maxGraph, TODO comments are intentionally included to mark sections that must be reviewed by maintainers before publishing the release. These TODOs should not be removed or converted to validation steps as they are part of the release review process.
Learnt from: tbouffard
PR: maxGraph/maxGraph#776
File: packages/core/src/view/AbstractGraph.ts:492-495
Timestamp: 2025-04-24T12:34:56.726Z
Learning: PR #776 focuses on refactoring to introduce AbstractGraph as a base class and moving code from Graph to AbstractGraph. Type improvements and other refinements should be addressed in future PRs to keep this one focused on the core architectural change.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: build (macos-14)
  • GitHub Check: build (ubuntu-24.04)
  • GitHub Check: build (windows-2022)
  • GitHub Check: build
🔇 Additional comments (1)
packages/core/src/view/mixins/ValidationMixin.type.ts (1)

23-26: Good addition of contextual JSDoc
Adds missing description and cross–links; nothing to change.

@tbouffard tbouffard marked this pull request as draft July 22, 2025 07:14
@tbouffard tbouffard changed the title refactor: move multiplicities from AbstractGraph to ValidationMixin refactor: ensure mixins do not create shared properties in AbstractGraph Jul 23, 2025
@sonarqubecloud
Copy link

The current implementation of the function applying mixins creates shared state for "object" or "array" properties.
For such properties, ensure they are all defined in AbstractGraph and add tests to validate that there is no global
states. Here, this includes a fix for 'alternateEdgeStyle' which is no longer a global state.
In some mixin type files, remove properties that are already defined in AbstractGraph.
@tbouffard tbouffard force-pushed the refactor/move_mulitiplicities_from_AbstractGraph_to_mixin branch from 66115fc to 37219de Compare September 17, 2025 09:15
@tbouffard tbouffard marked this pull request as ready for review September 17, 2025 09:18
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/core/src/view/AbstractGraph.ts (1)

98-125: Fix shared mutable default in PageBreaksMixin (initialize per-instance)
packages/core/src/view/mixins/PageBreaksMixin.ts:59 — exported PageBreaksMixin contains an array literal property (shared mutable state); change to a factory/getter or initialize the array inside each instance so it isn’t shared.

🧹 Nitpick comments (2)
packages/core/__tests__/view/BaseGraph.test.ts (1)

296-310: Avoid brittle assumptions about default listener count

Asserting exact initial length (1) can break if defaults change. Derive from the initial value instead.

-  const graph1 = new BaseGraph();
-  expect(graph1.mouseListeners).toHaveLength(1);
-  const graph2 = new BaseGraph();
-  expect(graph2.mouseListeners).toHaveLength(1);
+  const graph1 = new BaseGraph();
+  const graph2 = new BaseGraph();
+  const g1Initial = graph1.mouseListeners.length;
+  const g2Initial = graph2.mouseListeners.length;
+  expect(g1Initial).toBe(g2Initial);

   graph1.mouseListeners.push({
     mouseDown: () => {},
     mouseMove: () => {},
     mouseUp: () => {},
   });
-  expect(graph2.mouseListeners).toHaveLength(1);
-  expect(graph1.mouseListeners).toHaveLength(2);
+  expect(graph2.mouseListeners).toHaveLength(g2Initial);
+  expect(graph1.mouseListeners).toHaveLength(g1Initial + 1);
packages/core/src/view/AbstractGraph.ts (1)

92-95: Comment wording nit

Consider clarifying that these variables used to live in mixins but must be instantiated per graph to avoid shared state.

-// Group: Variables that should be in the mixins
-// The current implementation of the function applying mixins create a shared state for properties with "complex" type (object, array)
-// whereas here, we need to be created a specific instance for each new class instance.
+// Group: Variables previously in mixins but requiring per-instance initialization
+// The mixin application currently causes shared state for complex types (object/array),
+// so these are defined here to ensure a fresh instance per Graph.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3eabfae and 37219de.

📒 Files selected for processing (7)
  • packages/core/__tests__/serialization/codecs/all-graph-classes.test.ts (3 hunks)
  • packages/core/__tests__/view/BaseGraph.test.ts (2 hunks)
  • packages/core/src/internal/utils.ts (2 hunks)
  • packages/core/src/view/AbstractGraph.ts (4 hunks)
  • packages/core/src/view/mixins/EdgeMixin.ts (1 hunks)
  • packages/core/src/view/mixins/EdgeMixin.type.ts (0 hunks)
  • packages/core/src/view/mixins/ImageMixin.type.ts (0 hunks)
💤 Files with no reviewable changes (2)
  • packages/core/src/view/mixins/EdgeMixin.type.ts
  • packages/core/src/view/mixins/ImageMixin.type.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/core/src/internal/utils.ts
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: tbouffard
PR: maxGraph/maxGraph#776
File: packages/core/src/view/AbstractGraph.ts:492-495
Timestamp: 2025-04-24T12:34:56.726Z
Learning: PR #776 focuses on refactoring to introduce AbstractGraph as a base class and moving code from Graph to AbstractGraph. Type improvements and other refinements should be addressed in future PRs to keep this one focused on the core architectural change.
Learnt from: tbouffard
PR: maxGraph/maxGraph#857
File: packages/core/src/view/plugins/SelectionHandler.ts:1081-1092
Timestamp: 2025-06-26T13:27:19.012Z
Learning: User tbouffard prefers to keep PRs focused on their main objective and defer implementation improvements or logic fixes to future dedicated PRs. For maxGraph, refactoring PRs like Dictionary→Map migration should not include unrelated logic improvements even when valid issues are identified in the migrated code.
Learnt from: tbouffard
PR: maxGraph/maxGraph#849
File: packages/html/stories/DragSource.stories.js:98-101
Timestamp: 2025-06-13T07:48:10.300Z
Learning: User tbouffard prefers answers in English; avoid switching to other languages in future replies.
📚 Learning: 2025-04-24T12:34:56.726Z
Learnt from: tbouffard
PR: maxGraph/maxGraph#776
File: packages/core/src/view/AbstractGraph.ts:492-495
Timestamp: 2025-04-24T12:34:56.726Z
Learning: PR #776 focuses on refactoring to introduce AbstractGraph as a base class and moving code from Graph to AbstractGraph. Type improvements and other refinements should be addressed in future PRs to keep this one focused on the core architectural change.

Applied to files:

  • packages/core/src/view/AbstractGraph.ts
📚 Learning: 2025-04-24T12:34:10.366Z
Learnt from: tbouffard
PR: maxGraph/maxGraph#776
File: packages/core/src/view/Graph.ts:85-92
Timestamp: 2025-04-24T12:34:10.366Z
Learning: The Graph class intentionally doesn't use collaborators (cellRenderer, selectionModel, view) from options in initializeCollaborators, as it's designed to replicate the previous implementation using only its factory methods for backward compatibility.

Applied to files:

  • packages/core/src/view/AbstractGraph.ts
🧬 Code graph analysis (1)
packages/core/src/view/AbstractGraph.ts (1)
packages/core/src/types.ts (2)
  • CellStyle (50-75)
  • GraphFoldingOptions (1441-1462)
🔇 Additional comments (12)
packages/core/src/view/mixins/EdgeMixin.ts (2)

27-41: Graph-level alternateEdgeStyle wiring — LGTM

Picking 'alternateEdgeStyle' from AbstractGraph in PartialGraph removes the shared-state hazard from the mixin. Matches the PR goal.


149-166: Confirm flipEdge toggle semantics didn't regress

EdgeMixin.ts currently applies alternateEdgeStyle when edge.getStyle() is non-empty and sets {} when empty — this is inverted vs. the method doc which says it should toggle between null/empty and alternateEdgeStyle (packages/core/src/view/mixins/EdgeMixin.type.ts:115–117). If this is legacy/intentional, resolve; otherwise add a test and invert the condition so empty style → apply alternateEdgeStyle, non-empty → clear it.

packages/core/__tests__/view/BaseGraph.test.ts (2)

29-31: New public types imports — LGTM

Importing ImageBundle and Multiplicity from the public surface aligns tests with the new API.


266-340: Per-instance isolation tests — LGTM

Good coverage for alternateEdgeStyle, cells, imageBundles, mouseListeners, multiplicities, and options. Confirms the refactor eliminates shared state.

packages/core/__tests__/serialization/codecs/all-graph-classes.test.ts (3)

24-25: Multiplicity import on public API — LGTM

Confirms Multiplicity is exposed for consumer use and testing.


44-62: Serialize new graph-level fields — LGTM

Template includes alternateEdgeStyle, multiplicities, and options with images. Matches the refactor.


109-121: Populate multiplicities before export — LGTM

Ensures codec covers the new field; keeps the test self-contained.

packages/core/src/view/AbstractGraph.ts (5)

49-59: Type-only import for CellStyle — LGTM

Keeps runtime lean and clarifies intent.


60-60: ImageBundle as type-only — LGTM

Avoids accidental side effects from runtime imports.


98-103: Per-instance alternateEdgeStyle — LGTM

Defining CellStyle = {} on the class removes the shared object from the mixin and aligns with tests.


118-125: Per-instance folding options — LGTM

Fresh Image instances and object literal prevent cross-graph leakage. Matches serialization expectations.


116-117: Resolved — multiplicities are per-instance and validation stories require no follow-up.
Validation.stories.ts pushes to graph.multiplicities and ValidationMixin reads this.multiplicities; AbstractGraph initializes multiplicities as an instance array.

@sonarqubecloud
Copy link

@tbouffard tbouffard merged commit f160145 into main Sep 18, 2025
2 checks passed
@tbouffard tbouffard deleted the refactor/move_mulitiplicities_from_AbstractGraph_to_mixin branch September 18, 2025 12:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactor Code refactoring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant