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
5 changes: 5 additions & 0 deletions .changeset/add-database-client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bunny.net/database-client": patch
---

Add `@bunny.net/database-client`, a zero-dependency server-side SQL client for Bunny Database that runs on Edge Scripting, Bun, and Node.
5 changes: 5 additions & 0 deletions .changeset/database-env-read.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bunny.net/database-client": patch
---

Treat unreadable environment variables as unset instead of crashing when Deno runs without --allow-env
39 changes: 39 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ jobs:
if: needs.changesets.outputs.hasChangesets == 'false'
outputs:
cli-version: ${{ steps.check-cli.outputs.version }}
database-client-version: ${{ steps.check-database-client.outputs.version }}
database-shell-version: ${{ steps.check-database-shell.outputs.version }}
openapi-client-version: ${{ steps.check-openapi-client.outputs.version }}
sandbox-version: ${{ steps.check-sandbox.outputs.version }}
Expand Down Expand Up @@ -87,6 +88,17 @@ jobs:
else
echo "No version change: $VERSION"
fi
- name: Check database-client version
id: check-database-client
run: |
VERSION=$(node -p "require('./packages/database-client/package.json').version")
PUBLISHED=$(npm view @bunny.net/database-client version 2>/dev/null || echo "0.0.0")
if [ "$VERSION" != "$PUBLISHED" ]; then
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "New version detected: $VERSION (published: $PUBLISHED)"
else
echo "No version change: $VERSION"
fi
- name: Check database-shell version
id: check-database-shell
run: |
Expand Down Expand Up @@ -355,6 +367,33 @@ jobs:
npm-artifacts/bunny-darwin-x64-baseline/bunny-darwin-x64-baseline
fail_on_unmatched_files: true

publish-database-client:
name: Publish database-client
runs-on: ubuntu-latest
needs: version
if: needs.version.outputs.database-client-version
steps:
- uses: actions/checkout@v5
- uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.11"
- run: bun install

- name: Build package
run: bun run --filter @bunny.net/database-client build

- uses: actions/setup-node@v5
with:
node-version: "22"
registry-url: "https://registry.npmjs.org"

- name: Publish @bunny.net/database-client
run: |
cd packages/database-client
npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

publish-database-shell:
name: Publish database-shell
runs-on: ubuntu-latest
Expand Down
28 changes: 26 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,11 @@ Bun replaces the entire Node.js toolchain. There are no separate tools for trans

## Project Structure

This is a Bun workspace monorepo with six packages:
This is a Bun workspace monorepo with seven packages:

