Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/cli.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.25
go-version: 1.26

- name: Test
run: go test -v ./...
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pallas.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.25
go-version: 1.26

- name: Install pallas
run: go install github.com/vanilla-os/pallas/cmd/pallas@a07d6a955f40871655158d66178f90e69bd417a2
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/test-build-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.25
go-version: 1.26

- name: Run Unit Tests
run: go test -v ./...
Expand Down Expand Up @@ -163,7 +163,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.25
go-version: 1.26

- name: Build release assets
run: |
Expand Down Expand Up @@ -228,7 +228,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.25"
go-version: "1.26"
- uses: golangci/golangci-lint-action@v7
with:
version: v2.12.2
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
WebSocket as an automatic compatibility fallback.
- Keep SSC components, actions, hydration, sessions, replay and broadcasts
transport-independent.
- Keep StreamBus outbound failures from consuming SSC sequence numbers before
the connection can accept a frame.

All notable changes to this project are documented in this file.

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pull request.

## Development setup

You need Go **1.25** or later (see `go.mod`).
You need Go **1.26** or later (see `go.mod`).

Clone the repository and verify everything builds for both targets:

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ rfw 2.1 includes:

## Getting Started

rfw requires Go 1.25 or newer. Coming from Node and never installed Go? See
rfw requires Go 1.26 or newer. Coming from Node and never installed Go? See
[Getting started from Node](./docs/articles/guide/getting-started-from-node.md).

```bash
Expand Down
2 changes: 1 addition & 1 deletion bench/todomvc/rfw/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/rfwlab/bench/todomvc

go 1.25.0
go 1.26.0

require github.com/rfwlab/rfw/v2 v2.0.0-beta.19

Expand Down
2 changes: 1 addition & 1 deletion cmd/rfw/initproj/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
"strings"
)

const scaffoldGoVersion = "1.25.0"
const scaffoldGoVersion = "1.26.0"

// InitProject creates a new rfw project from the embedded template.
func InitProject(projectName string, skipTidy bool) (err error) {
Expand Down
10 changes: 5 additions & 5 deletions docs/articles/guide/getting-started-from-node.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ You know npm, `package.json`, and `npm run dev`. You have never installed Go.
This guide gets you from zero to a running rfw app without assuming any Go
background.

rfw requires **Go 1.25 or newer**.
rfw requires **Go 1.26 or newer**.

## 1. Install Go

Expand All @@ -13,9 +13,9 @@ rfw requires **Go 1.25 or newer**.
Most distro packages lag behind. Prefer the official tarball:

```bash
curl -LO https://go.dev/dl/go1.25.0.linux-amd64.tar.gz
curl -LO https://go.dev/dl/go1.26.0.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.25.0.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.26.0.linux-amd64.tar.gz
```

Then add Go to your PATH (in `~/.bashrc` or `~/.zshrc`):
Expand All @@ -24,7 +24,7 @@ Then add Go to your PATH (in `~/.bashrc` or `~/.zshrc`):
export PATH=$PATH:/usr/local/go/bin
```

If you prefer your package manager, check the version first: you need 1.25+.
If you prefer your package manager, check the version first: you need 1.26+.
On Arch `pacman -S go` is current; on Debian/Ubuntu the `golang` package is
often too old, use the tarball instead.

Expand All @@ -50,7 +50,7 @@ winget install GoLang.Go

```bash
go version
# go version go1.25.0 linux/amd64
# go version go1.26.0 linux/amd64
```

## 2. The PATH gotcha
Expand Down
2 changes: 1 addition & 1 deletion docs/articles/guide/realtime-dashboard-tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ metrics, a component that renders them, a simulated data feed driven by a
goroutine and a `time.Ticker`, a list rendered with `@for`, and a pause button
wired with `@on:click`. No JavaScript is written at any point.

Prerequisites: Go 1.25+ and the rfw CLI. If you have neither, start with
Prerequisites: Go 1.26+ and the rfw CLI. If you have neither, start with
[Getting started from Node](getting-started-from-node.md).

## 1. Scaffold the project
Expand Down
3 changes: 2 additions & 1 deletion docs/articles/guide/transports.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ Accepted values are:
StreamBus runs on WebTransport over HTTP/3 at `/streambus`. RFW preserves its
existing JSON SSC protocol and length-prefixes messages on a reliable QUIC
stream. On the server, Warp StreamBus provides bounded queues, priorities,
replay storage and explicit backpressure before frames reach the network.
and explicit backpressure before frames reach the network. SSC keeps its own
sequenced replay history above the transport.

