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
32 changes: 25 additions & 7 deletions .github/scripts/check.sh
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
#!/usr/bin/env bash
# Local pre-PR check — runs the same gates CI runs (cargo fmt + clippy +
# tests + Python tests) and writes a one-line PASS/FAIL summary you can
# paste into the PR description as proof.
# tests + Python tests + beava-js Vitest) and writes a one-line PASS/FAIL
# summary you can paste into the PR description as proof.
#
# Usage:
# bash .github/scripts/check.sh # run everything, print summary
# bash .github/scripts/check.sh --fast # skip cargo test (~10× faster)
# bash .github/scripts/check.sh --rust # rust gates only (cargo fmt/clippy/test)
# bash .github/scripts/check.sh --python # python gates only (ruff + mypy + pytest)
# bash .github/scripts/check.sh --js # beava-js only (pnpm install + turbo lint/check-types/test)
# bash .github/scripts/check.sh --output= # path for full log (default ~/.beava-check.log)
#
# --rust and --python are mutually exclusive; pass neither to run both.
# When target/debug/beava exists, beava-js Vitest also runs HTTP integration tests (BEAVA_INTEGRATION=1).
# Otherwise three integration tests are skipped (same as CI after cargo build).
#
# --rust, --python, and --js are mutually exclusive; pass none to run rust + python + beava-js.
#
# Exit code: 0 if all checks pass, non-zero otherwise.
set -uo pipefail

FAST=0
RUN_RUST=1
RUN_PYTHON=1
RUN_JS=1
OUT="$HOME/.beava-check.log"
for arg in "$@"; do
case "$arg" in
--fast) FAST=1 ;;
--rust) RUN_PYTHON=0 ;;
--python) RUN_RUST=0 ;;
--rust) RUN_PYTHON=0; RUN_JS=0 ;;
--python) RUN_RUST=0; RUN_JS=0 ;;
--js) RUN_RUST=0; RUN_PYTHON=0; RUN_JS=1 ;;
--output=*) OUT="${arg#--output=}" ;;
-h|--help)
sed -n '2,/^set/p' "$0" | sed 's/^# \{0,1\}//; /^set /d'
Expand All @@ -32,8 +38,8 @@ for arg in "$@"; do
esac
done

if [[ "$RUN_RUST" -eq 0 && "$RUN_PYTHON" -eq 0 ]]; then
echo "error: --rust and --python are mutually exclusive" >&2
if [[ "$RUN_RUST" -eq 0 && "$RUN_PYTHON" -eq 0 && "$RUN_JS" -eq 0 ]]; then
echo "error: no check targets enabled (use --rust, --python, or --js)" >&2
exit 2
fi

Expand Down Expand Up @@ -100,6 +106,18 @@ if [[ "$RUN_PYTHON" -eq 1 && -d python && -f python/pyproject.toml ]]; then
bash -c 'cd python && python -m pytest -q --no-header'
fi

if [[ "$RUN_JS" -eq 1 && -f "$REPO_ROOT/beava-js/package.json" ]]; then
js_cmd='cd beava-js && (command -v corepack >/dev/null 2>&1 && corepack enable pnpm || true) && pnpm install && pnpm exec turbo run lint check-types test'
if [[ -f "$REPO_ROOT/target/debug/beava" || -f "$REPO_ROOT/target/debug/beava.exe" ]]; then
run "pnpm turbo lint/check-types/test (beava-js, +HTTP integration)" \
env BEAVA_INTEGRATION=1 BEAVA_REPO_ROOT="$REPO_ROOT" bash -c "$js_cmd"
else
printf '\n(note: target/debug/beava missing; 3 HTTP integration Vitest tests skipped — run: cargo build --bin beava)\n' | tee -a "$OUT" >&2
run "pnpm turbo lint/check-types/test (beava-js, unit Vitest only)" \
bash -c "$js_cmd"
fi
fi