- **`@bunny.net/openapi-client`** (`packages/openapi-client/`) — Standalone, type-safe OpenAPI client for bunny.net, generated from OpenAPI specs. Zero CLI dependencies. Publishable to npm.
- **`@bunny.net/config`** (`packages/config/`) — Shared `bunny.jsonc` schemas (Zod), inferred types, JSON Schema generation, and API conversion functions. The root `BunnyConfigSchema` has optional `app` (Magic Containers) and `sites` (static sites) blocks; `BunnyAppConfigSchema` narrows it to require `app`. Used by the CLI and potentially other tools.
- **`@bunny.net/database-client`** (`packages/database-client/`) — Standalone SQL client for Bunny Database, for application code rather than the CLI. Speaks hrana-over-HTTP (`POST /v2/pipeline`) using only `fetch`, so it runs unchanged on Edge Scripting (Deno), Bun, and Node. Zero dependencies. **Server-side only, and documented as such:** an auth token is a bearer credential for the whole database and the client sends raw SQL, so it must never reach a browser or other untrusted client. Do not describe it as browser-compatible even though `fetch`-only code would technically run there; the correct pattern is an Edge Script (or `database-rest` behind an auth check) that holds the token and exposes only intended queries. Prepared-statement surface: `connect()`, `prepare().bind()`, `.all()`/`.first()`/`.raw()`/`.run()`, plus `batch()` (one transaction, one round trip) and `exec()` (multi-statement script). Deliberately stateless: no baton tracking, no connection pool, no interactive transactions, no cursor streaming. Publishable to npm.
- **`@bunny.net/database-shell`** (`packages/database-shell/`) — Standalone interactive SQL shell for libSQL databases. Framework-agnostic REPL, dot-commands, formatting, masking, and history. Also usable as a standalone CLI (binary: `bsql`).
- **`@bunny.net/scriptable-dns-types`** (`packages/scriptable-dns-types/`): Ambient TypeScript declarations for the Scriptable DNS runtime globals (`ARecord`, `Monitoring`, `RoutingEngine`, etc.). Types-only, no runtime code: the DNS runtime can't `import`, so these power editor autocomplete and an optional typecheck step. Scaffolded into projects by `bunny dns scripts init`; intended to also feed the dashboard editor. Publishable to npm.
- **`@bunny.net/sandbox`** (`packages/sandbox/`) — Standalone sandbox SDK. Code-first DX (`Sandbox.create`, `writeFiles`, `runCommand`, `exposePort`, `setEnv`/`getEnv`/`unsetEnv`, `listFiles`/`deleteFile`/`rename`/`exists`) over Magic Containers provisioning plus an `ssh2` SSH/SFTP transport. Blocking `runCommand` accepts `timeout` (rejects with `CommandTimeoutError` carrying partial output), `signal` for cancellation, and `onStdout`/`onStderr` callbacks for live output. Env vars can be baked in at `create` (persisted), passed per-command via `runCommand({ env })` (temporary), or persisted after creation via `setEnv`. The handle implements `Symbol.dispose`/`Symbol.asyncDispose` so `using`/`await using` release the SSH connection (without deleting the sandbox). Zero CLI dependencies.
Expand Down Expand Up @@ -137,6 +138,21 @@ bunny-cli/
│ │ ├── index.d.ts # Ambient globals: ARecord/AaaaRecord/CnameRecord/TxtRecord/PullZoneRecord/Server, Monitoring/GeoDatabase/GeoDistance/RoutingEngine, DnsRequest/DnsQuery/GeoLocation
│ │ └── README.md
│ │
│ ├── database-client/ # @bunny.net/database-client package (SQL client for app code)
│ │ ├── package.json # exports/main/types point at dist/ for npm consumers
│ │ ├── tsconfig.json
│ │ ├── tsconfig.build.json # Emits dist/ (JS + .d.ts); paths:{} so nothing resolves from source
│ │ ├── examples/
│ │ │ └── smoke.ts # Live end-to-end run on Bun and Deno; creates and drops its own tables
│ │ └── src/
│ │ ├── index.ts # Barrel export: connect, Database, Statement, DatabaseError, types
│ │ ├── client.ts # Database + Statement; batch() wraps steps in BEGIN/COMMIT/ROLLBACK
│ │ ├── protocol.ts # Hrana wire types, value codecs, normalizeUrl(), /v2/pipeline transport
│ │ ├── errors.ts # DatabaseError (code + status)
│ │ ├── env.ts # BUNNY_DATABASE_URL / _AUTH_TOKEN names, readEnv() over process.env (unreadable reads as unset)
│ │ ├── client.test.ts # Client behaviour against a fake fetch (no network)
│ │ └── protocol.test.ts # URL normalization and value codec tests
│ │
│ ├── database-shell/ # @bunny.net/database-shell package
│ │ ├── package.json # bin: { "bsql": "./src/cli.ts" }
│ │ ├── tsconfig.json
Expand Down Expand Up @@ -475,7 +491,7 @@ bunny-cli/

### Conventions

- **Monorepo with Bun workspaces.** `packages/openapi-client/` is the standalone API client SDK; `packages/config/` provides shared Zod schemas, types, and API conversion functions for `bunny.jsonc`; `packages/database-shell/` is the standalone SQL shell engine; `packages/sandbox/` is the standalone sandbox SDK (provisioning + SSH transport); `packages/cli/` is the CLI.
- **Monorepo with Bun workspaces.** `packages/openapi-client/` is the standalone API client SDK; `packages/config/` provides shared Zod schemas, types, and API conversion functions for `bunny.jsonc`; `packages/database-client/` is the standalone SQL client for application code; `packages/database-shell/` is the standalone SQL shell engine; `packages/sandbox/` is the standalone sandbox SDK (provisioning + SSH transport); `packages/cli/` is the CLI.
- **API clients use `ClientOptions`** — an options object with `apiKey`, `baseUrl`, `verbose`, `userAgent`, and `onDebug`. The CLI provides a `clientOptions(config, verbose)` helper to build this from `ResolvedConfig`.
- **One command per file.** Each file in `commands/` exports a single command or namespace.
- **Commands are grouped by domain** in subdirectories (`config/`, `db/`, `scripts/`).
Expand Down Expand Up @@ -937,6 +953,14 @@ Two differences from openapi-client:
- Sandbox depends on `@bunny.net/openapi-client` with `workspace:*`, so the `publish-sandbox` job in `release.yml` uses `bun publish` (not `npm publish`) — bun rewrites `workspace:*` to the local package version in the published tarball; npm would ship the unresolvable `workspace:*` spec verbatim. `bun publish` authenticates via the `NPM_CONFIG_TOKEN` env var.
- Its `tsconfig.build.json` overrides `paths` to `{}` so openapi-client resolves via its package `exports` (`dist/`) instead of source — otherwise openapi-client's sources would enter the program and violate `rootDir`. The publish job therefore builds openapi-client before building sandbox.