The WebSocket endpoint remains mounted as a compatibility fallback. No
application code or component API changes when the selected transport changes.
Expand Down
2 changes: 1 addition & 1 deletion examples/counter/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/rfwlab/examples/counter

go 1.25.0
go 1.26.0

require github.com/rfwlab/rfw/v2 v2.0.0-beta.19

Expand Down
2 changes: 1 addition & 1 deletion examples/dashboard/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/rfwlab/examples/dashboard

go 1.25.0
go 1.26.0

require github.com/rfwlab/rfw/v2 v2.0.0-beta.19

Expand Down
2 changes: 1 addition & 1 deletion examples/dynamic-list/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/rfwlab/examples/dynamic-list

go 1.25.0
go 1.26.0

require github.com/rfwlab/rfw/v2 v2.0.0-beta.19

Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/rfwlab/rfw/v2

go 1.25.0
go 1.26.0

require (
github.com/andybalholm/brotli v1.2.1
Expand All @@ -26,7 +26,7 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/quic-go/qpack v0.6.0 // indirect
github.com/tdewolff/parse/v2 v2.8.3 // indirect
golang.org/x/crypto v0.55.0 // indirect
golang.org/x/crypto v0.56.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/text v0.41.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZ
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y=
golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I=
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
90 changes: 90 additions & 0 deletions host/streambus_delivery_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package host

import (
"context"
"errors"
"testing"
"time"

"github.com/mirkobrombin/go-warp/v2/streambus"
)

func TestStreamBusInvalidPayloadDoesNotConsumeSequence(t *testing.T) {
connection := &streamBusConnection{}
session := newSession("stream-invalid", sessionOptions{replayLimit: 4})
session.streamConnection = connection

sendStreamBusSession(connection, session, Outbound{Payload: make(chan int)})

if session.outboundSeq != 0 {
t.Fatalf("sequence = %d, want 0", session.outboundSeq)
}
if len(session.replay) != 0 {
t.Fatalf("replay = %#v, want empty", session.replay)
}
}

func TestStreamBusBackpressureClosesBeforeSequence(t *testing.T) {
bus := streambus.NewInMemory(streambus.Config{DefaultBuffer: 1, MaxBuffer: 1})
t.Cleanup(func() { _ = bus.Close() })
subscription, err := bus.Subscribe(context.Background(), streambus.SubscribeOptions{
Topic: "blocked", Buffer: 1, Overflow: streambus.Block,
})
if err != nil {
t.Fatalf("subscribe: %v", err)
}
connection := &streamBusConnection{bus: bus, topic: "blocked", subscription: subscription, buffer: 1}
session := newSession("stream-full", sessionOptions{replayLimit: 4})
session.streamConnection = connection

if _, err := bus.Publish(context.Background(), streambus.Frame{Topic: "blocked", Payload: []byte("one")}); err != nil {
t.Fatalf("publish first: %v", err)
}
waitForStreamBus(t, func() bool { return subscription.Stats().Queued == 0 })
if _, err := bus.Publish(context.Background(), streambus.Frame{Topic: "blocked", Payload: []byte("two")}); err != nil {
t.Fatalf("publish second: %v", err)
}
waitForStreamBus(t, func() bool { return subscription.Stats().Queued == 1 })

sendStreamBusSession(connection, session, Outbound{Payload: "late"})

if session.outboundSeq != 0 {
t.Fatalf("sequence = %d, want 0", session.outboundSeq)
}
select {
case <-subscription.Done():
default:
t.Fatal("subscription stayed open")
}
}

func TestStreamBusEndpointDisablesTransportReplay(t *testing.T) {
endpoint := newStreamBusEndpoint(NewWSRuntime())
t.Cleanup(func() { _ = endpoint.bus.Close() })
topic := "rfw/connection/replay"
sequence, err := endpoint.bus.Publish(context.Background(), streambus.Frame{Topic: topic, Payload: []byte("{}")})
if err != nil {
t.Fatalf("publish: %v", err)
}
subscription, err := endpoint.bus.Subscribe(context.Background(), streambus.SubscribeOptions{
Topic: topic, Buffer: 1, Since: sequence,
})
if subscription != nil {
_ = subscription.Close()
}
if !errors.Is(err, streambus.ErrReplayUnavailable) {
t.Fatalf("subscribe since = %v, want %v", err, streambus.ErrReplayUnavailable)
}
}

func waitForStreamBus(t *testing.T, f func() bool) {
t.Helper()
deadline := time.Now().Add(time.Second)
for time.Now().Before(deadline) {
if f() {
return
}
time.Sleep(time.Millisecond)
}
t.Fatal("condition was not reached")
}
Loading
Loading