# Tail-of-log + summary.
echo
echo "─── Summary ────────────────────────────────────────────────"
Expand Down
31 changes: 31 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,34 @@ jobs:
- name: Run pytest (v0 acceptance suite via pyproject testpaths)
working-directory: python
run: python -m pytest -q --timeout=60 --no-header

beava-js:
name: beava-js (lint, types, Vitest + HTTP integration)
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
key: beava-js-${{ runner.os }}

- name: Build beava binary (Vitest integration harness)
run: cargo build --bin beava

- uses: actions/setup-node@v4
with:
node-version: "22"

- name: Enable pnpm
run: corepack enable pnpm

- name: pnpm install + turbo (lint, check-types, test)
working-directory: beava-js
env:
BEAVA_INTEGRATION: "1"
BEAVA_REPO_ROOT: ${{ github.workspace }}
run: |
pnpm install
pnpm exec turbo run lint check-types test
25 changes: 25 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,23 @@ jobs:
--cov-report=term-missing:skip-covered \
2>&1 | tee /tmp/python-test-output.txt

- uses: actions/setup-node@v4
with:
node-version: "22"

- name: Enable pnpm
run: corepack enable pnpm

- name: beava-js (lint, types, Vitest + integration)
working-directory: beava-js
env:
BEAVA_INTEGRATION: "1"
BEAVA_REPO_ROOT: ${{ github.workspace }}
id: beava_js_tests
run: |
pnpm install
pnpm exec turbo run lint check-types test 2>&1 | tee /tmp/beava-js-test-output.txt

- name: Add Python summary
if: always()
run: |
Expand All @@ -149,6 +166,14 @@ jobs:
|| echo "No coverage block captured" >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"

- name: Add beava-js summary
if: always()
run: |
echo "## beava-js Test Results" >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
tail -15 /tmp/beava-js-test-output.txt >> "$GITHUB_STEP_SUMMARY" 2>/dev/null || echo "No beava-js output captured" >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"

coverage-rust:
name: Rust coverage (informational)
runs-on: ubuntu-latest
Expand Down
5 changes: 3 additions & 2 deletions SOURCE-OF-TRUTH.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ left column wins; the right column is downstream and must be regenerated.
| Domain | Canonical source | Downstream |
|--------|-----------------|------------|
| Python SDK API | `python/beava/__init__.py` (`__all__`) + `python/beava/_*.py` | `beava-website/project/sdk/python/*/index.html` |
| TypeScript / npm HTTP SDK (`@beava/node`, `@beava/client`) | `beava-js/packages/beava-node/src/*.ts` + `wire-schemas.ts` | npm package READMEs; wire parity with Python `HttpTransport` |
| Server CLI flags | `crates/beava-server/src/cli.rs` | `beava-website/project/sdk/server/index.html` |
| Server config (env vars, YAML) | `crates/beava-server/src/{config,wal_config,main}.rs` + `beava.example.yaml` | `beava-website/project/sdk/server/index.html` |
| Wire format (TCP frames + opcodes) | `crates/beava-core/src/wire.rs` | `beava-website/project/sdk/http/wire-spec/index.html` |
Expand All @@ -22,7 +23,7 @@ left column wins; the right column is downstream and must be regenerated.
| Surface | Canonical source | Notes |
|---------|------------------|-------|
| SDK sidebar nav | `beava-website/project/js/sdk/SdkSidebar.jsx` (`SDK_NAV` const) | Single source for all 12 SDK pages |
| SDK shell CSS | `beava-website/project/styles/sdk-shell.css` | Hero CSS stays inline per page |
| SDK shell CSS | `beava-website/project/styles/sdk-shell.css` (bundled via `styles/beava.entry.css` → `project/styles/beava.css`) | Hero CSS stays inline per page |
| Site header / footer | `beava-website/project/js/_shared/{SiteHeader,SiteFooter}.jsx` | Mounted via Babel on every page |
| LLM agent text dump | regenerated by `npm run build:llms` | `beava-website/project/sdk/llms.txt` (index) + `llms-full.txt` (concat) |
| Pagefind search index | regenerated by `npm run build:search` | `beava-website/project/_pagefind/` |
Expand Down Expand Up @@ -57,7 +58,7 @@ cd beava-website && npm run build:search