### Publishing `@bunny.net/database-client`

`@bunny.net/database-client` follows the same compiled-library pattern as `@bunny.net/sandbox`, and is the simplest case of it: zero dependencies, so `npm publish` works (there is no `workspace:*` spec for bun to rewrite), and no declaration transformer, so emitted `.d.ts` keep their `.ts` import specifiers for TypeScript to resolve against the sibling `.d.ts`. Its `tsconfig.build.json` sets `include: ["src"]` so `examples/` stays out of the program and does not violate `rootDir`.

The `publish-database-client` job in `release.yml` (gated on a version bump detected via `npm view`, like the other independently versioned packages) builds with `bun run --filter @bunny.net/database-client build`, then runs `cd packages/database-client && npm publish`. The package versions independently of the CLI; it is not part of any `fixed` group in `.changeset/config.json`.

Nothing in the repo imports it: the CLI talks to databases through `@bunny.net/database-shell`, and this package exists for user application code. It therefore has no root `tsconfig.json` `paths` entry, and its tests run against its own source directly.

`@bunny.net/config` is a private workspace package (not published); the CLI consumes it from source via the workspace symlink.

### CI
Expand Down
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@ Monorepo for the [bunny.net](https://bunny.net) CLI and supporting packages.

## Packages

| Package | Name | Description |
| ------------------------------------------------------------------------ | ------------------------------------ | -------------------------------------------------------------------------- |
| [`packages/cli/`](packages/cli/) | `@bunny.net/cli` | Command-line interface for bunny.net |
| [`packages/openapi-client/`](packages/openapi-client/) | `@bunny.net/openapi-client` | Standalone, type-safe OpenAPI client for bunny.net |
| [`packages/sandbox/`](packages/sandbox/) | `@bunny.net/sandbox` | Standalone sandbox SDK over Magic Containers and SSH |
| [`packages/config/`](packages/config/) | `@bunny.net/config` | Shared Zod schemas, types, and JSON Schema for `bunny.jsonc` (app + sites) |
| [`packages/database-shell/`](packages/database-shell/) | `@bunny.net/database-shell` | Standalone interactive SQL shell for libSQL databases |
| [`packages/database-openapi/`](packages/database-openapi/) | `@bunny.net/database-openapi` | Generate OpenAPI 3.0 specs from a database schema |
| [`packages/database-rest/`](packages/database-rest/) | `@bunny.net/database-rest` | PostgREST-like REST API handler (database-agnostic) |
| [`packages/database-adapter-libsql/`](packages/database-adapter-libsql/) | `@bunny.net/database-adapter-libsql` | Bunny Database adapter for database-rest |
| [`packages/scriptable-dns-types/`](packages/scriptable-dns-types/) | `@bunny.net/scriptable-dns-types` | Ambient TypeScript types for the Scriptable DNS runtime |
| Package | Name | Description |
| ------------------------------------------------------------------------ | ------------------------------------ | ----------------------------------------------------------------------------------- |
| [`packages/cli/`](packages/cli/) | `@bunny.net/cli` | Command-line interface for bunny.net |
| [`packages/openapi-client/`](packages/openapi-client/) | `@bunny.net/openapi-client` | Standalone, type-safe OpenAPI client for bunny.net |
| [`packages/sandbox/`](packages/sandbox/) | `@bunny.net/sandbox` | Standalone sandbox SDK over Magic Containers and SSH |
| [`packages/config/`](packages/config/) | `@bunny.net/config` | Shared Zod schemas, types, and JSON Schema for `bunny.jsonc` (app + sites) |
| [`packages/database-client/`](packages/database-client/) | `@bunny.net/database-client` | Standalone `fetch`-only SQL client for server-side code (Edge Scripting, Bun, Node) |
| [`packages/database-shell/`](packages/database-shell/) | `@bunny.net/database-shell` | Standalone interactive SQL shell for libSQL databases |
| [`packages/database-openapi/`](packages/database-openapi/) | `@bunny.net/database-openapi` | Generate OpenAPI 3.0 specs from a database schema |
| [`packages/database-rest/`](packages/database-rest/) | `@bunny.net/database-rest` | PostgREST-like REST API handler (database-agnostic) |
| [`packages/database-adapter-libsql/`](packages/database-adapter-libsql/) | `@bunny.net/database-adapter-libsql` | Bunny Database adapter for database-rest |
| [`packages/scriptable-dns-types/`](packages/scriptable-dns-types/) | `@bunny.net/scriptable-dns-types` | Ambient TypeScript types for the Scriptable DNS runtime |

See each package's README for usage and API documentation.

Expand Down
28 changes: 18 additions & 10 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading