Skip to content

Conversation

@javier-godoy
Copy link
Member

@javier-godoy javier-godoy commented Nov 28, 2025

Summary by CodeRabbit

  • New Features

    • Return and receive JSON from client-callable component methods with optional runtime instrumentation for newer Vaadin versions.
    • New marker annotations and route registration helper to publish instrumented views.
    • Public JSON codec helpers to encode values without type info.
  • Documentation

    • README and Developer Guide expanded with examples, patterns, and instrumentation usage notes.
  • Chores

    • Added an optional bytecode/instrumentation library dependency.

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

@coderabbitai
Copy link

coderabbitai bot commented Nov 28, 2025

Walkthrough

Adds runtime bytecode instrumentation to produce instrumented Vaadin component subclasses that adapt ClientCallable/LegacyClientCallable methods for JsonValue handling; introduces instrumentation helpers, route registration support, new annotations, JsonCodec/JsonMigration API extensions, README examples, and an optional ASM dependency.

Changes

Cohort / File(s) Summary
Documentation & Build
README.md, pom.xml
Adds README sections/examples for returning and receiving JSON in ClientCallable methods; changes example return type to JsonValue; documents instrumentation usage; adds optional dependency org.ow2.asm:asm:9.8 to pom.xml.
Bytecode Instrumentation Core
src/main/java/com/flowingcode/vaadin/jsonmigration/ClassInstrumentationUtil.java
New utility that generates instrumented subclasses via ASM, overrides non-static/non-private methods annotated with ClientCallable/LegacyClientCallable, converts JsonValue parameters/results using JsonMigration helpers, enforces constructor/visibility constraints, and caches per classloader.
Instrumentation API & Helpers
src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigration.java, src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigrationHelper*.java, src/main/java/com/flowingcode/vaadin/jsonmigration/LegacyJsonMigrationHelper.java
Adds JsonMigration.instrumentClass(...) and corresponding instrumentClass methods on helper implementations; legacy helper is no-op.
Route registration & initializer
src/main/java/com/flowingcode/vaadin/jsonmigration/InstrumentationViewInitializer.java
New abstract VaadinServiceInitListener with registerInstrumentedRoute(Class<? extends Component>) that validates @InstrumentedRoute, optionally instruments classes for Vaadin 25+, and registers routes.
Annotations
src/main/java/com/flowingcode/vaadin/jsonmigration/InstrumentedRoute.java, src/main/java/com/flowingcode/vaadin/jsonmigration/LegacyClientCallable.java
Adds runtime-retained @InstrumentedRoute(String value) for component route metadata and @LegacyClientCallable to mark methods to be treated as ClientCallable when instrumented.
JSON API
src/main/java/com/flowingcode/vaadin/jsonmigration/JsonCodec.java
Makes JsonCodec public and adds canEncodeWithoutTypeInfo(Class<?>) and encodeWithoutTypeInfo(Object) helpers for encoding values to JsonValue.
README examples
README.md (examples)
Updates examples to use JsonValue return types and documents LegacyClientCallable and instrumentation setup (JsonMigration.instrumentClass, InstrumentedRoute, InstrumentationViewInitializer).

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120+ minutes

  • Areas needing extra attention:
    • ClassInstrumentationUtil.java: ASM bytecode generation, method descriptor correctness, exception table and stack map frames, parameter/return conversions, classloader define/caching, and generated constructor semantics.
    • Integration points between generated code and JsonMigration/JsonCodec conversion helpers (type mapping and runtime compatibility).
    • InstrumentationViewInitializer.java: Vaadin version detection, route registration behavior, and error handling for missing annotations.
    • Build impact and shading/packaging implications of adding optional org.ow2.asm:asm:9.8.

Possibly related PRs

Suggested reviewers

  • mlopezFC
  • paodb

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the two main features added: class instrumentation utilities and JsonCodec encoding enhancement, both of which are comprehensively covered in the changeset.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch instrumentation

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: 7

🧹 Nitpick comments (5)
README.md (1)

16-17: Clarify the purpose of JsonSerializer and JsonCodec in the features bullet.

Line 17 describes these classes but doesn't explain their purpose. Consider revising to clarify what serialization/deserialization value they provide to users.

-- **JsonSerializer and JsonCodec**: Includes `JsonSerializer` and `JsonCodec` classes for serialization and deserialization of elemental JSON values.
+- **JSON serialization/deserialization utilities**: Includes `JsonSerializer` and `JsonCodec` classes to convert between Java objects and elemental JSON values.
src/main/java/com/flowingcode/vaadin/jsonmigration/JsonCodec.java (1)

