### Create Bootstrap Script and Get River
Source: https://github.com/river-now/river/blob/main/internal/site/backend/__static/markdown/start.md
Creates a directory for the bootstrap script and a main Go file within it. It also fetches the River framework using go get, making it available for use.
```sh
mkdir __bootstrap
touch __bootstrap/main.go
go get github.com/river-now/river
```
--------------------------------
### River Bootstrap Initialization
Source: https://github.com/river-now/river/blob/main/internal/site/backend/__static/markdown/start.md
Initializes the River project with specified options. This Go code configures the project's base import path, UI variant, JavaScript package manager, and deployment target.
```go
package main
import "github.com/river-now/river/bootstrap"
func main() {
bootstrap.Init(bootstrap.Options{
GoImportBase: "app", // e.g., "appname" or "modroot/apps/appname"
UIVariant: "react", // "react", "solid", or "preact"
JSPackageManager: "npm", // "npm", "pnpm", "yarn", or "bun"
DeploymentTarget: "generic", // "generic" or "vercel" (defaults to "generic")
})
}
```
--------------------------------
### Run Bootstrap Script
Source: https://github.com/river-now/river/blob/main/internal/site/backend/__static/markdown/start.md
Executes the Go bootstrap script to create and set up a new River project. This command builds and runs the `main.go` file located in the `__bootstrap` directory.
```sh
go run ./__bootstrap/main.go
```
--------------------------------
### Initialize Go Module
Source: https://github.com/river-now/river/blob/main/internal/site/backend/__static/markdown/start.md
Initializes a new Go module in your project directory. This is a fundamental step for managing dependencies in Go projects.
```go
go mod init app
```
--------------------------------
### Setup Global Loading Indicator
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/frontend_entry_tsx_preact_str.txt
Configures a global loading indicator using nprogress. It hooks up the start, done, and isStarted functions from nprogress to manage the loading state visually.
```TypeScript
import { done, isStarted, start } from "nprogress";
import { setupGlobalLoadingIndicator } from "river.now/client";
setupGlobalLoadingIndicator({ start, stop: done, isRunning: isStarted });
```
--------------------------------
### Create and Run a New River Project
Source: https://github.com/river-now/river/blob/main/internal/site/backend/__static/markdown/docs.md
Commands to create a new River project using npm and start the development server.
```bash
npm create river@latest
npm run dev
```
--------------------------------
### Initialize River Client and Render App
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/frontend_entry_tsx_preact_str.txt
Initializes the River Now client and renders the main application component into the root element. This is a core setup step for the client-side application.
```TypeScript
import { render } from "preact";
import { getRootEl, initClient } from "river.now/client";
import { App } from "./app.tsx";
await initClient(() => {
render(, getRootEl());
});
```
--------------------------------
### Create River Project
Source: https://github.com/river-now/river/blob/main/README.md
Command to create a new River project using npm. This command initiates the project setup process.
```Shell
npm create river@latest
```
--------------------------------
### Example: About Page Loader
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/backend_router_loaders_go_tmpl.txt
Registers a loader for the "/about" path, returning a string describing the about page.
```go
var _ = NewLoader("/about", func(c *LoaderCtx) (string, error) {
return "This is the about page.", nil
})
```
--------------------------------
### Main Server Entry Point
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/cmd_app_main_go_tmpl.txt
The main function initializes and starts the backend server. It relies on the `Serve` function from the `server` package to handle server operations.
```go
package main
import "{{.GoImportBase}}/backend/server"
func main() {
server.Serve()
}
```
--------------------------------
### Example: About Hobbies Loader
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/backend_router_loaders_go_tmpl.txt
Registers a loader for the "/about/hobbies" path, returning a list of hobbies.
```go
var _ = NewLoader("/about/hobbies", func(c *LoaderCtx) (string, error) {
return "Hobbies: Gardening, Cooking, Basketball", nil
})
```
--------------------------------
### Main Application Setup and Routing
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/cmd_build_main_go_tmpl.txt
This Go code snippet demonstrates the main function for setting up the River application. It initializes TypeScript generation, defines constants, and configures the application's build process with routing and loaders.
```go
package main
import (
"{{.GoImportBase}}/app"
"{{.GoImportBase}}/backend/router"
"github.com/river-now/river"
"github.com/river-now/river/kit/tsgen"
)
func main() {
a := tsgen.Statements{}
a.Serialize("export const USERNAME_MAX_LEN", router.UsernameMaxLen)
app.Wave.Builder(func(isDev bool) error {
return app.River.Build(&river.BuildOptions{
IsDev: isDev,
LoadersRouter: router.LoadersRouter,
ActionsRouter: router.ActionsRouter,
AdHocTypes: []*river.AdHocType{},
ExtraTSCode: a.BuildString(),
})
})
}
```
--------------------------------
### Remove Bootstrap Directory
Source: https://github.com/river-now/river/blob/main/internal/site/backend/__static/markdown/start.md
Removes the `__bootstrap` directory after the project has been successfully created. This is a cleanup step to remove temporary build files.
```sh
rm -rf ./__bootstrap
```
--------------------------------
### Global Loading Indicator Configuration
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/frontend_entry_tsx_react_str.txt
Configures the global loading indicator using the nprogress library functions. It links the start, done, and isStarted states to the River.now client's loading indicator.
```typescript
setupGlobalLoadingIndicator({ start, stop: done, isRunning: isStarted });
```
--------------------------------
### River.now Client Initialization and React App Rendering
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/frontend_entry_tsx_react_str.txt
Initializes the River.now client and renders the main React application component within the RiverProvider. This is the core setup for a River.now application.
```typescript
import { done, isStarted, start } from "nprogress";
import { createRoot } from "react-dom/client";
import {
getRootEl,
initClient,
revalidateOnWindowFocus,
setupGlobalLoadingIndicator,
} from "river.now/client";
import { RiverProvider } from "river.now/react";
import { App } from "./app.tsx";
await initClient(() => {
createRoot(getRootEl()).render(
,
);
});
```
--------------------------------
### Example: Root Loader
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/backend_router_loaders_go_tmpl.txt
Demonstrates registering a loader for the root path ("/"). This loader returns a `RootData` struct containing a message, count, and username.
```go
type RootData struct {
Message string
Count int
Username string
}
var _ = NewLoader("/", func(c *LoaderCtx) (*RootData, error) {
return &RootData{
Message: "Hello from a River loader!",
Count: count,
Username: username,
}, nil
})
```
--------------------------------
### Initialize River Now Client and Render App
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/frontend_entry_tsx_solid_str.txt
Initializes the River Now client and renders the main application component when the client is ready. It uses SolidJS for rendering.
```TypeScript
import { done, isStarted, start } from "nprogress";
import {
getRootEl,
initClient,
revalidateOnWindowFocus,
setupGlobalLoadingIndicator,
} from "river.now/client";
import { render } from "solid-js/web";
import { App } from "./app.tsx";
await initClient(() => {
render(() => , getRootEl());
});
```
--------------------------------
### Example: About Location Loader
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/backend_router_loaders_go_tmpl.txt
Registers a loader for the "/about/location" path, returning the location information.
```go
var _ = NewLoader("/about/location", func(c *LoaderCtx) (string, error) {
return "Location: Internet", nil
})
```
--------------------------------
### Core Router Setup with Global Middleware
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/backend_router_core_go_tmpl.txt
Initializes the main router and applies common middleware for logging, recovery, ETag generation, compression, static file serving, security headers, health checks, robots.txt, and favicon redirection.
```go
package router
import (
"{{.GoImportBase}}/app"
chimw "github.com/go-chi/chi/v5/middleware"
"github.com/river-now/river/kit/middleware/etag"
"github.com/river-now/river/kit/middleware/healthcheck"
"github.com/river-now/river/kit/middleware/robotstxt"
"github.com/river-now/river/kit/middleware/secureheaders"
"github.com/river-now/river/kit/mux"
)
func Core() *mux.Router {
r := mux.NewRouter(nil)
mux.SetGlobalHTTPMiddleware(r, chimw.Logger)
mux.SetGlobalHTTPMiddleware(r, chimw.Recoverer)
mux.SetGlobalHTTPMiddleware(r, etag.Auto())
mux.SetGlobalHTTPMiddleware(r, chimw.Compress(5))
mux.SetGlobalHTTPMiddleware(r, app.Wave.ServeStatic(true))
mux.SetGlobalHTTPMiddleware(r, secureheaders.Middleware)
mux.SetGlobalHTTPMiddleware(r, healthcheck.Healthz)
mux.SetGlobalHTTPMiddleware(r, robotstxt.Allow)
mux.SetGlobalHTTPMiddleware(r, app.Wave.FaviconRedirect())
// river API routes
actionsHandler := app.River.GetActionsHandler(ActionsRouter)
// include other methods as needed
mux.RegisterHandler(r, "GET", ActionsRouter.MountRoot("*" ), actionsHandler)
mux.RegisterHandler(r, "POST", ActionsRouter.MountRoot("*" ), actionsHandler)
// river UI routes
mux.RegisterHandler(r, "GET", "/*", app.River.GetUIHandler(LoadersRouter))
return r
}
```
--------------------------------
### Example: Index Loader
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/backend_router_loaders_go_tmpl.txt
Registers a loader for the index segment ("/_index"). This loader returns a simple string indicating it's the home page.
```go
var _ = NewLoader("/_index", func(c *LoaderCtx) (string, error) {
return "This is the home page.", nil
})
```
--------------------------------
### Configure Global Loading Indicator
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/frontend_entry_tsx_solid_str.txt
Sets up a global loading indicator using the nprogress library. It maps nprogress functions to the client's loading indicator API.
```TypeScript
setupGlobalLoadingIndicator({ start, stop: done, isRunning: isStarted });
```
--------------------------------
### Serve HTTP Server
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/backend_server_server_go_tmpl.txt
Configures and starts the HTTP server for the River application. It sets up various timeouts, error logging, and integrates with a graceful shutdown mechanism.
```go
package server
import (
"{{.GoImportBase}}/app"
"{{.GoImportBase}}/backend/router"
"context"
"fmt"
"log"
"net/http"
"os"
"time"
"github.com/river-now/river/kit/grace"
"github.com/river-now/river/wave"
)
func Serve() {
app.River.Init(wave.GetIsDev())
addr := fmt.Sprintf(":%d", wave.MustGetPort())
server := &http.Server{
Addr: addr,
Handler: http.TimeoutHandler(router.Core(), 60*time.Second, "Request timed out"),
ReadTimeout: 15 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 60 * time.Second,
ReadHeaderTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20, // 1 MB
DisableGeneralOptionsHandler: true,
ErrorLog: log.New(os.Stderr, "HTTP: ", log.Ldate|log.Ltime|log.Lshortfile),
}
url := "http://localhost" + addr
grace.Orchestrate(grace.OrchestrateOptions{
StartupCallback: func() error {
app.Log.Info("Starting server", "url", url)
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("Server listen and serve error: %v\n", err)
}
return nil
},
ShutdownCallback: func(shutdownCtx context.Context) error {
app.Log.Info("Shutting down server", "url", url)
if err := server.Shutdown(shutdownCtx); err != nil {
log.Fatalf("Server shutdown error: %v\n", err)
}
return nil
},
})
}
```
--------------------------------
### Configure Revalidation on Window Focus
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/frontend_entry_tsx_solid_str.txt
Configures the application to revalidate data when the browser window regains focus, with a specified stale time.
```TypeScript
revalidateOnWindowFocus({ staleTimeMS: 10_000 });
```
--------------------------------
### Window Focus Revalidation Configuration
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/frontend_entry_tsx_react_str.txt
Sets up automatic revalidation of data when the browser window regains focus. This ensures that the application displays the most up-to-date information.
```typescript
revalidateOnWindowFocus({ staleTimeMS: 10_000 });
```
--------------------------------
### Revalidate on Window Focus
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/frontend_entry_tsx_preact_str.txt
Sets up automatic data revalidation when the browser window regains focus. This ensures that the displayed data is fresh without manual intervention.
```TypeScript
import { revalidateOnWindowFocus } from "river.now/client";
revalidateOnWindowFocus({ staleTimeMS: 10_000 });
```
--------------------------------
### River Router Navigation and Data Display
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/frontend_root_tsx_tmpl.txt
This snippet demonstrates the core functionality of the River router. It sets up navigation links for various route patterns, including static, dynamic, and splat routes. It also displays router data such as build ID, matched patterns, splat values, and parameters, along with root data properties like Message, Count, and Username.
```TypeScript
import { api } from "./api_client.ts";
import { AppLink, useRouterData, type RouteProps } from "./app_utils.tsx";
import { USERNAME_MAX_LEN } from "./river.gen.ts";
export function Root(props: RouteProps<"/">) {
const routerData = useRouterData(props);
return (
<>
>
);
}
```
--------------------------------
### Set Username Action
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/backend_router_actions_go_tmpl.txt
An example action that sets a global username. It uses a POST request and expects a JSON payload with a 'username' field. Includes validation for the username.
```go
type UsernameInput struct {
Username string `json:"username"`
}
func (i *UsernameInput) Validate() error {
if i.Username == "" {
return errors.New("username is required")
}
if len(i.Username) > UsernameMaxLen {
return fmt.Errorf("username must be at most %d characters", UsernameMaxLen)
}
return nil
}
var _ = NewAction("POST", "/set-username", func(c *ActionCtx[UsernameInput]) (string, error) {
username = c.Input().Username
return username, nil
})
```
--------------------------------
### Vite Configuration with UI Variants and River Plugin
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/vite_config_ts_tmpl.txt
This snippet shows the main Vite configuration file. It imports necessary plugins like the UI variant plugin and the river-specific Vite plugin. It also conditionally includes Tailwind CSS based on project setup.
```typescript
import {
defineConfig
} from "vite";
import {{.UIVariant}} from "{{.UIVitePlugin}}";
import { riverVitePlugin } from "./frontend/river.gen.ts";
{{.TailwindViteImport}}
export default defineConfig({
plugins: [{{.UIVariant}}(), riverVitePlugin(){{.TailwindViteCall}}],
});
```
--------------------------------
### Define Application Routes
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/frontend_routes_ts_str.txt
This snippet demonstrates how to define application routes using the `routes.Add` method in TypeScript. It maps URL paths to specific components and provides a display name for each route. The example includes static paths, a dynamic route segment (`:id`), and a splat route (`*`).
```TypeScript
import type { RiverRoutes } from "river.now/client";
declare const routes: RiverRoutes;
routes.Add("/", import("./root.tsx"), "Root");
routes.Add("/_index", import("./home.tsx"), "Home");
routes.Add("/about", import("./about.tsx"), "About");
routes.Add("/about/location", import("./about.tsx"), "Location");
routes.Add("/about/hobbies", import("./about.tsx"), "Hobbies");
routes.Add("/about/:id", import("./about.tsx"), "Dynamic");
routes.Add("/about/*", import("./about.tsx"), "Splat");
export default routes;
```
--------------------------------
### River Now API Query Function
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/frontend_api_client_ts_str.txt
Handles making GET requests to the River Now API. It constructs the URL, resolves the HTTP method, and submits the request using the provided options. It expects a RiverQueryPattern for input and returns a RiverQueryOutput.
```typescript
import { apiHelper, submit } from "river.now/client";
import {
apiConfig,
type BaseQueryPropsWithInput,
type RiverQueryOutput,
type RiverQueryPattern,
} from "./river.gen.ts";
async function query
(
props: BaseQueryPropsWithInput
,
) {
const opts = apiHelper.toQueryOpts(apiConfig, props);
return await submit>(
apiHelper.buildURL(opts),
{
method: apiHelper.resolveMethod(opts),
},
props.options,
);
}
```
--------------------------------
### API Mutation for Setting Username
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/frontend_root_tsx_tmpl.txt
This snippet demonstrates how to update a username via an API mutation. It captures the username from a form input, constructs an input object, and sends it to the '/set-username' pattern using `api.mutate`. The input is constrained by `USERNAME_MAX_LEN`.
```TypeScript
const formData = new FormData(e.currentTarget);
const username = formData.get("username") as string;
api.mutate({
pattern: "/set-username",
input: { username },
});
```
--------------------------------
### Initialize River Instance
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/app_go_tmpl.txt
Demonstrates the initialization of the main River instance, including setting up wave configuration, defining unique head element rules, default head elements, and root template data.
```go
package app
import (
"embed"
"net/http"
"github.com/river-now/river"
"github.com/river-now/river/kit/colorlog"
"github.com/river-now/river/kit/headels"
"github.com/river-now/river/kit/htmlutil"
"github.com/river-now/river/wave"
)
var River = &river.River{
Wave: Wave,
GetHeadElUniqueRules: func() *headels.HeadEls {
e := river.NewHeadEls(2)
e.Meta(e.Property("og:title"))
e.Meta(e.Property("og:description"))
return e
},
GetDefaultHeadEls: func(r *http.Request) ([]*htmlutil.Element, error) {
e := river.NewHeadEls()
e.Title("River Example")
e.Description("This is a River example.")
return e.Collect(), nil
},
GetRootTemplateData: func(r *http.Request) (map[string]any, error) {
// This gets fed into backend/__static/entry.go.html
return map[string]any{}, nil
},
}
//go:embed wave.config.json
var configBytes []byte
//go:embed all:__dist/static
var staticFS embed.FS
var Wave = wave.New(&wave.Config{
ConfigBytes: configBytes,
StaticFS: staticFS,
StaticFSEmbedDirective: "all:__dist/static",
})
```
--------------------------------
### Initialize Actions Router
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/backend_router_actions_go_tmpl.txt
Sets up the main router for actions with a root path and custom input parsing logic based on HTTP methods.
```go
var ActionsRouter = mux.NewRouter(&mux.Options{
MountRoot: "/api/",
ParseInput: func(r *http.Request, iPtr any) error {
// include other methods as needed
if r.Method == http.MethodGet {
return validate.URLSearchParamsInto(r, iPtr)
}
if r.Method == http.MethodPost {
return validate.JSONBodyInto(r, iPtr)
}
return errors.New("unsupported method")
},
})
```
--------------------------------
### Increment Count Action
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/backend_router_actions_go_tmpl.txt
An example action that increments a global counter and returns the new count. It uses a POST request and expects no input.
```go
var _ = NewAction("POST", "/increment-count", func(c *ActionCtx[mux.None]) (int, error) {
count++
return count, nil
})
```
--------------------------------
### Bump Package and Publish to NPM
Source: https://github.com/river-now/river/blob/main/RELEASE_INSTRUCTIONS.md
Automates the process of bumping the package version in package.json, running the build, and publishing to NPM.
```sh
make npmbump
```
--------------------------------
### Initialize Color Logger
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/app_go_tmpl.txt
Sets up a new color logger instance using the provided Go import base. This logger is used for formatted output in the application.
```go
var Log = colorlog.New("{{.GoImportBase}}")
```
--------------------------------
### Bump the Site
Source: https://github.com/river-now/river/blob/main/RELEASE_INSTRUCTIONS.md
Executes the command to bump the version for the project's associated website.
```sh
make sitebump
```
--------------------------------
### Publish to Go Proxy and Push Version Tag
Source: https://github.com/river-now/river/blob/main/RELEASE_INSTRUCTIONS.md
Handles the process of publishing to the Go proxy and pushing a version tag to the repository.
```sh
make gobump
```
--------------------------------
### Push to GitHub
Source: https://github.com/river-now/river/blob/main/RELEASE_INSTRUCTIONS.md
Stages all changes, commits them with a version message, and pushes to the GitHub repository.
```sh
git add .
git commit -m 'v0.0.0-pre.0'
git push
```
--------------------------------
### API Mutation for Incrementing Count
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/frontend_root_tsx_tmpl.txt
This snippet shows how to trigger a mutation on the API to increment a count. It uses the `api.mutate` function with a specified pattern '/increment-count'. This is a common pattern for updating server-side state.
```TypeScript
api.mutate({
pattern: "/increment-count",
});
```
--------------------------------
### River Server-Side Rendering Script (Go)
Source: https://github.com/river-now/river/blob/main/internal/site/backend/__static/entry.go.html
Handles server-side rendering logic for the River project. This script is crucial for initial page content generation and SEO.
```Go
package main
import "fmt"
func main() {
// Placeholder for RiverSSRScript execution
fmt.Println("Executing River Server-Side Rendering Script...")
}
```
--------------------------------
### Build Hooks
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/wave_config_json_tmpl.txt
Defines the commands to execute for development and production builds. These commands utilize Go to run a build script with specific flags.
```go
go run ./__cmd/build --dev --hook
```
```go
go run ./__cmd/build --hook
```
--------------------------------
### River Head Elements Rendering (Go)
Source: https://github.com/river-now/river/blob/main/internal/site/backend/__static/entry.go.html
Renders essential head elements for the River application. This likely includes meta tags, title, and links to stylesheets or scripts required for the initial page load.
```Go
package main
import "fmt"
func main() {
// Placeholder for RiverHeadEls rendering logic
fmt.Println("Rendering River Head Elements...")
}
```
--------------------------------
### Client-Side Entry and Routing
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/wave_config_json_tmpl.txt
Specifies the main entry point for the client-side application and the file containing route definitions. It also includes the output path for TypeScript generation.
```typescript
ClientEntry: frontend/entry.tsx
```
```typescript
ClientRouteDefsFile: frontend/routes.ts
```
```typescript
TSGenOutPath: frontend/river.gen.ts
```
--------------------------------
### Initialize and Proxy Go App (TypeScript)
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/api_proxy_ts_str.txt
This TypeScript code initializes a Go application by spawning it as a child process. It includes a health check mechanism to ensure the Go app is ready before proxying requests and handles request forwarding, response piping, and error management.
```typescript
import type { VercelRequest, VercelResponse } from "@vercel/node";
import { ChildProcess, spawn } from "node:child_process";
import http from "node:http";
import { join } from "node:path";
import waveConfig from "../app/wave.config.json" with { type: "json" };
logInfo("Initializing proxy...");
const GO_APP_LOCATION = join(process.cwd(), waveConfig.Core.DistDir, "main");
const GO_APP_HEALTH_CHECK_ENDPOINT = waveConfig.Watch.HealthcheckEndpoint;
const GO_APP_STARTUP_TIMEOUT_IN_MS = 10_000; // 10s
const PORT = 8080;
let goProcess: ChildProcess | null = null;
let goReady = false;
async function waitForGoApp(url: string, timeout: number): Promise {
const startTime = Date.now();
return new Promise((resolve, reject) => {
const attempt = () => {
http.get(url, (res) => {
if (
res.statusCode &&
res.statusCode >= 200 &&
res.statusCode < 400
) {
resolve();
} else {
scheduleNextAttempt();
}
}).on("error", scheduleNextAttempt);
};
const scheduleNextAttempt = (err?: Error) => {
if (Date.now() - startTime > timeout) {
return reject(err || new Error("Health check timed out."));
}
setTimeout(attempt, 50);
};
attempt();
});
}
async function init() {
const startTime = performance.now();
try {
goProcess = spawn(GO_APP_LOCATION, [], {
env: { ...process.env, PORT: PORT.toString() },
stdio: "pipe",
});
goProcess.stdout?.on("data", (data) =>
console.log(`[Go]: ${data.toString().trim()}`),
);
goProcess.stderr?.on("data", (data)
console.error(`[Go ERR]: ${data.toString().trim()}`),
);
goProcess.on("exit", (code, signal) => {
logInfo(`Go process exited with code ${code}, signal ${signal}.`);
goProcess = null;
});
const healthCheckURL = `http://localhost:${PORT}${GO_APP_HEALTH_CHECK_ENDPOINT}`;
await waitForGoApp(healthCheckURL, GO_APP_STARTUP_TIMEOUT_IN_MS);
goReady = true;
logInfo(
`Go app is ready in ${(performance.now() - startTime).toFixed(2)}ms.`,
);
} catch (err) {
logErr("Fatal error during initialization:", err);
goProcess?.kill();
goProcess = null;
throw err;
}
}
const startupPromise = init();
await startupPromise;
export default async function handler(req: VercelRequest, res: VercelResponse) {
try {
if (!goReady) {
logInfo("Go app is not ready yet, waiting for initialization.");
await startupPromise;
}
if (!goProcess || goProcess.killed) {
res.status(503).send(
"Service Unavailable: The backend service is not running.",
);
return;
}
const proxyReq = http.request(
{
hostname: "localhost",
port: PORT,
path: req.url,
method: req.method,
headers: req.headers,
},
(proxyRes) => {
res.writeHead(proxyRes.statusCode ?? 500, proxyRes.headers);
proxyRes.pipe(res, { end: true });
},
);
proxyReq.on("error", (err) => {
logErr("Error proxying request:", err);
if (!res.headersSent) {
res.status(502).send("Bad Gateway");
}
res.end();
});
req.pipe(proxyReq, { end: true });
} catch (error) {
logErr("Handler error:", error);
if (!res.headersSent) {
res.status(500).send(
"Internal Server Error: Initialization failed.",
);
}
}
}
process.on("SIGTERM", () => {
if (goProcess) {
logInfo("SIGTERM received, shutting down Go process.");
goProcess.kill("SIGTERM");
}
});
function logInfo(message?: any, ...optionalParams: any[]) {
console.log(`[Node Proxy]: ${message}`, ...optionalParams);
}
function logErr(message?: any, ...optionalParams: any[]) {
console.error(`[Node Proxy ERR]: ${message}`, ...optionalParams);
}
```
--------------------------------
### Push Site Bump Commit
Source: https://github.com/river-now/river/blob/main/RELEASE_INSTRUCTIONS.md
Stages changes related to the site bump, commits them, and pushes to GitHub.
```sh
git add .
git commit -m 'sitebump'
git push
```
--------------------------------
### CSS Entry Files
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/wave_config_json_tmpl.txt
Defines the entry points for critical and non-critical CSS files. These are used for managing CSS loading and optimization during the build process.
```go
Critical: frontend/css/main.critical.css
```
```go
NonCritical: frontend/css/main.css
```
--------------------------------
### System Theme Script (TypeScript)
Source: https://github.com/river-now/river/blob/main/internal/site/backend/__static/entry.go.html
Manages the application's theme settings, likely handling dark mode, light mode, or custom color schemes. This script ensures a consistent user interface across the application.
```TypeScript
function setSystemTheme(theme: 'light' | 'dark'): void {
// Placeholder for theme setting logic
console.log(`Setting system theme to: ${theme}`);
document.body.className = theme;
}
// Example usage:
// setSystemTheme('dark');
```
--------------------------------
### Vercel Configuration (vercel.json)
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/vercel_json_tmpl.txt
The main configuration file for Vercel deployments. It specifies build commands, output directories, function routing, rewrites, and headers.
```json
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"installCommand": "{{.ResolveJSPackageManagerRunScriptPrefix}} vercel-install",
"buildCommand": "{{.ResolveJSPackageManagerRunScriptPrefix}} vercel-build",
"outputDirectory": "app/__dist/static/assets/public",
"functions": {
"api/proxy.ts": {
"includeFiles": "app/{__dist/main,wave.config.json}"
}
},
"rewrites": [
{
"source": "/(.*)",
"destination": "/api/proxy"
}
],
"headers": [
{
"source": "/river_out_(.*).(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
}
]
}
```
--------------------------------
### Benchmark Results for FindBestMatchAtScale
Source: https://github.com/river-now/river/blob/main/kit/matcher/bench.txt
Evaluates the performance of FindBestMatchAtScale under different scaling conditions (small, medium, large) and a worst-case scenario with deep nesting. Metrics include ops/sec, ns/op, B/op, and allocs/op.
```Go
BenchmarkFindBestMatchAtScale/Scale_small-14 13821547 85.69 ns/op 214 B/op 2 allocs/op
BenchmarkFindBestMatchAtScale/Scale_medium-14 10015520 117.5 ns/op 236 B/op 2 allocs/op
BenchmarkFindBestMatchAtScale/Scale_large-14 10214275 115.9 ns/op 238 B/op 2 allocs/op
BenchmarkFindBestMatchAtScale/WorstCase_DeepNested-14 5872576 204.2 ns/op 480 B/op 4 allocs/op
```
--------------------------------
### Static Asset Directories
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/wave_config_json_tmpl.txt
Specifies the locations for private and public static assets within the project structure. These directories are used to organize frontend and backend static files.
```go
Private: backend/__static
```
```go
Public: frontend/__static
```
--------------------------------
### Benchmark Results for River Router
Source: https://github.com/river-now/river/blob/main/kit/mux/bench.txt
This section presents the benchmark results for different routing configurations within the River project. It includes operations per second, nanoseconds per operation, bytes per operation, and allocations per operation for each test case.
```Go
BenchmarkRouter/SimpleStaticRoute-14 16236374 65.43 ns/op 72 B/op 2 allocs/op
BenchmarkRouter/DynamicRoute-14 4548141 256.7 ns/op 888 B/op 9 allocs/op
BenchmarkRouter/WithMiddleware-14 18213898 65.94 ns/op 72 B/op 2 allocs/op
BenchmarkRouter/RESTfulAPI-14 3779438 306.1 ns/op 708 B/op 7 allocs/op
BenchmarkRouter/LargeRouterMatch-14 4852839 242.6 ns/op 223 B/op 6 allocs/op
BenchmarkRouter/WorstCaseMatch-14 5603818 213.3 ns/op 183 B/op 6 allocs/op
BenchmarkRouter/NestedDynamicRoute-14 3413928 351.9 ns/op 984 B/op 9 allocs/op
BenchmarkRouter/TaskHandler-14 1286095 875.4 ns/op 2412 B/op 28 allocs/op
BenchmarkNestedRouter/Simple_Nested_Match-14 2390478 505.1 ns/op 584 B/op 10 allocs/op
BenchmarkNestedRouter/Nested_Tasks_Execution-14 407470 2826 ns/op 1874 B/op 36 allocs/op
BenchmarkNestedRouter/Deep_Nesting-14 1000000 1059 ns/op 1672 B/op 20 allocs/op
```
--------------------------------
### Create New Action
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/backend_router_actions_go_tmpl.txt
Defines a new action handler for a given HTTP method and pattern, wrapping a user-provided function with custom context.
```go
func NewAction[I any, O any](method, pattern string, f func(c *ActionCtx[I]) (O, error)) *mux.TaskHandler[I, O] {
wrappedF := func(c *mux.ReqData[I]) (O, error) {
return f(&ActionCtx[I]{
ReqData: c,
// Anything else you want available on the ActionCtx
})
}
actionTask := mux.TaskHandlerFromFunc(wrappedF)
mux.RegisterTaskHandler(ActionsRouter, method, pattern, actionTask)
return actionTask
}
```
--------------------------------
### Benchmark Results for River Project
Source: https://github.com/river-now/river/blob/main/kit/tasks/bench.txt
Provides performance metrics for different task execution scenarios within the River project. Includes operations per second (ns/op), bytes allocated per operation (B/op), and allocations per operation (allocs/op).
```Go
BenchmarkSingleTask-14 6125827 180.9 ns/op
BenchmarkParallelIndependentTasks-14 467799 2511 ns/op
BenchmarkHighContention-14 202872 5845 ns/op
BenchmarkTaskWithDependencies-14 4311055 274.1 ns/op
BenchmarkAllocations-14 5430886 218.1 ns/op 504 B/op 9 allocs/op
BenchmarkParallelScaling/tasks-1-14 5063791 217.0 ns/op
BenchmarkParallelScaling/tasks-2-14 604203 1823 ns/op
BenchmarkParallelScaling/tasks-5-14 333288 3530 ns/op
BenchmarkParallelScaling/tasks-10-14 123667 9642 ns/op
BenchmarkParallelScaling/tasks-20-14 53574 22285 ns/op
BenchmarkParallelScaling/tasks-50-14 23145 52007 ns/op
BenchmarkContextCancellation-14 100 10929385 ns/op
BenchmarkRepeatedTaskCalls-14 69537187 16.89 ns/op 0 B/op 0 allocs/op
```
--------------------------------
### Location Accessor for Solid
Source: https://github.com/river-now/river/blob/main/internal/site/backend/__static/markdown/docs.md
Direct access to reactive location data for SolidJS applications using river.now.
```typescript
import { location } from "river.now/solid";
// then call the accessor anywhere in a component:
location();
```
--------------------------------
### River Routes API
Source: https://github.com/river-now/river/blob/main/bootstrap/tmpls/frontend_routes_ts_str.txt
This section details the `routes.Add` method used for configuring routes within the River framework. It explains the parameters required to map a URL path to a component and assign a display name.
```APIDOC
routes.Add(path: string, component: Promise, name: string)
Adds a new route to the application.
Parameters:
- path: The URL path for the route. Can include dynamic segments (e.g., ":id") and splat routes (e.g., "*").
- component: A Promise that resolves to the React component to be rendered for this route.
- name: A human-readable name for the route, used for display purposes.
Example:
routes.Add("/users/:userId", import("./UserProfile.tsx"), "User Profile");
Special Rules:
1. Do not import anything other than types.
2. Do not rename the `routes` variable.
3. Always use the true extension for import paths (e.g., ".tsx")
```