Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: wvlet/airframe
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v2025.1.11
Choose a base ref
...
head repository: wvlet/airframe
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v2025.1.12
Choose a head ref
  • 17 commits
  • 50 files changed
  • 6 contributors

Commits on May 28, 2025

  1. Update airframe-http, airframe-http-netty, ... to 2025.1.11 (#3927)

    ## About this PR
    📦 Updates 
    * [org.wvlet.airframe:airframe-http](https://github.com/wvlet/airframe)
    *
    [org.wvlet.airframe:airframe-http-netty](https://github.com/wvlet/airframe)
    *
    [org.wvlet.airframe:airframe-launcher](https://github.com/wvlet/airframe)
    *
    [org.wvlet.airframe:airframe-rx-html](https://github.com/wvlet/airframe)
    * [org.wvlet.airframe:sbt-airframe](https://github.com/wvlet/airframe)
    
     from `2025.1.10` to `2025.1.11`
    
    📜 [GitHub Release
    Notes](https://github.com/wvlet/airframe/releases/tag/v2025.1.11) -
    [Version
    Diff](v2025.1.10...v2025.1.11)
    
    ## Usage
    ✅ **Please merge!**
    
    I'll automatically update this PR to resolve conflicts as long as you
    don't change it yourself.
    
    If you'd like to skip this version, you can just close this PR. If you
    have any feedback, just mention me in the comments below.
    
    Configure Scala Steward for your repository with a
    [`.scala-steward.conf`](https://github.com/scala-steward-org/scala-steward/blob/f0dfaa6d8e24b261aeafbc7f99c5325ed9365cf2/docs/repo-specific-configuration.md)
    file.
    
    _Have a fantastic day writing Scala!_
    
    <details>
    <summary>⚙ Adjust future updates</summary>
    
    Add this to your `.scala-steward.conf` file to ignore future updates of
    this dependency:
    ```
    updates.ignore = [ { groupId = "org.wvlet.airframe" } ]
    ```
    Or, add this to slow down future updates of this dependency:
    ```
    dependencyOverrides = [{
      pullRequests = { frequency = "30 days" },
      dependency = { groupId = "org.wvlet.airframe" }
    }]
    ```
    </details>
    
    <sup>
    labels: library-update
    </sup>
    
    <!-- scala-steward = {
      "Update" : {
        "ForGroupId" : {
          "forArtifactIds" : [
            {
              "ForArtifactId" : {
                "crossDependency" : [
                  {
                    "groupId" : "org.wvlet.airframe",
                    "artifactId" : {
                      "name" : "airframe-http",
                      "maybeCrossName" : "airframe-http_3"
                    },
                    "version" : "2025.1.10",
                    "sbtVersion" : null,
                    "scalaVersion" : null,
                    "configurations" : null
                  },
                  {
                    "groupId" : "org.wvlet.airframe",
                    "artifactId" : {
                      "name" : "airframe-http",
                      "maybeCrossName" : "airframe-http_sjs1_3"
                    },
                    "version" : "2025.1.10",
                    "sbtVersion" : null,
                    "scalaVersion" : null,
                    "configurations" : null
                  }
                ],
                "newerVersions" : [
                  "2025.1.11"
                ],
                "newerGroupId" : null,
                "newerArtifactId" : null
              }
            },
            {
              "ForArtifactId" : {
                "crossDependency" : [
                  {
                    "groupId" : "org.wvlet.airframe",
                    "artifactId" : {
                      "name" : "airframe-http-netty",
                      "maybeCrossName" : "airframe-http-netty_3"
                    },
                    "version" : "2025.1.10",
                    "sbtVersion" : null,
                    "scalaVersion" : null,
                    "configurations" : null
                  }
                ],
                "newerVersions" : [
                  "2025.1.11"
                ],
                "newerGroupId" : null,
                "newerArtifactId" : null
              }
            },
            {
              "ForArtifactId" : {
                "crossDependency" : [
                  {
                    "groupId" : "org.wvlet.airframe",
                    "artifactId" : {
                      "name" : "airframe-launcher",
                      "maybeCrossName" : "airframe-launcher_3"
                    },
                    "version" : "2025.1.10",
                    "sbtVersion" : null,
                    "scalaVersion" : null,
                    "configurations" : null
                  }
                ],
                "newerVersions" : [
                  "2025.1.11"
                ],
                "newerGroupId" : null,
                "newerArtifactId" : null
              }
            },
            {
              "ForArtifactId" : {
                "crossDependency" : [
                  {
                    "groupId" : "org.wvlet.airframe",
                    "artifactId" : {
                      "name" : "airframe-rx-html",
                      "maybeCrossName" : "airframe-rx-html_sjs1_2.13"
                    },
                    "version" : "2025.1.10",
                    "sbtVersion" : null,
                    "scalaVersion" : null,
                    "configurations" : null
                  },
                  {
                    "groupId" : "org.wvlet.airframe",
                    "artifactId" : {
                      "name" : "airframe-rx-html",
                      "maybeCrossName" : "airframe-rx-html_sjs1_3"
                    },
                    "version" : "2025.1.10",
                    "sbtVersion" : null,
                    "scalaVersion" : null,
                    "configurations" : null
                  }
                ],
                "newerVersions" : [
                  "2025.1.11"
                ],
                "newerGroupId" : null,
                "newerArtifactId" : null
              }
            },
            {
              "ForArtifactId" : {
                "crossDependency" : [
                  {
                    "groupId" : "org.wvlet.airframe",
                    "artifactId" : {
                      "name" : "sbt-airframe",
                      "maybeCrossName" : null
                    },
                    "version" : "2025.1.10",
                    "sbtVersion" : "1.0",
                    "scalaVersion" : "2.12",
                    "configurations" : null
                  }
                ],
                "newerVersions" : [
                  "2025.1.11"
                ],
                "newerGroupId" : null,
                "newerArtifactId" : null
              }
            }
          ]
        }
      },
      "Labels" : [
        "library-update"
      ]
    } -->
    xerial-bot authored May 28, 2025
    Configuration menu
    Copy the full SHA
    7d87bd7 View commit details
    Browse the repository at this point in the history

Commits on May 29, 2025

  1. Update grpc-netty-shaded, grpc-protobuf, ... to 1.73.0 (#3924)

    ## About this PR
    📦 Updates 
    * [io.grpc:grpc-netty-shaded](https://github.com/grpc/grpc-java)
    * [io.grpc:grpc-protobuf](https://github.com/grpc/grpc-java)
    * [io.grpc:grpc-stub](https://github.com/grpc/grpc-java)
    
     from `1.72.0` to `1.73.0`
    
    📜 [GitHub Release
    Notes](https://github.com/grpc/grpc-java/releases/tag/v1.73.0) -
    [Version
    Diff](grpc/grpc-java@v1.72.0...v1.73.0)
    
    ## Usage
    ✅ **Please merge!**
    
    I'll automatically update this PR to resolve conflicts as long as you
    don't change it yourself.
    
    If you'd like to skip this version, you can just close this PR. If you
    have any feedback, just mention me in the comments below.
    
    Configure Scala Steward for your repository with a
    [`.scala-steward.conf`](https://github.com/scala-steward-org/scala-steward/blob/f0dfaa6d8e24b261aeafbc7f99c5325ed9365cf2/docs/repo-specific-configuration.md)
    file.
    
    _Have a fantastic day writing Scala!_
    
    <details>
    <summary>🔍 Files still referring to the old version number</summary>
    
    The following files still refer to the old version number (1.72.0).
    You might want to review and update them manually.
    ```
    website/static/img/undraw_monitor.svg
    ```
    </details>
    <details>
    <summary>⚙ Adjust future updates</summary>
    
    Add this to your `.scala-steward.conf` file to ignore future updates of
    this dependency:
    ```
    updates.ignore = [ { groupId = "io.grpc" } ]
    ```
    Or, add this to slow down future updates of this dependency:
    ```
    dependencyOverrides = [{
      pullRequests = { frequency = "30 days" },
      dependency = { groupId = "io.grpc" }
    }]
    ```
    </details>
    
    <sup>
    labels: library-update
    </sup>
    
    <!-- scala-steward = {
      "Update" : {
        "ForGroupId" : {
          "forArtifactIds" : [
            {
              "ForArtifactId" : {
                "crossDependency" : [
                  {
                    "groupId" : "io.grpc",
                    "artifactId" : {
                      "name" : "grpc-netty-shaded",
                      "maybeCrossName" : null
                    },
                    "version" : "1.72.0",
                    "sbtVersion" : null,
                    "scalaVersion" : null,
                    "configurations" : null
                  }
                ],
                "newerVersions" : [
                  "1.73.0"
                ],
                "newerGroupId" : null,
                "newerArtifactId" : null
              }
            },
            {
              "ForArtifactId" : {
                "crossDependency" : [
                  {
                    "groupId" : "io.grpc",
                    "artifactId" : {
                      "name" : "grpc-protobuf",
                      "maybeCrossName" : null
                    },
                    "version" : "1.72.0",
                    "sbtVersion" : null,
                    "scalaVersion" : null,
                    "configurations" : null
                  }
                ],
                "newerVersions" : [
                  "1.73.0"
                ],
                "newerGroupId" : null,
                "newerArtifactId" : null
              }
            },
            {
              "ForArtifactId" : {
                "crossDependency" : [
                  {
                    "groupId" : "io.grpc",
                    "artifactId" : {
                      "name" : "grpc-stub",
                      "maybeCrossName" : null
                    },
                    "version" : "1.72.0",
                    "sbtVersion" : null,
                    "scalaVersion" : null,
                    "configurations" : null
                  },
                  {
                    "groupId" : "io.grpc",
                    "artifactId" : {
                      "name" : "grpc-stub",
                      "maybeCrossName" : null
                    },
                    "version" : "1.72.0",
                    "sbtVersion" : null,
                    "scalaVersion" : null,
                    "configurations" : "test"
                  }
                ],
                "newerVersions" : [
                  "1.73.0"
                ],
                "newerGroupId" : null,
                "newerArtifactId" : null
              }
            }
          ]
        }
      },
      "Labels" : [
        "library-update"
      ]
    } -->
    xerial-bot authored May 29, 2025
    Configuration menu
    Copy the full SHA
    0021b55 View commit details
    Browse the repository at this point in the history
  2. Update duckdb_jdbc to 1.3.0.0 (#3917)

    ## About this PR
    📦 Updates org.duckdb:duckdb_jdbc from `1.2.2.0` to `1.3.0.0`
    
    ## Usage
    ✅ **Please merge!**
    
    I'll automatically update this PR to resolve conflicts as long as you
    don't change it yourself.
    
    If you'd like to skip this version, you can just close this PR. If you
    have any feedback, just mention me in the comments below.
    
    Configure Scala Steward for your repository with a
    [`.scala-steward.conf`](https://github.com/scala-steward-org/scala-steward/blob/f0dfaa6d8e24b261aeafbc7f99c5325ed9365cf2/docs/repo-specific-configuration.md)
    file.
    
    _Have a fantastic day writing Scala!_
    
    <details>
    <summary>⚙ Adjust future updates</summary>
    
    Add this to your `.scala-steward.conf` file to ignore future updates of
    this dependency:
    ```
    updates.ignore = [ { groupId = "org.duckdb", artifactId = "duckdb_jdbc" } ]
    ```
    Or, add this to slow down future updates of this dependency:
    ```
    dependencyOverrides = [{
      pullRequests = { frequency = "30 days" },
      dependency = { groupId = "org.duckdb", artifactId = "duckdb_jdbc" }
    }]
    ```
    </details>
    
    <sup>
    labels: library-update
    </sup>
    
    <!-- scala-steward = {
      "Update" : {
        "ForArtifactId" : {
          "crossDependency" : [
            {
              "groupId" : "org.duckdb",
              "artifactId" : {
                "name" : "duckdb_jdbc",
                "maybeCrossName" : null
              },
              "version" : "1.2.2.0",
              "sbtVersion" : null,
              "scalaVersion" : null,
              "configurations" : null
            }
          ],
          "newerVersions" : [
            "1.3.0.0"
          ],
          "newerGroupId" : null,
          "newerArtifactId" : null
        }
      },
      "Labels" : [
        "library-update"
      ]
    } -->
    xerial-bot authored May 29, 2025
    Configuration menu
    Copy the full SHA
    ab3f07e View commit details
    Browse the repository at this point in the history

Commits on May 31, 2025

  1. feature: Refine copilot-instructions.md and add PR template (#3939)

    **Description**
    This PR refines the copilot-instructions.md for better agentic coding
    and adds a pull_request_template.md.
    **Related Issue/Task**
    N/A
    **Checklist**
    - [x] This pull request focuses on a single task.
    - [x] The change does not contain security credentials
    
    ---------
    
    Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
    xerial and gemini-code-assist[bot] authored May 31, 2025
    Configuration menu
    Copy the full SHA
    e961786 View commit details
    Browse the repository at this point in the history
  2. Configuration menu
    Copy the full SHA
    199f001 View commit details
    Browse the repository at this point in the history
  3. surface: Fix Scala 3 compiler crash with case classes defined in trai…

    …ts (#3941)
    
    This PR fixes a Scala 3.6.3 compiler crash that occurs during the
    erasure phase when using `Surface.of` with case classes defined inside
    traits and accessed through objects that extend those traits.
    
    ## Problem
    
    The following code would crash the Scala 3 compiler with an
    `AssertionError: assertion failed: missing outer accessor`:
    
    ```scala
    import wvlet.airframe.surface.Surface
    
    object MT extends MT
    
    trait MT {
      case class B(min: Int = 0, max: Int = 0)
    }
    
    object Crash {
      val schema = Surface.of[MT.B]  // Compiler crash here
    
      def main(args: Array[String]): Unit = {
        println(schema)
      }
    }
    ```
    
    ## Root Cause
    
    The issue was in `CompileTimeSurfaceFactory.scala` where the code
    attempted to generate default value accessors for case class parameters
    by creating a reference to the companion module using
    `Ref(t.typeSymbol.companionModule)`. For case classes defined inside
    traits, this reference pattern causes problems during the Scala 3
    erasure phase because the compiler cannot properly resolve the outer
    accessor chain.
    
    ## Solution
    
    Added detection for case classes defined in traits and skip default
    value generation in those cases:
    
    ```scala
    // Check if this is a case class in a trait, which can cause companion reference issues
    val isInTrait = t.typeSymbol.owner.flags.is(Flags.Trait)
    if isInTrait then
      // Skip default values for case classes defined in traits to avoid erasure issues
      '{ None }
    else
      // Original logic for companion module reference
      val companion = Ref(t.typeSymbol.companionModule)
      // ... rest of default value generation
    ```
    
    ## Impact
    
    - **Minimal change**: Only 11 lines modified to fix the specific issue
    - **Safe fallback**: When default values cannot be safely generated, the
    code falls back to `None`
    - **Preserves functionality**: All existing tests pass, surface
    generation works correctly
    - **Scala 3 specific**: Only affects Scala 3 compilation, Scala 2
    remains unchanged
    - **Cross-platform**: Fix verified on both JVM and JS platforms
    
    ## Testing
    
    - Added comprehensive test cases covering the crash scenario
    - Verified all existing tests pass (131 JVM + 93 JS tests for Scala 3,
    113 JVM tests for Scala 2.13)
    - Confirmed the original issue code now compiles and works correctly
    
    Fixes #3869.
    
    ---
    
    💡 You can make Copilot smarter by setting up custom instructions,
    customizing its development environment and configuring Model Context
    Protocol (MCP) servers. Learn more [Copilot coding agent
    tips](https://gh.io/copilot-coding-agent-tips) in the docs.
    
    ---------
    
    Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
    Co-authored-by: xerial <57538+xerial@users.noreply.github.com>
    Co-authored-by: Taro L. Saito <leo@xerial.org>
    3 people authored May 31, 2025
    Configuration menu
    Copy the full SHA
    e494651 View commit details
    Browse the repository at this point in the history
  4. feat: Add comprehensive Airframe walkthrough documentation (#3940)

    ## Summary
    
    This PR addresses the need for comprehensive walkthrough documentation
    that shows how Airframe modules work together to build complete
    applications. Previously, the documentation consisted of isolated module
    guides that didn't demonstrate the integrated ecosystem.
    
    ## What's New
    
    **📚 New Comprehensive Walkthrough**: `docs/airframe-walkthrough.md`
    
    A step-by-step tutorial that builds a complete **Task Management
    Application** demonstrating:
    
    - **🔧 Backend Development**: REST APIs with dependency injection,
    configuration, and structured logging
    - **🚀 RPC Communication**: Type-safe client-server communication with
    shared interfaces
    - **💻 Frontend Development**: Reactive Scala.js UI that shares code with
    the server
    - **📟 CLI Development**: Rich command-line interfaces with multiple
    output formats
    - **🧪 Testing Strategies**: Comprehensive testing using AirSpec with DI
    integration
    
    ## Key Features
    
    - **Progressive Learning**: Each step builds upon the previous, showing
    natural module integration
    - **Real-World Example**: Complete application that demonstrates
    practical usage patterns
    - **Working Code**: All examples are syntactically correct and follow
    Airframe conventions
    - **Cross-Platform**: Demonstrates JVM, Scala.js, and testing across the
    entire stack
    
    ## Navigation Updates
    
    - **Homepage Enhancement**: Added prominent link to walkthrough on main
    documentation page
    - **Sidebar Integration**: Included walkthrough in "Resources" section
    for easy discovery
    - **User Guidance**: Clear call-to-action for new users to start with
    the walkthrough
    
    ## Example Structure
    
    The walkthrough progressively builds features:
    
    ```scala
    // Step 1: Foundation with logging
    class TaskApp extends LogSupport {
      info("Starting Task Management Application")
    }
    
    // Step 3: Dependency injection
    val design = newDesign
      .bind[TaskRepository].to[InMemoryTaskRepository]
      .bind[TaskService].toSingleton
    
    // Step 4: REST APIs  
    @rpc
    trait TaskApi {
      @endpoint(method = HttpMethod.GET, path = "/api/tasks")
      def listTasks(): TaskListResponse
    }
    
    // Step 6: Scala.js frontend
    class TaskApp extends LogSupport {
      private val tasks = Rx.variable(Seq.empty[Task])
      def render: RxElement = div(/* reactive UI */)
    }
    ```
    
    ## Impact
    
    This transforms the learning experience from studying isolated modules
    to understanding how Airframe enables rapid development of type-safe,
    cross-platform applications. Users can now see the "big picture" of how
    modules complement each other.
    
    Fixes #2169.
    
    ---
    
    💡 You can make Copilot smarter by setting up custom instructions,
    customizing its development environment and configuring Model Context
    Protocol (MCP) servers. Learn more [Copilot coding agent
    tips](https://gh.io/copilot-coding-agent-tips) in the docs.
    
    ---------
    
    Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
    Co-authored-by: xerial <57538+xerial@users.noreply.github.com>
    Copilot and xerial authored May 31, 2025
    Configuration menu
    Copy the full SHA
    83baf45 View commit details
    Browse the repository at this point in the history
  5. airframe-control: Add RateLimiter with token bucket algorithm (#3942)

    This PR implements a `RateLimiter` for airframe-control using a token
    bucket algorithm to control the rate of operations. This addresses the
    need for rate limiting and retry budget control mentioned in the issue.
    
    ## Features
    
    ### Core Functionality
    - **Token bucket algorithm** with configurable permits per second and
    burst size
    - **Thread-safe implementation** using atomic operations with immutable
    state
    - **Cross-platform compatibility** (JVM, JS, Native) using
    `Compat.sleep`
    - **Blocking and non-blocking operations**: `acquire()` and
    `tryAcquire()` methods
    - **Timeout support**: `tryAcquire(permits, timeout, unit)`
    
    ### Usage Examples
    
    Basic rate limiting:
    ```scala
    import wvlet.airframe.control.RateLimiter
    
    // Allow 10 permits per second
    val limiter = RateLimiter.create(10.0)
    
    // Acquire a permit (blocks if necessary)
    limiter.acquire()
    
    // Try to acquire without blocking
    if (limiter.tryAcquire()) {
      // Permit acquired
    }
    ```
    
    Burst control:
    ```scala
    // Allow 5 permits/sec with burst of up to 20 permits
    val limiter = RateLimiter.create(5.0, 20)
    
    // Can immediately acquire up to 20 permits
    for (_ <- 1 to 20) {
      limiter.tryAcquire() // All succeed immediately
    }
    ```
    
    Per-host rate limiting:
    ```scala
    import scala.collection.concurrent.TrieMap
    
    val hostLimiters = TrieMap.empty[String, RateLimiter]
    
    def getRateLimiter(host: String): RateLimiter = {
      hostLimiters.getOrElseUpdate(host, RateLimiter.create(10.0))
    }
    
    def makeRequest(host: String, request: Request): Response = {
      val limiter = getRateLimiter(host)
      limiter.acquire() // Wait for permit
      sendRequest(host, request)
    }
    ```
    
    ## Implementation Details
    
    ### Key Design Decisions
    - **Separate blocking/non-blocking logic**: `tryReservePermits`
    (non-blocking) doesn't allow token bucket to go negative, while
    `reservePermits` (blocking) can reserve future tokens
    - **Atomic state management**: Uses `AtomicReference[RateLimiterState]`
    with immutable case class for thread safety
    - **Configurable burst size**: Defaults to `permitsPerSecond` but can be
    customized
    - **Cross-platform sleep**: Uses `Compat.sleep` for millisecond-level
    precision across platforms
    
    ### Test Coverage
    - 22 comprehensive tests covering basic functionality, edge cases,
    concurrency, and time-based behavior
    - Manual ticker support for deterministic testing
    - Parameter validation and error handling
    
    ## Documentation
    
    Added comprehensive documentation to `airframe-control.md` with:
    - Basic usage examples
    - Burst control configuration
    - Integration patterns with retry mechanisms
    - Per-host rate limiting implementation
    
    The implementation provides a foundation for maintaining average traffic
    rates and implementing retry budgets as mentioned in the original issue,
    complementing the existing retry and circuit breaker functionality in
    airframe-control.
    
    Fixes #1027.
    
    ---
    
    💡 You can make Copilot smarter by setting up custom instructions,
    customizing its development environment and configuring Model Context
    Protocol (MCP) servers. Learn more [Copilot coding agent
    tips](https://gh.io/copilot-coding-agent-tips) in the docs.
    
    ---------
    
    Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
    Co-authored-by: xerial <57538+xerial@users.noreply.github.com>
    Copilot and xerial authored May 31, 2025
    Configuration menu
    Copy the full SHA
    72e0f1f View commit details
    Browse the repository at this point in the history
  6. Fix literal types interfering with primitive types in Surface caching (

    …#3943)
    
    The Surface reflection-based implementation had a caching issue where
    literal types (e.g., `1`, `true`, `'a'`) would interfere with their
    corresponding primitive types (`Int`, `Boolean`, `Char`). This caused
    subsequent calls to return incorrect surface types.
    
    ## Problem
    When using Scala 2's reflection-based Surface implementation, the
    following sequence would produce incorrect results:
    
    ```scala
    Surface.of[1]           // Returns a surface for literal 1
    Surface.of[Int]         // Returns Object instead of Int
    Surface.of[Seq[Int]]    // Returns Seq[Object] instead of Seq[Int]
    
    // Same issue affected all primitive literal types:
    Surface.of[1.0]         // Literal double
    Surface.of[Double]      // Returns Object instead of Double
    
    Surface.of['a']         // Literal char  
    Surface.of[Char]        // Returns Object instead of Char
    
    Surface.of[true]        // Literal boolean
    Surface.of[Boolean]     // Returns Object instead of Boolean
    ```
    
    ## Root Cause
    The issue was in the `fullTypeNameOf` function in
    `ReflectSurfaceFactory`. Both literal types (e.g., `Int(1)`) and their
    corresponding primitive types (`Int`) have the same
    `typeSymbol.fullName` (`scala.Int`), causing them to share the same
    cache key. When a literal type was cached first, subsequent primitive
    type lookups would return the cached literal surface instead of the
    correct primitive surface.
    
    ## Solution
    Added a case for `ConstantType` in `fullTypeNameOf` to distinguish
    literal types from primitive types by including the constant value in
    the cache key:
    
    ```scala
    case ct: ru.ConstantType =>
      // Distinguish literal types (e.g., Int(1), Boolean(true)) from their primitive types (Int, Boolean)
      s"${ct.typeSymbol.fullName}(${ct.value.value})"
    ```
    
    This ensures literal types like `Int(1)` and primitive types like `Int`
    have different cache keys (`scala.Int(1)` vs `scala.Int`).
    
    ## Testing
    - Added comprehensive tests covering all affected primitive types (Int,
    Double, Char, Boolean)
    - Tests are placed in `scala-2.13` directory since literal types were
    introduced in Scala 2.13
    - Verified the fix works with the exact sequence described in the issue
    - All existing surface tests continue to pass
    
    The fix only affects the Scala 2 reflection-based implementation. Scala
    3's macro-based implementation was not affected by this issue.
    
    Fixes #3813.
    
    ---
    
    💡 You can make Copilot smarter by setting up custom instructions,
    customizing its development environment and configuring Model Context
    Protocol (MCP) servers. Learn more [Copilot coding agent
    tips](https://gh.io/copilot-coding-agent-tips) in the docs.
    
    ---------
    
    Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
    Co-authored-by: xerial <57538+xerial@users.noreply.github.com>
    Copilot and xerial authored May 31, 2025
    Configuration menu
    Copy the full SHA
    d6b2890 View commit details
    Browse the repository at this point in the history

Commits on Jun 2, 2025

  1. Update sbt, sbt-dependency-tree, ... to 1.11.1 (#3948)

    ## About this PR
    📦 Updates 
    * [org.scala-sbt:sbt](https://github.com/sbt/sbt)
    * [org.scala-sbt:sbt-dependency-tree](https://github.com/sbt/sbt)
    * [org.scala-sbt:scripted-plugin](https://github.com/sbt/sbt)
    
     from `1.11.0` to `1.11.1`
    
    ## Usage
    ✅ **Please merge!**
    
    I'll automatically update this PR to resolve conflicts as long as you
    don't change it yourself.
    
    If you'd like to skip this version, you can just close this PR. If you
    have any feedback, just mention me in the comments below.
    
    Configure Scala Steward for your repository with a
    [`.scala-steward.conf`](https://github.com/scala-steward-org/scala-steward/blob/f0dfaa6d8e24b261aeafbc7f99c5325ed9365cf2/docs/repo-specific-configuration.md)
    file.
    
    _Have a fantastic day writing Scala!_
    
    <details>
    <summary>🔍 Files still referring to the old version number</summary>
    
    The following files still refer to the old version number (1.11.0).
    You might want to review and update them manually.
    ```
    docs/release-notes.md
    ```
    </details>
    <details>
    <summary>⚙ Adjust future updates</summary>
    
    Add this to your `.scala-steward.conf` file to ignore future updates of
    this dependency:
    ```
    updates.ignore = [ { groupId = "org.scala-sbt" } ]
    ```
    Or, add this to slow down future updates of this dependency:
    ```
    dependencyOverrides = [{
      pullRequests = { frequency = "30 days" },
      dependency = { groupId = "org.scala-sbt" }
    }]
    ```
    </details>
    
    <sup>
    labels: library-update
    </sup>
    
    <!-- scala-steward = {
      "Update" : {
        "ForGroupId" : {
          "forArtifactIds" : [
            {
              "ForArtifactId" : {
                "crossDependency" : [
                  {
                    "groupId" : "org.scala-sbt",
                    "artifactId" : {
                      "name" : "sbt",
                      "maybeCrossName" : null
                    },
                    "version" : "1.11.0",
                    "sbtVersion" : null,
                    "scalaVersion" : null,
                    "configurations" : null
                  }
                ],
                "newerVersions" : [
                  "1.11.1"
                ],
                "newerGroupId" : null,
                "newerArtifactId" : null
              }
            },
            {
              "ForArtifactId" : {
                "crossDependency" : [
                  {
                    "groupId" : "org.scala-sbt",
                    "artifactId" : {
                      "name" : "sbt-dependency-tree",
                      "maybeCrossName" : null
                    },
                    "version" : "1.11.0",
                    "sbtVersion" : "1.0",
                    "scalaVersion" : "2.12",
                    "configurations" : null
                  }
                ],
                "newerVersions" : [
                  "1.11.1"
                ],
                "newerGroupId" : null,
                "newerArtifactId" : null
              }
            },
            {
              "ForArtifactId" : {
                "crossDependency" : [
                  {
                    "groupId" : "org.scala-sbt",
                    "artifactId" : {
                      "name" : "scripted-plugin",
                      "maybeCrossName" : "scripted-plugin_2.12"
                    },
                    "version" : "1.11.0",
                    "sbtVersion" : null,
                    "scalaVersion" : null,
                    "configurations" : null
                  }
                ],
                "newerVersions" : [
                  "1.11.1"
                ],
                "newerGroupId" : null,
                "newerArtifactId" : null
              }
            }
          ]
        }
      },
      "Labels" : [
        "library-update"
      ]
    } -->
    xerial-bot authored Jun 2, 2025
    Configuration menu
    Copy the full SHA
    4313df0 View commit details
    Browse the repository at this point in the history
  2. feature: Add Rx.delay(..) operation to insert artificial delay (#3945)

    This PR adds a new `delay` operation to the `RxOps` trait that inserts
    an artificial delay before emitting each element from the source stream.
    This is similar to the `tap` operator but adds a configurable delay.
    
    ## Changes
    
    - **Added `DelayOp[A]` case class**: A new operator that extends
    `UnaryRx[A, A]` for delaying stream elements
    - **Added `delay` method to `RxOps` trait**: Allows chaining
    `.delay(interval, unit)` on any Rx stream
    - **Implemented `DelayOp` handling in `RxRunner`**: Uses
    `compat.scheduleOnce` to asynchronously delay each element
    - **Added comprehensive tests**: Demonstrates timing behavior and value
    preservation using AirSpec async testing
    - **Updated documentation**: Clarified the difference between the
    existing timer-based `Rx.delay` and the new stream-based delay operation
    
    ## Usage
    
    ```scala
    import wvlet.airframe.rx.Rx
    import java.util.concurrent.TimeUnit
    
    // Delay each element by 100ms before emission
    val rx = Rx.sequence(1, 2, 3)
      .delay(100, TimeUnit.MILLISECONDS)
      .tap(x => println(s"Received: $x"))
    
    val c = rx.run()
    // Elements will be emitted with 100ms delay between each
    ```
    
    ## Key Features
    
    - **Preserves original values**: Like `tap`, the delay operation passes
    through values unchanged
    - **Configurable timing**: Supports any `TimeUnit` for flexible delay
    configuration
    - **Cross-platform support**: Works on JVM, JS, and Native platforms
    - **Async implementation**: Uses platform-specific scheduling for
    non-blocking delays
    - **Composable**: Can be chained with other Rx operators
    
    ## Distinction from existing `Rx.delay`
    
    The existing `Rx.delay(interval, unit)` creates a timer stream that
    emits `0` once after a delay period. This new operation delays elements
    in an existing stream, making it more useful for throttling or
    artificial latency simulation.
    
    Fixes #3871.
    
    ---
    
    💡 You can make Copilot smarter by setting up custom instructions,
    customizing its development environment and configuring Model Context
    Protocol (MCP) servers. Learn more [Copilot coding agent
    tips](https://gh.io/copilot-coding-agent-tips) in the docs.
    
    ---------
    
    Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
    Co-authored-by: xerial <57538+xerial@users.noreply.github.com>
    Copilot and xerial authored Jun 2, 2025
    Configuration menu
    Copy the full SHA
    61f0d32 View commit details
    Browse the repository at this point in the history
  3. Update postgresql to 42.7.6 (#3928)

    ## About this PR
    📦 Updates [org.postgresql:postgresql](https://github.com/pgjdbc/pgjdbc)
    from `42.7.5` to `42.7.6`
    
    📜 [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md)
    
    ## Usage
    ✅ **Please merge!**
    
    I'll automatically update this PR to resolve conflicts as long as you
    don't change it yourself.
    
    If you'd like to skip this version, you can just close this PR. If you
    have any feedback, just mention me in the comments below.
    
    Configure Scala Steward for your repository with a
    [`.scala-steward.conf`](https://github.com/scala-steward-org/scala-steward/blob/f0dfaa6d8e24b261aeafbc7f99c5325ed9365cf2/docs/repo-specific-configuration.md)
    file.
    
    _Have a fantastic day writing Scala!_
    
    <details>
    <summary>⚙ Adjust future updates</summary>
    
    Add this to your `.scala-steward.conf` file to ignore future updates of
    this dependency:
    ```
    updates.ignore = [ { groupId = "org.postgresql", artifactId = "postgresql" } ]
    ```
    Or, add this to slow down future updates of this dependency:
    ```
    dependencyOverrides = [{
      pullRequests = { frequency = "30 days" },
      dependency = { groupId = "org.postgresql", artifactId = "postgresql" }
    }]
    ```
    </details>
    
    <sup>
    labels: library-update
    </sup>
    
    <!-- scala-steward = {
      "Update" : {
        "ForArtifactId" : {
          "crossDependency" : [
            {
              "groupId" : "org.postgresql",
              "artifactId" : {
                "name" : "postgresql",
                "maybeCrossName" : null
              },
              "version" : "42.7.5",
              "sbtVersion" : null,
              "scalaVersion" : null,
              "configurations" : null
            }
          ],
          "newerVersions" : [
            "42.7.6"
          ],
          "newerGroupId" : null,
          "newerArtifactId" : null
        }
      },
      "Labels" : [
        "library-update"
      ]
    } -->
    xerial-bot authored Jun 2, 2025
    Configuration menu
    Copy the full SHA
    9009bc0 View commit details
    Browse the repository at this point in the history
  4. fix: Ensure onMount called only after element is available by ID (#3947)

    ## Problem
    
    When RxElements with ID attributes are rendered in nested sequences, the
    `onMount` callback may be called before the DOM element is actually
    available via `document.getElementById()`. This causes issues when
    `onMount` code tries to find elements by their ID.
    
    Example that demonstrates the issue:
    
    ```scala
    class HoverableTextLabel(txt: RxElement, hoverMessage: String) extends RxElement:
      private val elementId = ULID.newULIDString
    
      override def onMount: Unit =
        RxDOM.getHTMLElementById(elementId).foreach { el =>
          // This may fail because element is not yet available by ID
          try Dynamic.newInstance(Dynamic.global.bootstrap.Tooltip)(el)
          catch case e: Throwable => warn(e)
        }
    
      override def render: RxElement = span(
        id -> elementId,
        data("bs-toggle") -> "tooltip",
        txt
      )
    
    div(
      Seq[RxElement](
        HoverableTextLabel("hello", "mouseover message")
      )
    )
    ```
    
    ## Root Cause
    
    The previous implementation used a `MutationObserver` to detect when
    elements were added to the DOM and then immediately called `onMount`.
    However, there was a timing race condition where the element was added
    to the DOM but not immediately available via `document.getElementById()`
    due to browser batching of DOM updates.
    
    ## Solution
    
    Modified `DOMRenderer` to ensure elements with IDs are fully available
    before calling `onMount`:
    
    1. **Mount element to DOM first** - The element is added to the DOM tree
    2. **Check for ID attribute** - Determine if the element has an ID that
    needs to be queryable
    3. **Verify availability** - For elements with IDs, verify they're
    available via `document.getElementById()`
    4. **Retry if needed** - Use `setTimeout` with 0ms delay to retry if
    element not immediately available
    5. **Preserve existing behavior** - For elements without IDs, call
    `onMount` immediately as before
    
    ## Testing
    
    Added a comprehensive test case that reproduces the original issue and
    verifies the fix works:
    
    ```scala
    test("onMount should find element by ID in nested sequences") {
      class HoverableTextLabel(txt: RxElement, hoverMessage: String) extends RxElement {
        private val elementId = s"element-${System.nanoTime()}"
    
        override def onMount(n: Any): Unit = {
          RxDOM.getHTMLElementById(elementId) match {
            case Some(el) => foundElement := true  // Should succeed
            case None => // Should not happen with fix
          }
        }
        // ... render implementation
      }
      // Test passes with fix, was failing before
    }
    ```
    
    All existing `onMount` tests continue to pass, ensuring backward
    compatibility.
    
    Fixes #3453.
    
    ---
    
    💡 You can make Copilot smarter by setting up custom instructions,
    customizing its development environment and configuring Model Context
    Protocol (MCP) servers. Learn more [Copilot coding agent
    tips](https://gh.io/copilot-coding-agent-tips) in the docs.
    
    ---------
    
    Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
    Co-authored-by: xerial <57538+xerial@users.noreply.github.com>
    Copilot and xerial authored Jun 2, 2025
    Configuration menu
    Copy the full SHA
    13b132d View commit details
    Browse the repository at this point in the history
  5. docs: Update copilot-instructions.md for clearer commit formatting gu…

    …idelines (#3951)
    
    **Description**
    
    **Related Issue/Task**
    
    **Checklist**
    
    - [x] This pull request focuses on a single task.
    - [x] The change does not contain security credentials
    xerial authored Jun 2, 2025
    Configuration menu
    Copy the full SHA
    f23ea29 View commit details
    Browse the repository at this point in the history
  6. Fix LazyCodec recursive reference resolution to prevent infinite recu…

    …rsion (#3946)
    
    ## Problem
    
    The codec system failed to unpack recursive types like:
    
    ```scala
    case class FileEntry(
        name: String,
        path: String,
        isDirectory: Boolean,
        size: Long,
        lastUpdatedAtMillis: Long,
        content: Option[String] = None,
        children: List[FileEntry] = List.empty
    )
    ```
    
    ## Root Cause
    
    When `LazyCodec.ref` was accessed during unpacking, it called
    `codecFactory.ofSurface(surface)` with default parameters (`seen =
    Set.empty`), which could potentially cause infinite recursion if the
    cache lookup failed for any reason.
    
    The original implementation:
    ```scala
    private lazy val ref: MessageCodec[A] = codecFactory.ofSurface(surface).asInstanceOf[MessageCodec[A]]
    ```
    
    ## Solution
    
    Modified `LazyCodec.ref` initialization to call
    `codecFactory.ofSurface(surface, Set(surface))`, ensuring recursion
    protection is maintained even if cache lookup fails:
    
    ```scala
    private lazy val ref: MessageCodec[A] = {
      // When resolving the lazy reference, pass the current surface in the seen set
      // to prevent infinite recursion if the cache lookup fails for any reason
      codecFactory.ofSurface(surface, Set(surface)).asInstanceOf[MessageCodec[A]]
    }
    ```
    
    ## Testing
    
    Added comprehensive test suite covering:
    - Basic recursive types serialization/deserialization
    - Deeply nested recursive structures  
    - JSON serialization/deserialization
    - Circular reference handling
    - Concurrent access patterns
    - Edge cases with cache scenarios
    
    All existing codec tests continue to pass, ensuring backward
    compatibility.
    
    Fixes #3688.
    
    ---
    
    💡 You can make Copilot smarter by setting up custom instructions,
    customizing its development environment and configuring Model Context
    Protocol (MCP) servers. Learn more [Copilot coding agent
    tips](https://gh.io/copilot-coding-agent-tips) in the docs.
    
    ---------
    
    Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
    Co-authored-by: xerial <57538+xerial@users.noreply.github.com>
    Copilot and xerial authored Jun 2, 2025
    Configuration menu
    Copy the full SHA
    57d6659 View commit details
    Browse the repository at this point in the history
  7. Add IO.copy(InputStream, OutputStream) method to airframe-control (#3949

    )
    
    This PR adds a new `IO.copy(InputStream, OutputStream)` method to the
    airframe-control module for copying data between streams on JVM and
    Native platforms.
    
    ## Changes
    
    - Added `IO.copy(in: InputStream, out: OutputStream): Unit` method to
    the shared IO object
    - Method uses 8192-byte buffer (consistent with existing `readFully`
    method)
    - Properly handles null inputs without throwing exceptions
    - Uses `withResource` for automatic resource management of InputStream
    - Calls `flush()` on OutputStream to ensure buffered data is written
    - Added comprehensive tests covering normal operation, edge cases, and
    large data transfers
    
    ## Usage
    
    ```scala
    import wvlet.airframe.control.IO
    import java.io.{FileInputStream, FileOutputStream}
    
    val input = new FileInputStream("source.txt")
    val output = new FileOutputStream("destination.txt")
    IO.copy(input, output)
    ```
    
    ## Platform Support
    
    - ✅ **JVM**: Fully supported
    - ✅ **Native**: Fully supported  
    - ⚠️ **JS**: Compiles but InputStream/OutputStream are not practically
    usable on JavaScript
    
    The method is available on all platforms that support Java IO classes
    (JVM and Native), while JS compilation succeeds but the classes
    themselves are not meaningful in a JavaScript context.
    
    ## Testing
    
    Added comprehensive test suite (`IOTest`) that verifies:
    - Basic copy functionality with various data sizes
    - Null input/output stream handling
    - Empty stream handling
    - Large data transfers (>8KB to test buffer logic)
    
    All tests pass on JVM and Native platforms.
    
    Fixes #396.
    
    ---
    
    💡 You can make Copilot smarter by setting up custom instructions,
    customizing its development environment and configuring Model Context
    Protocol (MCP) servers. Learn more [Copilot coding agent
    tips](https://gh.io/copilot-coding-agent-tips) in the docs.
    
    ---------
    
    Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
    Co-authored-by: xerial <57538+xerial@users.noreply.github.com>
    Copilot and xerial authored Jun 2, 2025
    Configuration menu
    Copy the full SHA
    ef5c66b View commit details
    Browse the repository at this point in the history
  8. http: Fix flaky tests using httpbin.org by adding local Netty server …

    …tests (#3944)
    
    This PR addresses the flaky test failures caused by external service
    dependencies (httpbin.org and jsonplaceholder.typicode.com) by
    implementing a two-pronged solution:
    
    ## 1. Integration Tests with Local Netty Server
    
    Added comprehensive HTTP client tests in the `integration-test` module
    that use a local Netty server instead of external services:
    
    - **MockServer API**: Created a complete mock server that simulates
    httpbin.org endpoints (`/get`, `/post`, `/put`, `/delete`,
    `/status/:code`, `/user-agent`, etc.)
    - **New Test Files**:
    - `URLConnectionClientTest` - Tests URLConnection backend with local
    server
      - `RPCHttpClientTest` - Tests RPC client functionality 
      - `JavaSyncClientTest` - Tests Java HTTP client sync operations
      - `JavaAsyncClientTest` - Tests Java HTTP client async operations
    
    These tests provide reliable coverage without external dependencies and
    follow @xerial's suggestion to use Netty backend in integration tests.
    
    ## 2. Resilient Original Tests
    
    Updated the original test files to gracefully handle external service
    unavailability:
    
    - Added service availability checks with short timeouts (5 seconds)
    - Tests now skip with informative messages when external services are
    down instead of failing
    - Removed `flaky` wrappers in favor of availability-based skipping
    - All 135 JVM HTTP tests now pass (8 pending when external services
    unavailable)
    
    ## Before/After Comparison
    
    **Before**: Tests would fail with connection errors when httpbin.org was
    down:
    ```
    wvlet.airframe.http.HttpClientException: java.net.UnknownHostException: httpbin.org
    ```
    
    **After**: Tests either pass using local server or skip gracefully:
    ```
    pending: External service https://httpbin.org/ is not available. Use integration tests with local Netty server instead.
    ```
    
    ## Test Results
    
    - ✅ 46 integration tests passing (using local Netty server)
    - ✅ 135 JVM HTTP tests passing (8 pending when external services down)
    - ✅ No regression in existing functionality
    - ✅ Builds are no longer blocked by external service outages
    
    Fixes #3097.
    
    > [!WARNING]
    >
    > <details>
    > <summary>Firewall rules blocked me from connecting to one or more
    addresses</summary>
    >
    > #### I tried to connect to the following addresses, but was blocked by
    firewall rules:
    >
    > - `httpbin.org`
    > - Triggering command: `java -Xms512m -Xss2m -XX:MaxInlineLevel=18 -jar
    /home/REDACTED/.sbt/launchers/1.11.0/sbt-launch.jar httpJVM/testOnly
    *URLConnectionClientTest ` (dns block)
    > - Triggering command: `java -Xms512m -Xss2m -XX:MaxInlineLevel=18 -jar
    /home/REDACTED/.sbt/launchers/1.11.0/sbt-launch.jar httpJVM/testOnly
    *RPCHttpClientTest ` (dns block)
    > - Triggering command: `java -Xms512m -Xss2m -XX:MaxInlineLevel=18 -jar
    /home/REDACTED/.sbt/launchers/1.11.0/sbt-launch.jar httpJVM/testOnly
    *JavaSyncClientTest ` (dns block)
    > - `playwright-akamai.azureedge.net`
    > - Triggering command: `/tmp/playwright-java-16590106247988965818/node
    /tmp/playwright-java-16590106247988965818/package/lib/server/registry/oopDownloadBrowserMain.js
    ` (dns block)
    > - `playwright-verizon.azureedge.net`
    > - Triggering command: `/tmp/playwright-java-16590106247988965818/node
    /tmp/playwright-java-16590106247988965818/package/lib/server/registry/oopDownloadBrowserMain.js
    ` (dns block)
    > - `playwright.azureedge.net`
    > - Triggering command: `/tmp/playwright-java-16590106247988965818/node
    /tmp/playwright-java-16590106247988965818/package/lib/server/registry/oopDownloadBrowserMain.js
    ` (dns block)
    >
    > If you need me to access, download, or install something from one of
    these locations, you can either:
    >
    > - Configure [Actions setup
    steps](https://gh.io/copilot/actions-setup-steps) to set up my
    environment, which run before the firewall is enabled
    > - Add the appropriate URLs or hosts to my [firewall allow
    list](https://gh.io/copilot/firewall-config)
    >
    > </details>
    
    
    ---
    
    💡 You can make Copilot smarter by setting up custom instructions,
    customizing its development environment and configuring Model Context
    Protocol (MCP) servers. Learn more [Copilot coding agent
    tips](https://gh.io/copilot-coding-agent-tips) in the docs.
    
    ---------
    
    Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
    Co-authored-by: xerial <57538+xerial@users.noreply.github.com>
    Copilot and xerial authored Jun 2, 2025
    Configuration menu
    Copy the full SHA
    265b537 View commit details
    Browse the repository at this point in the history
Loading