sproutboat 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,11 +1,23 @@
1
- # sproutboat
1
+ <picture>
2
+ <source media="(prefers-color-scheme: dark)" srcset="docs/logo-dark.svg">
3
+ <img src="docs/logo-light.svg" alt="sproutboat" width="220" height="32">
4
+ </picture>
2
5
 
3
- The CLI for [Sproutboat](https://github.com/baronunread/sproutboat). It is a
4
- Wrangler-shaped tool that compiles a `fetch` handler to a native binary and
5
- ships it to any Sproutboat control plane. MIT licensed.
6
+ https://sproutboat.com
6
7
 
7
- Full reference: [sproutboat.com/docs](https://sproutboat.com/docs)
8
- (plain text for agents: [sproutboat.com/llms.txt](https://sproutboat.com/llms.txt)).
8
+ The CLI for [Sproutboat](https://github.com/baronunread/sproutboat). Compiles a
9
+ `fetch` handler to a native binary and ships it to any Sproutboat control plane.
10
+
11
+ ## Overview
12
+
13
+ Wrangler-shaped, MIT licensed. `build` and `deploy` cross-compile your handler
14
+ to a static `linux-x86_64` binary with [Porffor](https://porffor.dev) and Zig —
15
+ no Docker, no root, nothing to run as a daemon.
16
+
17
+ Full reference: [sproutboat.com/docs](https://sproutboat.com/docs) (plain text
18
+ for agents: [sproutboat.com/llms.txt](https://sproutboat.com/llms.txt)).
19
+
20
+ ## Using
9
21
 
10
22
  ```sh
11
23
  bunx sproutboat init hello
@@ -26,45 +38,30 @@ Or install it once and drop the `bunx`:
26
38
  bun add -g sproutboat # then: sproutboat deploy, sproutboat tail, ...
27
39
  ```
28
40
 
29
- Want it shorter? Alias it yourself: `alias sprout='sproutboat'`.
30
-
31
- The `login` browser step is one-time. It writes a long-lived token to
41
+ `login` is one-time. It writes a long-lived token to
32
42
  `~/.config/sproutboat/credentials.json`, keyed by API URL, so you can hold
33
- logins for several instances at once. For CI or an agent, skip `login`
34
- entirely and set `SPROUTBOAT_API_URL` + `SPROUTBOAT_TOKEN` (copy the token out
35
- of that file).
43
+ logins for several instances at once. For CI or an agent, skip it and set
44
+ `SPROUTBOAT_API_URL` + `SPROUTBOAT_TOKEN`.
36
45
 
37
46
  ## Commands
38
47
 
48
+ The everyday loop:
49
+
39
50
  | Command | What it does |
40
51
  | --- | --- |
41
52
  | `init [name]` | Scaffold `sproutboat.jsonc` + `src/index.js` |
42
- | `check` | Validate the config and entry point |
43
- | `dev [--port <n>] [--no-watch]` | Run the project on this machine against a real broker, rebuilding on save |
44
- | `build [--target host]` | Cross-compile the sprout binary (Porffor + Zig); `--target host` builds for this machine instead, for `dev` not deployable |
45
- | `deploy [--dry-run] [--no-wait] [--no-provision] [--artifact <dir>]` | Build, auto-provision id-less storage bindings, upload, wait until the URL serves |
46
- | `login [--api-url <url>] [--token <token>]` | Browser device flow, or store a token directly |
47
- | `tail [name] [--sprout]` | Recent request logs; `--sprout` streams the running sprout + broker output |
48
- | `versions <list \| view <id>>` | Deployed versions, or one version's artifact and bindings |
49
- | `rollback <id>` | Activate a previous version |
50
- | `logout [--api-url <url>]` | Forget the stored credential |
51
- | `whoami` | Active endpoint and the account the token belongs to |
52
- | `kv <list \| create \| info \| rename \| delete>` | KV namespaces |
53
- | `d1 <list \| create \| info \| rename \| delete>` | D1 databases |
54
- | `r2 <list \| create \| info \| rename \| delete>` | R2 buckets |
55
- | `queues <list \| create \| info \| rename \| delete>` | Queues |
56
- | `secrets <list \| put <NAME> [--value <v>] \| delete <NAME>>` | Encrypted project secrets, read as `env.NAME` |
57
- | `domains <list \| add <host> \| verify <host> \| delete <host>>` | Attach your own hostname (TXT + A, apex allowed) |
58
- | `delete --yes` | Delete the project, every version, and its route |
59
-
60
- Each storage product is its own command with the same five verbs. Wrangler nests
61
- two of its four (`kv namespace create`, `r2 bucket create`) and leaves the other
62
- two flat; the nesting separates a container from its contents, which the verb
63
- already does, so ours are uniform. Contents get their own noun when they arrive
64
- (`kv key get`, `r2 object put`).
53
+ | `dev [--port <n>]` | Run it here against a real broker, rebuilding on save |
54
+ | `deploy` | Build, provision bindings, upload, wait until the URL serves |
55
+ | `tail [--sprout]` | Recent request logs, or the running sprout's output |
56
+ | `rollback <id>` | Re-activate a previous version |
57
+
58
+ Then `check`, `build`, `versions`, `delete`, `login` / `logout` / `whoami`, and
59
+ one command per storage product `kv`, `d1`, `r2`, `queues`, `secrets`,
60
+ `domains` each with the same five verbs.
65
61
 
66
62
  Run `sproutboat` with no arguments for the grouped list.
67
- [`SURFACE.md`](SURFACE.md) is the generated inventory of every command and env var.
63
+ [`SURFACE.md`](SURFACE.md) is the generated inventory: every command, every
64
+ argument, every env var, kept honest by a drift test.
68
65
 
69
66
  ## Config
70
67
 
@@ -91,55 +88,47 @@ Run `sproutboat` with no arguments for the grouped list.
91
88
  ```
92
89
 
93
90
  A bare `"CACHE"` binding is auto-provisioned on `deploy`: the CLI creates an
94
- account-level resource, writes its id back into `sproutboat.jsonc`, and the
95
- store then survives redeploys. Pass `--no-provision` to keep it a throwaway
96
- per-deploy store instead, or `sproutboat kv create <name>` (or `d1`/`r2`/`queues`) to make one up front
97
- and share its id across projects.
98
-
99
- The handler is `export default { fetch(request) }`; it may import from other
100
- files in the project and from its own `node_modules`, and optionally export
101
- `scheduled(event)` / `queue(batch)` handlers and Durable Object classes.
102
- `env` is a global (not a parameter), and every binding call is synchronous.
103
- See [`examples/kitchen-sink/`](examples/kitchen-sink) for one app that uses
104
- every binding.
91
+ account-level resource and writes its id back into `sproutboat.jsonc`, so the
92
+ store survives redeploys. `--no-provision` keeps it a throwaway per-deploy
93
+ store instead.
94
+
95
+ The handler is `export default { fetch(request) }`. It may import from other
96
+ files in the project and from its own `node_modules`, and may also export
97
+ `scheduled(event)` / `queue(batch)` handlers and Durable Object classes. `env`
98
+ is a global, not a parameter, and every binding call is synchronous.
99
+ [`examples/kitchen-sink/`](examples/kitchen-sink) uses every binding.
105
100
 
106
101
  `sproutboat dev` runs against a real local broker, so bindings behave like
107
- production without a deploy — except `secrets`, which live only in the
108
- control plane. Give it a value with a `.dev.vars` file next to
109
- `sproutboat.jsonc` (`API_KEY=whatever`, one per line, gitignore it):
110
- `env.API_KEY` then resolves to that value under `dev` only.
102
+ production without a deploy — except `secrets`, which live only in the control
103
+ plane. Put values in a `.dev.vars` file next to `sproutboat.jsonc`
104
+ (`API_KEY=whatever`, one per line, gitignored) and `env.API_KEY` resolves under
105
+ `dev` only.
111
106
 
112
107
  ## Requirements
113
108
 
114
- - [Bun](https://bun.sh) 1.4+
115
-
116
- `build` / `deploy` cross-compile the handler to a static `linux-x86_64` binary
117
- with Porffor and Zig (the CLI fetches Zig automatically on first use). The
118
- package ships a prebuilt uWebSockets and compiles nothing else from source. No
119
- Docker, no root. On Windows, build from WSL.
120
-
121
- If that prebuilt is unusable (a `porffor` pin bump before the archive is
122
- refreshed), the first build falls back to compiling uWebSockets locally, which
123
- needs `git` and `make` on `PATH`. `SPROUTBOAT_UWS_TARBALL=<archive>` overrides
124
- the shipped one.
109
+ [Bun](https://bun.sh) 1.4+. The CLI fetches Zig automatically on first use and
110
+ ships a prebuilt uWebSockets, so it compiles nothing else from source. On
111
+ Windows, build from WSL.
125
112
 
126
113
  ## Limits
127
114
 
128
115
  Capability profile `http-sync-v0`: one synchronous `fetch` handler, optional
129
116
  `scheduled` / `queue` handlers, no streaming, no WebSockets. Binding values are
130
- text/JSON and travel one at a time over the loopback frame (32 MiB cap);
131
- large-object R2 is [#56](https://github.com/baronunread/sproutboat/issues/56).
132
- The sprout upload caps at 16 MiB, assets at 64 MiB / 4096 files. The entry
133
- point is bundled before it compiles, so relative imports and npm dependencies
134
- from the project's own `node_modules` work; CommonJS `require`, dynamic
135
- `import()`, and any `process`/`Bun`/`Deno`/`node:*` reached even through a
136
- dependency do not. `new Proxy(...)` compiles but its traps are silently
137
- ignored by Porffor alpha, so it's rejected too. A parsed `Date` with a numeric
138
- timezone offset (`+02:00`) currently comes out wrong; plain ISO-8601 UTC
139
- strings are fine. `sproutboat check` catches most of this before a build.
140
- Full list at [sproutboat.com/docs](https://sproutboat.com/docs).
141
-
142
- ---
143
-
144
- Contributors: [`MIGRATION.md`](MIGRATION.md) maps this repo back to the monorepo.
145
- [`SURFACE.md`](SURFACE.md) is a generated inventory of every command and env var.
117
+ text/JSON, one at a time over a 32 MiB loopback frame. Sprout uploads cap at
118
+ 16 MiB, assets at 64 MiB / 4096 files.
119
+
120
+ Porffor is pre-1.0, so some JavaScript does not survive the compile — CommonJS
121
+ `require`, dynamic `import()`, anything reaching `process`/`Bun`/`Deno`/`node:*`,
122
+ and `new Proxy(...)` (it compiles, then silently ignores every trap).
123
+ `sproutboat check` catches most of it before a build. The full list, and the
124
+ current sharp edges, are at [sproutboat.com/docs](https://sproutboat.com/docs).
125
+
126
+ ## Bugs
127
+
128
+ File issues at
129
+ [baronunread/sproutboat/issues](https://github.com/baronunread/sproutboat/issues).
130
+
131
+ ## Contributing
132
+
133
+ PRs welcome. [`MIGRATION.md`](MIGRATION.md) maps this repo back to the
134
+ monorepo it was extracted from.
package/SURFACE.md CHANGED
@@ -3,7 +3,7 @@
3
3
  > Generated by `src/surface.test.ts` from `src/surface.ts` + the pinned
4
4
  > toolchain constants. Do not edit by hand — run `UPDATE_SURFACE=1 bun test`.
5
5
 
6
- **Package:** `sproutboat` 0.5.0 · runs on Bun (use `bunx`, not `npx`)
6
+ **Package:** `sproutboat` 0.6.0 · runs on Bun (use `bunx`, not `npx`)
7
7
 
8
8
  ## Commands
9
9
 
package/package.json CHANGED
@@ -1,27 +1,33 @@
1
1
  {
2
2
  "name": "sproutboat",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Wrangler-shaped CLI for Sproutboat. Deploys workers to any control plane via --api-url / SPROUTBOAT_API_URL.",
5
- "license": "MIT",
6
- "type": "module",
7
- "homepage": "https://github.com/baronunread/sproutboat-cli",
8
- "repository": {
9
- "type": "git",
10
- "url": "git+https://github.com/baronunread/sproutboat-cli.git"
11
- },
12
- "bugs": "https://github.com/baronunread/sproutboat-cli/issues",
13
5
  "keywords": [
14
- "sproutboat",
15
6
  "cli",
16
7
  "deploy",
8
+ "edge",
17
9
  "porffor",
18
10
  "serverless",
19
- "edge",
11
+ "sproutboat",
20
12
  "wrangler"
21
13
  ],
14
+ "homepage": "https://github.com/baronunread/sproutboat-cli",
15
+ "bugs": "https://github.com/baronunread/sproutboat-cli/issues",
16
+ "license": "MIT",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/baronunread/sproutboat-cli.git"
20
+ },
22
21
  "bin": {
23
22
  "sproutboat": "src/main.ts"
24
23
  },
24
+ "files": [
25
+ "src",
26
+ "!src/*.test.ts",
27
+ "vendor",
28
+ "SURFACE.md"
29
+ ],
30
+ "type": "module",
25
31
  "exports": {
26
32
  "./runtime/config": "./src/config.ts",
27
33
  "./runtime/source": "./src/source.ts",
@@ -32,18 +38,12 @@
32
38
  "./runtime/prelude": "./src/native-fetch-prelude.js",
33
39
  "./package.json": "./package.json"
34
40
  },
35
- "files": [
36
- "src",
37
- "!src/*.test.ts",
38
- "vendor",
39
- "SURFACE.md"
40
- ],
41
- "engines": {
42
- "bun": ">=1.4.0"
43
- },
44
41
  "scripts": {
42
+ "prepare": "lefthook install || true",
45
43
  "typecheck": "tsc --noEmit",
46
44
  "lint": "oxlint .",
45
+ "fmt": "oxfmt .",
46
+ "fmt:check": "oxfmt --check .",
47
47
  "test": "bun test",
48
48
  "example:kitchen-sink": "bun examples/kitchen-sink/harness.ts",
49
49
  "surface": "UPDATE_SURFACE=1 bun test src/surface.test.ts",
@@ -58,7 +58,12 @@
58
58
  "devDependencies": {
59
59
  "@oxlint/plugins": "1.81.0",
60
60
  "@types/bun": "1.4.0",
61
+ "lefthook": "2.1.12",
62
+ "oxfmt": "0.66.0",
61
63
  "oxlint": "1.81.0",
62
64
  "typescript": "5.9.2"
65
+ },
66
+ "engines": {
67
+ "bun": ">=1.4.0"
63
68
  }
64
69
  }
package/src/assets.ts CHANGED
@@ -21,13 +21,26 @@ export type AssetManifest = {
21
21
  };
22
22
 
23
23
  const TYPES = new Map<string, string>([
24
- ["html", "text/html; charset=utf-8"], ["css", "text/css; charset=utf-8"],
25
- ["js", "text/javascript; charset=utf-8"], ["mjs", "text/javascript; charset=utf-8"],
26
- ["json", "application/json; charset=utf-8"], ["map", "application/json; charset=utf-8"],
27
- ["txt", "text/plain; charset=utf-8"], ["xml", "application/xml; charset=utf-8"],
28
- ["svg", "image/svg+xml"], ["png", "image/png"], ["jpg", "image/jpeg"], ["jpeg", "image/jpeg"],
29
- ["gif", "image/gif"], ["webp", "image/webp"], ["avif", "image/avif"], ["ico", "image/x-icon"],
30
- ["woff2", "font/woff2"], ["woff", "font/woff"], ["ttf", "font/ttf"], ["wasm", "application/wasm"],
24
+ ["html", "text/html; charset=utf-8"],
25
+ ["css", "text/css; charset=utf-8"],
26
+ ["js", "text/javascript; charset=utf-8"],
27
+ ["mjs", "text/javascript; charset=utf-8"],
28
+ ["json", "application/json; charset=utf-8"],
29
+ ["map", "application/json; charset=utf-8"],
30
+ ["txt", "text/plain; charset=utf-8"],
31
+ ["xml", "application/xml; charset=utf-8"],
32
+ ["svg", "image/svg+xml"],
33
+ ["png", "image/png"],
34
+ ["jpg", "image/jpeg"],
35
+ ["jpeg", "image/jpeg"],
36
+ ["gif", "image/gif"],
37
+ ["webp", "image/webp"],
38
+ ["avif", "image/avif"],
39
+ ["ico", "image/x-icon"],
40
+ ["woff2", "font/woff2"],
41
+ ["woff", "font/woff"],
42
+ ["ttf", "font/ttf"],
43
+ ["wasm", "application/wasm"],
31
44
  ["webmanifest", "application/manifest+json"],
32
45
  ]);
33
46
 
@@ -68,10 +81,17 @@ export function walkAssets(dir: string) {
68
81
  if (ent.name.startsWith(".")) continue;
69
82
  const childAbs = join(abs, ent.name);
70
83
  const childRel = posix.join(rel, ent.name);
71
- if (ent.isDirectory()) { recurse(childAbs, childRel); continue; }
84
+ if (ent.isDirectory()) {
85
+ recurse(childAbs, childRel);
86
+ continue;
87
+ }
72
88
  if (!ent.isFile()) continue;
73
89
  const body = readFileSync(childAbs);
74
- out[`/${childRel}`] = { hash: createHash("sha256").update(body).digest("hex"), size: body.byteLength, type: contentType(ent.name) };
90
+ out[`/${childRel}`] = {
91
+ hash: createHash("sha256").update(body).digest("hex"),
92
+ size: body.byteLength,
93
+ type: contentType(ent.name),
94
+ };
75
95
  }
76
96
  };
77
97
  recurse(dir, "");