39-41: Remove unused imports.

The imports for Node and ReturnChannelRegistration are not used anywhere in this file.

Apply this diff to remove the unused imports:

-import com.vaadin.flow.dom.Node;
 import com.vaadin.flow.internal.ReflectTools;
-import com.vaadin.flow.internal.nodefeature.ReturnChannelRegistration;
src/main/java/com/flowingcode/vaadin/jsonmigration/InstrumentedRoute.java (1)

33-35: Consider adding @Documented for consistency.

LegacyClientCallable includes @Documented but this annotation does not. Since InstrumentedRoute is part of the public API and will appear in component class declarations, adding @Documented would ensure it appears in generated Javadoc.

+import java.lang.annotation.Documented;
...
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
+@Documented
 public @interface InstrumentedRoute {
src/main/java/com/flowingcode/vaadin/jsonmigration/InstrumentationViewInitializer.java (1)

56-60: Avoid reassigning method parameter.

Reassigning navigationTarget on line 58 is a code smell that can make the method harder to reason about. Consider using a local variable instead.

     String route = annotation.value();
+    Class<? extends Component> targetClass = navigationTarget;
     if (Version.getMajorVersion() > 24) {
-      navigationTarget = JsonMigration.instrumentClass(navigationTarget);
+      targetClass = JsonMigration.instrumentClass(navigationTarget);
     }
-    RouteConfiguration.forApplicationScope().setRoute(route, navigationTarget);
+    RouteConfiguration.forApplicationScope().setRoute(route, targetClass);
src/main/java/com/flowingcode/vaadin/jsonmigration/ClassInstrumentationUtil.java (1)

348-379: Consider using Type.getDescriptor() from ASM.

The getTypeDescriptor method manually builds type descriptors, but ASM's Type.getDescriptor(Class<?>) already provides this functionality (and is already imported). This would reduce code duplication and potential for errors.

     private String getTypeDescriptor(Class<?> type) {
-      if (type == Void.TYPE) {
-        return "V";
-      }
-      if (type == Boolean.TYPE) {
-        return "Z";
-      }
-      // ... all the manual cases
-      return "L" + type.getName().replace('.', '/') + ";";
+      return Type.getDescriptor(type);
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 295f183 and dff4982.

📒 Files selected for processing (11)
  • README.md (2 hunks)
  • pom.xml (1 hunks)
  • src/main/java/com/flowingcode/vaadin/jsonmigration/ClassInstrumentationUtil.java (1 hunks)
  • src/main/java/com/flowingcode/vaadin/jsonmigration/InstrumentationViewInitializer.java (1 hunks)
  • src/main/java/com/flowingcode/vaadin/jsonmigration/InstrumentedRoute.java (1 hunks)
  • src/main/java/com/flowingcode/vaadin/jsonmigration/JsonCodec.java (3 hunks)
  • src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigration.java (2 hunks)
  • src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigrationHelper.java (2 hunks)
  • src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigrationHelper25.java (2 hunks)
  • src/main/java/com/flowingcode/vaadin/jsonmigration/LegacyClientCallable.java (1 hunks)
  • src/main/java/com/flowingcode/vaadin/jsonmigration/LegacyJsonMigrationHelper.java (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-25T16:35:42.544Z
Learnt from: javier-godoy
Repo: FlowingCode/JsonMigrationHelper PR: 11
File: src/main/java/com/flowingcode/vaadin/jsonmigration/JsonSerializer.java:281-301
Timestamp: 2025-11-25T16:35:42.544Z
Learning: In the JsonMigrationHelper project, code copied from the Vaadin codebase should be kept consistent with the original source unless there's a specific reason to deviate.

Applied to files:

  • src/main/java/com/flowingcode/vaadin/jsonmigration/JsonCodec.java
  • src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigrationHelper.java
  • README.md
  • src/main/java/com/flowingcode/vaadin/jsonmigration/LegacyJsonMigrationHelper.java
  • src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigrationHelper25.java
  • src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigration.java
  • src/main/java/com/flowingcode/vaadin/jsonmigration/ClassInstrumentationUtil.java
🧬 Code graph analysis (1)
src/main/java/com/flowingcode/vaadin/jsonmigration/InstrumentationViewInitializer.java (1)
src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigration.java (1)
  • JsonMigration (44-208)
🔇 Additional comments (9)
README.md (1)

128-151: I'll verify the public API classes and annotations referenced in the instrumentation example by searching the codebase for their definitions.
<tool_calls>

#!/bin/bash

Search for the public instrumentation-related classes and annotations

Find @InstrumentedRoute annotation

echo "=== Searching for @InstrumentedRoute ==="
rg -n '@interface InstrumentedRoute|class InstrumentedRoute' --type java -A 5

echo ""
echo "=== Searching for @LegacyClientCallable ==="
rg -n '@interface LegacyClientCallable|class LegacyClientCallable' --type java -A 5

echo ""
echo "=== Searching for InstrumentationViewInitializer ==="
rg -n 'class InstrumentationViewInitializer' --type java -A 10

echo ""
echo "=== Searching for registerInstrumentedRoute method ==="
rg -n 'registerInstrumentedRoute' --type java -B 2 -A 3



</function_calls>

pom.xml (1)

71-76: ASM 9.8 dependency addition looks good.

ASM 9.8 is a current, stable release and appropriately marked as optional since bytecode instrumentation is an optional feature. The exact version pinning provides reproducible builds. No known security concerns identified for this version.

src/main/java/com/flowingcode/vaadin/jsonmigration/JsonCodec.java (2)

64-64: LGTM!

Making the class public is appropriate for the new API surface supporting class instrumentation.


64-153: New public API methods are not tested, documented, or used within the codebase.

The methods canEncodeWithoutTypeInfo() and encodeWithoutTypeInfo() are newly made public but lack integration with the rest of the project:

  • No test coverage: The repository contains no test files (no src/test directory). These new public methods have no dedicated tests.
  • No internal usage: The methods are not used anywhere in the instrumentation code or elsewhere in the codebase. encodeWithoutTypeInfo() only calls canEncodeWithoutTypeInfo() internally (lines 138, 150).
  • No documentation: While the README lists JsonCodec as a feature for "serialization and deserialization," the new public methods are not documented in the Developer Guide or README.

Consider:

  1. Adding test coverage for the new public methods
  2. Clarifying the intended use case (external API? future internal use?)
  3. Documenting these methods in the Developer Guide if they are part of the public API contract
src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigrationHelper.java (1)

39-40: Well-designed interface method for instrumentation.

The method signature correctly uses Class<? extends T> return type, allowing implementations to return either the original class (legacy path) or an instrumented subclass (Vaadin 25+ path). This is consistent with the strategy pattern used elsewhere in this interface.

src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigrationHelper25.java (1)

45-48: Clean delegation to the instrumentation utility.

The implementation correctly delegates to ClassInstrumentationUtil.instrumentClass, maintaining separation of concerns.

src/main/java/com/flowingcode/vaadin/jsonmigration/LegacyJsonMigrationHelper.java (1)

34-37: Correct no-op implementation for legacy Vaadin versions.

Returning the original class unchanged is the expected behavior for Vaadin 24 and earlier, where the elemental.json API is natively supported.

src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigration.java (1)

172-206: Excellent documentation for the public instrumentation API.

The Javadoc thoroughly documents the version-specific behavior, return value semantics, exception conditions, and cross-references to related APIs. This provides good guidance for library consumers.

src/main/java/com/flowingcode/vaadin/jsonmigration/ClassInstrumentationUtil.java (1)

144-168: The review comment is based on a misunderstanding of the instrumentation design.

The use of getDeclaredMethods() is intentional and correct. The class documentation (lines 53-72) clearly states that this utility creates dynamic subclasses that override methods annotated with @ClientCallable. The instrumentation works by creating a wrapper subclass (e.g., ClassName$Instrumented) for the specific class passed to instrumentClass().

If a parent class has @ClientCallable methods returning JsonValue, that parent class is instrumented separately when it's passed to instrumentClass(). Child classes only need their own declared methods instrumented. This is not a limitation—it's the correct design for a wrapper-based instrumentation system. Including inherited methods would result in redundant or incorrect overrides.

Likely an incorrect or invalid review 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

🧹 Nitpick comments (2)
README.md (1)

145-151: Consider adding service registration note.

The ViewInitializerImpl example shows extending InstrumentationViewInitializer, but doesn't mention that the implementation needs to be registered as a Vaadin service. Users may need to add a META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener file or use @SpringComponent annotation (for Spring) to ensure the initializer is discovered.

Consider adding a note like:

**Note:** Register `ViewInitializerImpl` via `META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener` or use `@SpringComponent` with Spring.
src/main/java/com/flowingcode/vaadin/jsonmigration/LegacyClientCallable.java (1)

29-37: Annotation is properly defined.

The annotation has the correct retention policy, target, and is documented. Consider expanding the Javadoc to explain when to use @LegacyClientCallable instead of @ClientCallable (i.e., when methods have JsonValue parameters).

Optional enhancement for the Javadoc:

 /**
- * When instrumented, publishes the annotated method as if {@link ClientCallable} has been used.
+ * Marks a method to be published as callable from client-side JavaScript when the containing
+ * class is instrumented. Use this annotation instead of {@link ClientCallable} when the method
+ * has {@code JsonValue} parameters, which are not directly supported by {@code @ClientCallable}
+ * in Vaadin 25+.
  */
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 353df8c and 3353729.

📒 Files selected for processing (10)
  • README.md (2 hunks)
  • pom.xml (1 hunks)
  • src/main/java/com/flowingcode/vaadin/jsonmigration/ClassInstrumentationUtil.java (1 hunks)
  • src/main/java/com/flowingcode/vaadin/jsonmigration/InstrumentationViewInitializer.java (1 hunks)
  • src/main/java/com/flowingcode/vaadin/jsonmigration/InstrumentedRoute.java (1 hunks)
  • src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigration.java (2 hunks)
  • src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigrationHelper.java (2 hunks)
  • src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigrationHelper25.java (2 hunks)
  • src/main/java/com/flowingcode/vaadin/jsonmigration/LegacyClientCallable.java (1 hunks)
  • src/main/java/com/flowingcode/vaadin/jsonmigration/LegacyJsonMigrationHelper.java (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/main/java/com/flowingcode/vaadin/jsonmigration/InstrumentedRoute.java
  • src/main/java/com/flowingcode/vaadin/jsonmigration/LegacyJsonMigrationHelper.java
  • src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigrationHelper25.java
  • src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigration.java
  • pom.xml
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-25T16:35:42.544Z
Learnt from: javier-godoy
Repo: FlowingCode/JsonMigrationHelper PR: 11
File: src/main/java/com/flowingcode/vaadin/jsonmigration/JsonSerializer.java:281-301
Timestamp: 2025-11-25T16:35:42.544Z
Learning: In the JsonMigrationHelper project, code copied from the Vaadin codebase should be kept consistent with the original source unless there's a specific reason to deviate.

Applied to files:

  • src/main/java/com/flowingcode/vaadin/jsonmigration/ClassInstrumentationUtil.java
  • src/main/java/com/flowingcode/vaadin/jsonmigration/InstrumentationViewInitializer.java
  • src/main/java/com/flowingcode/vaadin/jsonmigration/LegacyClientCallable.java
  • src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigrationHelper.java
  • README.md
🧬 Code graph analysis (1)
src/main/java/com/flowingcode/vaadin/jsonmigration/InstrumentationViewInitializer.java (1)
src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigration.java (1)
  • JsonMigration (44-208)
🔇 Additional comments (8)
src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigrationHelper.java (1)

39-40: LGTM!

The new instrumentClass method signature is well-designed with proper generic bounds, ensuring type safety when returning an instrumented subclass of the provided component class.

README.md (1)

128-134: Good documentation of the instrumentation complexity.

The note about instrumentation being a complex mechanism and suggesting a rewrite of affected code is helpful for users to understand the tradeoffs.

src/main/java/com/flowingcode/vaadin/jsonmigration/InstrumentationViewInitializer.java (1)

48-61: Well-structured route registration logic.

The implementation correctly:

  • Validates the @InstrumentedRoute annotation presence before proceeding
  • Only instruments classes when Vaadin major version > 24
  • Registers routes at application scope

The Javadoc accurately describes the behavior.

src/main/java/com/flowingcode/vaadin/jsonmigration/ClassInstrumentationUtil.java (5)

65-66: Good use of WeakHashMap with proper synchronization.

The WeakHashMap for classLoaderCache allows classloaders to be garbage collected, preventing memory leaks in dynamic classloader environments. The synchronized block in getOrCreateInstrumentedClassLoader ensures thread-safe access.

Also applies to: 193-197


95-145: Comprehensive validation and error handling.

The method properly validates input constraints (null, interface, primitive, array, final class) and handles constructor accessibility checks. The exception handling for setAccessible is appropriately wrapped.


267-284: Potential runtime ClassCastException on JsonValue subtype parameters.

When paramType != JsonValue.class (e.g., JsonObject), the code emits a CHECKCAST after convertToJsonValue. If the actual JSON structure doesn't match the expected subtype (e.g., method expects JsonObject but receives a JsonArray), this will throw ClassCastException at runtime. This is acceptable behavior but consider documenting this constraint.


347-362: Type mapping covers common cases.

The getConvertedTypeDescriptor method maps elemental JSON types to Jackson equivalents. The fallback to JsonNode for unrecognized JsonValue subtypes is appropriate.


151-175: I'll help you verify whether the getInstrumentableMethods method should include inherited methods from superclasses. Let me investigate the codebase to understand the design intent and if this is truly a concern.
<function_calls>

#!/bin/bash

First, let's understand the broader context of class instrumentation

Search for usages of getInstrumentableMethods and related method collection patterns

rg -n "getInstrumentableMethods|getDeclaredMethods" --type=java -B3 -A3 | head -80


</function_calls>

<function_calls>

#!/bin/bash

Search for @ClientCallable and @LegacyClientCallable annotations to understand usage patterns

rg -n "@ClientCallable|@LegacyClientCallable" --type=java -B2 -A2 | head -150


</function_calls>

<function_calls>

#!/bin/bash

Look for class hierarchy/inheritance patterns in tests related to instrumentation

fd -e java -type f | xargs rg -l "extends.@ClientCallable|extends.@LegacyClientCallable|class.extends.{" | head -20


</function_calls>

<function_calls>

#!/bin/bash

Find the complete context of getInstrumentableMethods and how it's used

rg -n "getInstrumentableMethods" --type=java -B5 -A10


</function_calls>

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 (1)
README.md (1)

128-153: Consider clarifying instrumentation guidance and META-INF/services registration.

The instrumentation section explains the what but could benefit from more explanation of the why. Users might better understand the need for instrumentation if you briefly explain that @ClientCallable doesn't support JsonValue parameters due to framework limitations, and instrumentation is one way to work around this.

Additionally, the META-INF/services registration comment could be more explicit. Consider adding a brief example of the actual configuration file structure (e.g., a file named com.vaadin.flow.server.VaadinServiceInitListener in META-INF/services/ with the class name as content).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3353729 and ac50922.

📒 Files selected for processing (1)
  • README.md (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-25T16:35:42.544Z
Learnt from: javier-godoy
Repo: FlowingCode/JsonMigrationHelper PR: 11
File: src/main/java/com/flowingcode/vaadin/jsonmigration/JsonSerializer.java:281-301
Timestamp: 2025-11-25T16:35:42.544Z
Learning: In the JsonMigrationHelper project, code copied from the Vaadin codebase should be kept consistent with the original source unless there's a specific reason to deviate.

Applied to files:

  • README.md
🔇 Additional comments (3)
README.md (3)

16-17: Approve feature bullets.

These additions appropriately highlight the new ClientCallable and JSON serialization capabilities. The descriptions are clear and concise.


128-153: I'll help you verify the instrumentation API classes and the completeness of the documentation. Let me search the codebase for these classes and methods.
<function_calls>

#!/bin/bash

Search for the instrumentation-related classes

echo "=== Searching for LegacyClientCallable ==="
rg -n 'class LegacyClientCallable|@interface LegacyClientCallable' --type=java -B 2 -A 5

echo -e "\n=== Searching for InstrumentedRoute ==="
rg -n 'class InstrumentedRoute|@interface InstrumentedRoute' --type=java -B 2 -A 5

echo -e "\n=== Searching for InstrumentationViewInitializer ==="
rg -n 'class InstrumentationViewInitializer' --type=java -B 2 -A 10

echo -e "\n=== Searching for JsonMigration.instrumentClass ==="
rg -n 'instrumentClass|JsonMigration' --type=java -B 2 -A 5 | head -50

echo -e "\n=== Searching for META-INF/services ==="
find . -path '/META-INF/services/' 2>/dev/null | head -20


</function_calls>


122-126: ClientCallable example is accurate.

The verification confirms both claims: @ClientCallable methods can return JsonValue directly (as evidenced by ClassInstrumentationUtil.java:166 checking isCallable && hasJsonValueReturn), and JsonMigration.convertToClientCallableResult() is the correct public static helper method for this purpose (defined in JsonMigration.java:76-78 with generic signature <T extends JsonValue> T convertToClientCallableResult(T object)).

@paodb paodb merged commit aa010ea into master Nov 28, 2025
3 checks passed
@github-project-automation github-project-automation bot moved this from To Do to Pending release in Flowing Code Addons Nov 28, 2025
@paodb paodb deleted the instrumentation branch November 28, 2025 19:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Pending release

Development

Successfully merging this pull request may close these issues.

3 participants