The 12 SDK pages under `beava-website/project/sdk/` are hand-written HTML
(~6,500 LOC total). They mount shared chrome via React+Babel (SiteHeader /
SiteFooter / SdkSidebar / SdkTOC / SdkPager) and load `sdk-shell.css` for
SiteFooter / SdkSidebar / SdkTOC / SdkPager) and load the compiled `beava.css` (includes sdk-shell rules) for
prose styling. Authoring is 80% prose + 20% chrome boilerplate.

A markdown-based pipeline was considered for this PR. **Findings:**
Expand Down
40 changes: 40 additions & 0 deletions beava-js/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# Dependencies
node_modules
.pnp
.pnp.js

# Local env files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# Testing
coverage

# Turbo
.turbo

# Build outputs
dist

# Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# pnpm
.pnpm-debug.log*

# TypeScript incremental (if enabled in a package tsconfig)
*.tsbuildinfo

# ESLint
.eslintcache

# Misc
.DS_Store
*.pem
5 changes: 5 additions & 0 deletions beava-js/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
dist
coverage
.turbo
pnpm-lock.yaml
163 changes: 163 additions & 0 deletions beava-js/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# beava-js

[Turborepo](https://turbo.build/repo) workspace for the official Beava TypeScript HTTP clients.

- **`@beava/node`**: Node.js service and script client.
- **`@beava/client`**: browser-oriented package name that re-exports the same fetch-based API.

Both packages expose `createBeavaClient`, typed request and response shapes, Beava wire error classes, and Zod schemas for the HTTP data plane.

## Install

```sh
npm install @beava/node
# or, in browser bundles:
npm install @beava/client
```

Run a local server first:

```sh
beava
# default HTTP URL: http://127.0.0.1:8080
```

## Example

```ts
import { BeavaError, createBeavaClient } from "@beava/node";

const beava = createBeavaClient({
baseUrl: "http://127.0.0.1:8080",
timeoutSeconds: 10,
});

await beava.ping();

await beava.register({
nodes: [
{
kind: "event",
name: "Purchase",
schema: {
fields: {
user_id: "str",
amount: "f64",
},
optional_fields: [],
},
dedupe_key: null,
dedupe_window_ms: null,
keep_events_for_ms: null,
},
],
});

await beava.push({
event: "Purchase",
data: {
user_id: "alice",
amount: 42.5,
},
});
```

Read from a feature table that your deployed Beava pipeline has registered:

```ts
try {
const row = await beava.get({ table: "UserSpend", key: "alice" });
console.log(row);
} catch (error) {
if (error instanceof BeavaError) {
console.error(error.status, error.code, error.message);
}
}
```

A runnable Node example lives at `examples/node-basic.mjs`. After `pnpm install` and `pnpm run build`, run it with:

```sh
BEAVA_URL=http://127.0.0.1:8080 node examples/node-basic.mjs
```

## API Surface

| Method | Wire route | Notes |
| ------------------------------------- | ----------------- | -------------------------------------- |
| `ping()` | `POST /ping` | Liveness and registry version. |
| `register({ nodes, force, dry_run })` | `POST /register` | Registers event and table descriptors. |
| `push({ event, data })` | `POST /push` | Sends one event payload. |
| `get({ table, key, features })` | `POST /get` | Reads one feature row. |
| `batchGet({ requests })` | `POST /batch_get` | Reads many feature rows. |
| `reset()` | `POST /reset` | Test-mode only state reset. |

Server error envelopes throw `BeavaError`. Malformed success responses throw `BeavaResponseValidationError`, which usually means the client and server versions disagree about the wire shape.

## Layout

| Path | Package | Role |
| ------------------------- | --------------- | ------------------------------------------------------------------------------------ |
| `packages/beava-node` | `@beava/node` | `createBeavaClient`, Zod wire schemas, Vitest unit + optional HTTP integration tests |
| `packages/beava-client` | `@beava/client` | Re-exports `@beava/node` for app bundles that want a browser-scoped package name |
| `examples/node-basic.mjs` | example | Connects to a running Beava server, registers an event, and pushes one row |

Workspace members are defined in **`pnpm-workspace.yaml`** (`packages/beava-node`, `packages/beava-client`). Library packages extend **`tsconfig.node-library.json`** at this directory root (no separate TypeScript config package).

## Prerequisites

- **Node** `>=18` (see root **`package.json`** `engines`)
- **pnpm** `9.x` via [Corepack](https://nodejs.org/api/corepack.html): `corepack enable pnpm`

## Commands

From **`beava-js/`**:

```sh
pnpm install
pnpm run build # tsc emit for publishable packages
pnpm run lint # eslint across workspace
pnpm run check-types # tsc --noEmit
pnpm run test # Vitest (see below)
```

Scoped examples:

```sh
pnpm exec turbo run build test --filter=@beava/node
pnpm exec turbo run lint check-types --filter=@beava/client
```

## Tests

**`@beava/node`** uses [Vitest](https://vitest.dev/). Default **`pnpm run test`** runs **unit tests** only (mocked `fetch`).

**HTTP integration tests** (real `beava` subprocess, same idea as `python/tests/test_transport_http.py`) run when:

1. The **`beava`** binary exists at **`target/debug/beava`** (repo root: run **`cargo build --bin beava`** from the Beava repo root), and
2. You set **`BEAVA_INTEGRATION=1`**. Optionally set **`BEAVA_REPO_ROOT`** to the Beava git root if discovery fails.

```sh
# from beava-js/
BEAVA_INTEGRATION=1 BEAVA_REPO_ROOT=/path/to/beava pnpm exec turbo run test --filter=@beava/node
```

CI sets these when running the **`beava-js`** job in **`.github/workflows/ci.yml`**.

## Repo checks

From the **Beava repo root**, **`bash .github/scripts/check.sh`** can run Rust, Python, and this tree together. **`bash .github/scripts/check.sh --js`** runs **`pnpm install`** and **`turbo run lint check-types test`** under **`beava-js/`** only. If **`target/debug/beava`** exists (from **`cargo build --bin beava`**), **`BEAVA_INTEGRATION=1`** is set so all Vitest tests run; otherwise three HTTP integration tests are skipped and the log notes why.

## npm publish

1. Bump **`version`** in **`packages/beava-node/package.json`** and **`packages/beava-client/package.json`**, and set **`@beava/client`** `dependencies["@beava/node"]` to **`workspace:^<newVersion>`** (same major/minor/patch as **`@beava/node`**). **`pnpm publish`** rewrites that to a normal **`^`** range on the tarball.
2. From **`beava-js/`**: **`pnpm install`**, **`pnpm run build`**, **`pnpm run test`** (with integration if you use **`BEAVA_INTEGRATION=1`**).
3. **`pnpm publish --filter @beava/node --access public`** (uses **`prepack`** to run **`tsc`**; add **`--dry-run`** or **`pnpm pack --filter @beava/node`** to inspect the tarball).
4. After **`@beava/node`** is on the registry, **`pnpm publish --filter @beava/client --access public`**.

Use **`npm whoami`** / **`npm login`** (or **`pnpm config set //registry.npmjs.org/:_authToken`**). Scoped **`@beava/*`** packages need **`publishConfig.access`** (already **`public`**).

## Links

- [Turborepo tasks and filters](https://turbo.build/repo/docs/crafting-your-repository/running-tasks)
- Beava repo: [github.com/beava-dev/beava](https://github.com/beava-dev/beava)
Loading