sproutboat 0.4.11 → 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 +74 -55
- package/SURFACE.md +13 -7
- package/package.json +28 -20
- package/src/assets.ts +29 -9
- package/src/broker.ts +188 -87
- package/src/build.ts +42 -7
- package/src/bundle.ts +70 -0
- package/src/compile.ts +37 -9
- package/src/config.ts +72 -30
- package/src/credentials.ts +19 -2
- package/src/dev.ts +213 -0
- package/src/json.ts +38 -0
- package/src/main.ts +473 -126
- package/src/manifest.ts +81 -14
- package/src/native-fetch-prelude.js +274 -135
- package/src/patch-porffor.ts +5 -2
- package/src/report.ts +39 -17
- package/src/source.ts +20 -2
- package/src/style.ts +9 -6
- package/src/surface.ts +195 -42
- package/src/toolchain.ts +21 -7
- package/src/update-check.ts +33 -9
- package/src/wrap.ts +71 -17
package/README.md
CHANGED
|
@@ -1,16 +1,34 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8
|
-
|
|
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
|
-
bunx sproutboat login --api-url https://control.example.com # one browser approval
|
|
12
23
|
bunx sproutboat init hello
|
|
13
24
|
cd hello
|
|
25
|
+
bunx sproutboat dev # runs it right here, no control plane needed
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Happy with it? Ship it:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
bunx sproutboat login --api-url https://control.example.com # one browser approval
|
|
14
32
|
bunx sproutboat deploy
|
|
15
33
|
```
|
|
16
34
|
|
|
@@ -20,33 +38,30 @@ Or install it once and drop the `bunx`:
|
|
|
20
38
|
bun add -g sproutboat # then: sproutboat deploy, sproutboat tail, ...
|
|
21
39
|
```
|
|
22
40
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
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
|
|
26
42
|
`~/.config/sproutboat/credentials.json`, keyed by API URL, so you can hold
|
|
27
|
-
logins for several instances at once. For CI or an agent, skip
|
|
28
|
-
|
|
29
|
-
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`.
|
|
30
45
|
|
|
31
46
|
## Commands
|
|
32
47
|
|
|
48
|
+
The everyday loop:
|
|
49
|
+
|
|
33
50
|
| Command | What it does |
|
|
34
51
|
| --- | --- |
|
|
35
52
|
| `init [name]` | Scaffold `sproutboat.jsonc` + `src/index.js` |
|
|
36
|
-
| `
|
|
37
|
-
| `
|
|
38
|
-
| `
|
|
39
|
-
| `
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
| `resource [list \| create <kind> <name> \| rename <id> <name> \| delete <id>]` | Account-level KV / D1 / R2 / queue stores |
|
|
45
|
-
| `domains [list \| add <host> \| verify <host> \| rm <host>]` | Attach your own hostname (TXT + A, apex allowed) |
|
|
46
|
-
| `delete --yes` | Delete the project, every version, and its route |
|
|
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.
|
|
47
61
|
|
|
48
62
|
Run `sproutboat` with no arguments for the grouped list.
|
|
49
|
-
[`SURFACE.md`](SURFACE.md) is the generated inventory
|
|
63
|
+
[`SURFACE.md`](SURFACE.md) is the generated inventory: every command, every
|
|
64
|
+
argument, every env var, kept honest by a drift test.
|
|
50
65
|
|
|
51
66
|
## Config
|
|
52
67
|
|
|
@@ -73,43 +88,47 @@ Run `sproutboat` with no arguments for the grouped list.
|
|
|
73
88
|
```
|
|
74
89
|
|
|
75
90
|
A bare `"CACHE"` binding is auto-provisioned on `deploy`: the CLI creates an
|
|
76
|
-
account-level resource
|
|
77
|
-
store
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
`scheduled(event)` / `queue(batch)` handlers and Durable Object classes
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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.
|
|
100
|
+
|
|
101
|
+
`sproutboat dev` runs against a real local broker, so bindings behave like
|
|
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.
|
|
86
106
|
|
|
87
107
|
## Requirements
|
|
88
108
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
with Porffor and Zig (the CLI fetches Zig automatically on first use). The
|
|
93
|
-
package ships a prebuilt uWebSockets and compiles nothing else from source. No
|
|
94
|
-
Docker, no root. On Windows, build from WSL.
|
|
95
|
-
|
|
96
|
-
If that prebuilt is unusable (a `porffor` pin bump before the archive is
|
|
97
|
-
refreshed), the first build falls back to compiling uWebSockets locally, which
|
|
98
|
-
needs `git` and `make` on `PATH`. `SPROUTBOAT_UWS_TARBALL=<archive>` overrides
|
|
99
|
-
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.
|
|
100
112
|
|
|
101
113
|
## Limits
|
|
102
114
|
|
|
103
115
|
Capability profile `http-sync-v0`: one synchronous `fetch` handler, optional
|
|
104
116
|
`scheduled` / `queue` handlers, no streaming, no WebSockets. Binding values are
|
|
105
|
-
text/JSON
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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).
|
|
111
130
|
|
|
112
|
-
|
|
131
|
+
## Contributing
|
|
113
132
|
|
|
114
|
-
|
|
115
|
-
|
|
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.
|
|
6
|
+
**Package:** `sproutboat` 0.6.0 · runs on Bun (use `bunx`, not `npx`)
|
|
7
7
|
|
|
8
8
|
## Commands
|
|
9
9
|
|
|
@@ -11,19 +11,25 @@
|
|
|
11
11
|
| --- | --- | --- |
|
|
12
12
|
| `init` | `[name]` | Scaffold sproutboat.jsonc + src/index.js in ./<name>. |
|
|
13
13
|
| `check` | `[project-dir]` | Validate the config and entry point without building. |
|
|
14
|
-
| `
|
|
14
|
+
| `dev` | `[project-dir] [--port <n>] [--no-watch]` | Run the project on this machine against a real broker, rebuilding on save. |
|
|
15
|
+
| `build` | `[project-dir] [--target host]` | Cross-compile the native-fetch sprout (Porffor + Zig). `--target host` builds for this machine instead, to run locally — not deployable. |
|
|
15
16
|
| `deploy` | `[project-dir] [--dry-run] [--artifact <dir>] [--no-wait] [--no-provision]` | Build (unless --artifact), auto-provision id-less storage bindings and pin their ids into sproutboat.jsonc, print the report, upload, wait until the URL serves. The control plane skips an upload that matches the live artifact byte-for-byte. --dry-run stops before upload; --no-wait skips the health check; --no-provision leaves id-less bindings as ephemeral deploy-scoped stores. |
|
|
16
|
-
| `versions` |
|
|
17
|
+
| `versions` | `<list | view <version-id>> [project-dir]` | List the project's deployed versions, or show one version's artifact and bindings. |
|
|
17
18
|
| `rollback` | `<version-id> [project-dir]` | Re-activate a previous version. |
|
|
18
19
|
| `tail` | `[project-dir] [--sprout]` | Print recent request logs; --sprout prints the running sprout + broker stdout/stderr instead. |
|
|
19
|
-
| `
|
|
20
|
-
| `
|
|
21
|
-
| `
|
|
20
|
+
| `kv` | `<list | create <name> | info <name> | rename <name> <new> | delete <name>>` | KV namespaces. `create` prints the id to bind from sproutboat.jsonc. |
|
|
21
|
+
| `d1` | `<list | create <name> | info <name> | rename <name> <new> | delete <name>>` | D1 databases. `create` prints the id to bind from sproutboat.jsonc. |
|
|
22
|
+
| `r2` | `<list | create <name> | info <name> | rename <name> <new> | delete <name>>` | R2 buckets. `create` prints the id to bind from sproutboat.jsonc. |
|
|
23
|
+
| `queues` | `<list | create <name> | info <name> | rename <name> <new> | delete <name>>` | Queues. `create` prints the id to bind from sproutboat.jsonc; consumers are not implemented yet. |
|
|
24
|
+
| `domains` | `<list | add <host> | verify <host> | delete <host>> [project-dir]` | Attach a custom domain to the project (TXT-verified). No sub-command lists. |
|
|
25
|
+
| `secrets` | `<list | put <NAME> [--value <value>] | delete <NAME>> [project-dir]` | Manage encrypted project secrets (read as env.NAME). `put` reads the value from stdin unless --value is given, so it stays out of shell history; applies on next deploy. |
|
|
22
26
|
| `delete` | `[project-dir] [--name <project>] --yes` | Delete the project, every version, and its route. |
|
|
23
27
|
| `login` | `[--api-url <url>] [--token <token>]` | Device-code browser flow, or store <token> for <url> directly. |
|
|
28
|
+
| `logout` | `[--api-url <url>]` | Forget the stored credential for the active endpoint, or for <url>. |
|
|
29
|
+
| `whoami` | — | Show the active endpoint and the account the stored token belongs to. |
|
|
24
30
|
|
|
25
31
|
```
|
|
26
|
-
usage: sproutboat <init [name] | check [project-dir] | build [project-dir] | deploy [project-dir] [--dry-run] [--artifact <dir>] [--no-wait] [--no-provision] | versions list [project-dir] | rollback <version-id> [project-dir] | tail [project-dir] [--sprout] |
|
|
32
|
+
usage: sproutboat <init [name] | check [project-dir] | dev [project-dir] [--port <n>] [--no-watch] | build [project-dir] [--target host] | deploy [project-dir] [--dry-run] [--artifact <dir>] [--no-wait] [--no-provision] | versions <list | view <version-id>> [project-dir] | rollback <version-id> [project-dir] | tail [project-dir] [--sprout] | kv <list | create <name> | info <name> | rename <name> <new> | delete <name>> | d1 <list | create <name> | info <name> | rename <name> <new> | delete <name>> | r2 <list | create <name> | info <name> | rename <name> <new> | delete <name>> | queues <list | create <name> | info <name> | rename <name> <new> | delete <name>> | domains <list | add <host> | verify <host> | delete <host>> [project-dir] | secrets <list | put <NAME> [--value <value>] | delete <NAME>> [project-dir] | delete [project-dir] [--name <project>] --yes | login [--api-url <url>] [--token <token>] | logout [--api-url <url>] | whoami>
|
|
27
33
|
```
|
|
28
34
|
|
|
29
35
|
## Environment variables
|
package/package.json
CHANGED
|
@@ -1,27 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sproutboat",
|
|
3
|
-
"version": "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
|
-
"
|
|
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,17 +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",
|
|
44
|
+
"lint": "oxlint .",
|
|
45
|
+
"fmt": "oxfmt .",
|
|
46
|
+
"fmt:check": "oxfmt --check .",
|
|
46
47
|
"test": "bun test",
|
|
47
48
|
"example:kitchen-sink": "bun examples/kitchen-sink/harness.ts",
|
|
48
49
|
"surface": "UPDATE_SURFACE=1 bun test src/surface.test.ts",
|
|
@@ -55,7 +56,14 @@
|
|
|
55
56
|
"porffor": "github:CanadaHonk/porffor#alpha-4"
|
|
56
57
|
},
|
|
57
58
|
"devDependencies": {
|
|
59
|
+
"@oxlint/plugins": "1.81.0",
|
|
58
60
|
"@types/bun": "1.4.0",
|
|
61
|
+
"lefthook": "2.1.12",
|
|
62
|
+
"oxfmt": "0.66.0",
|
|
63
|
+
"oxlint": "1.81.0",
|
|
59
64
|
"typescript": "5.9.2"
|
|
65
|
+
},
|
|
66
|
+
"engines": {
|
|
67
|
+
"bun": ">=1.4.0"
|
|
60
68
|
}
|
|
61
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"],
|
|
25
|
-
["
|
|
26
|
-
["
|
|
27
|
-
["
|
|
28
|
-
["
|
|
29
|
-
["
|
|
30
|
-
["
|
|
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()) {
|
|
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}`] = {
|
|
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, "");
|