sproutboat 0.2.0 → 0.2.1
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 +10 -3
- package/SURFACE.md +2 -1
- package/package.json +2 -1
- package/src/compile.ts +27 -4
- package/src/surface.ts +1 -0
- package/src/toolchain.ts +68 -0
- package/vendor/uwebsockets-360c276d-musl.tar.xz +0 -0
package/README.md
CHANGED
|
@@ -70,9 +70,16 @@ binding, with an Astro UI and a runnable end-to-end harness.
|
|
|
70
70
|
## Requirements
|
|
71
71
|
|
|
72
72
|
- [Bun](https://bun.sh) 1.4+
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
|
|
74
|
+
`build` / `deploy` cross-compile the handler to a static `linux-x86_64` binary
|
|
75
|
+
with Porffor and Zig (Zig is fetched automatically on first use). The package
|
|
76
|
+
ships a prebuilt uWebSockets, so nothing else is compiled from source. No Docker,
|
|
77
|
+
no root. On Windows, build from WSL.
|
|
78
|
+
|
|
79
|
+
If that prebuilt is unusable (a `porffor` pin bump before the archive is
|
|
80
|
+
refreshed), the first build falls back to compiling uWebSockets locally, which
|
|
81
|
+
needs `git` and `make` on `PATH`. `SPROUTBOAT_UWS_TARBALL=<archive>` overrides
|
|
82
|
+
the shipped one.
|
|
76
83
|
|
|
77
84
|
## Limits (v1)
|
|
78
85
|
|
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.2.
|
|
6
|
+
**Package:** `sproutboat` 0.2.1 · runs on Bun (use `bunx`, not `npx`)
|
|
7
7
|
|
|
8
8
|
## Commands
|
|
9
9
|
|
|
@@ -30,6 +30,7 @@ usage: sproutboat <init [name] | check [project-dir] | build [project-dir] | dep
|
|
|
30
30
|
| `SPROUTBOAT_API_URL` | Control-plane URL. Overrides the saved active endpoint. |
|
|
31
31
|
| `SPROUTBOAT_TOKEN` | API token. Overrides the saved credential for the endpoint. |
|
|
32
32
|
| `SPROUTBOAT_ZIG` | Path to a Zig binary to use instead of downloading the pinned one. |
|
|
33
|
+
| `SPROUTBOAT_UWS_TARBALL` | Path to a prebuilt uWebSockets (x86_64-linux-musl) tarball to seed the Porffor cache with, instead of downloading it (removes the first-build git + make need). |
|
|
33
34
|
| `SPROUTBOAT_COMPILE_TIMEOUT_MS` | Porffor compile timeout in ms (default 600000). |
|
|
34
35
|
| `SPROUTBOAT_CONFIG_DIR` | Directory for credentials.json (default ~/.config/sproutboat). |
|
|
35
36
|
| `XDG_CONFIG_HOME` | Base for the default credentials dir when SPROUTBOAT_CONFIG_DIR is unset. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sproutboat",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Wrangler-shaped CLI for Sproutboat. Deploys workers to any control plane via --api-url / SPROUTBOAT_API_URL.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"files": [
|
|
26
26
|
"src",
|
|
27
27
|
"!src/*.test.ts",
|
|
28
|
+
"vendor",
|
|
28
29
|
"SURFACE.md"
|
|
29
30
|
],
|
|
30
31
|
"engines": {
|
package/src/compile.ts
CHANGED
|
@@ -4,14 +4,15 @@
|
|
|
4
4
|
* `zig cc -target x86_64-linux-musl` and statically links, so the same command
|
|
5
5
|
* works from macOS, Linux, or WSL with no Docker.
|
|
6
6
|
*
|
|
7
|
-
* One-time per machine: Porffor
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* One-time per machine: the uWebSockets tree Porffor links is unpacked into
|
|
8
|
+
* ~/.cache/porffor/deps/. `ensureUWebSockets()` extracts the prebuilt archive
|
|
9
|
+
* shipped in `vendor/` so this needs no `git` or `make`; if that archive is
|
|
10
|
+
* unusable it falls back to Porffor's own git + make path (needs both on PATH).
|
|
10
11
|
*/
|
|
11
12
|
import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
12
13
|
import { dirname, resolve } from "node:path";
|
|
13
14
|
import { ensurePorfforPatched } from "./patch-porffor";
|
|
14
|
-
import { porfforRoot } from "./toolchain";
|
|
15
|
+
import { ensureUWebSockets, porfforRoot, UwsUnavailableError } from "./toolchain";
|
|
15
16
|
|
|
16
17
|
const preludePath = new URL("./native-fetch-prelude.js", import.meta.url);
|
|
17
18
|
// The server honours $PORT at runtime (patches/porffor-render.patch); this baked
|
|
@@ -99,6 +100,28 @@ export type CompileInput = {
|
|
|
99
100
|
/** Compile `sourcePath` to a native binary at `outPath` (mode 0555). */
|
|
100
101
|
export async function compileWorker(input: CompileInput): Promise<void> {
|
|
101
102
|
await ensurePorfforPatched();
|
|
103
|
+
|
|
104
|
+
// Seed the Porffor uWebSockets cache from the prebuilt archive in `vendor/` so
|
|
105
|
+
// the first build needs no `git` / `make`. Fall back to Porffor's own git+make
|
|
106
|
+
// path if the archive is missing or fails its checksum.
|
|
107
|
+
try {
|
|
108
|
+
await ensureUWebSockets();
|
|
109
|
+
} catch (error) {
|
|
110
|
+
if (!(error instanceof UwsUnavailableError)) throw error;
|
|
111
|
+
const haveGit = Bun.which("git");
|
|
112
|
+
const haveMake = Bun.which("make");
|
|
113
|
+
if (haveGit && haveMake) {
|
|
114
|
+
console.warn(`prebuilt uWebSockets unusable (${error.message.split("\n")[0]}); falling back to git + make (slower, one-time)`);
|
|
115
|
+
} else {
|
|
116
|
+
const missing = [!haveGit && "git", !haveMake && "make"].filter(Boolean).join(" and ");
|
|
117
|
+
throw new Error(
|
|
118
|
+
`${error.message}\n\nThe prebuilt uWebSockets is unusable, and ${missing} ` +
|
|
119
|
+
`${missing.includes("and") ? "are" : "is"} not on PATH for the fallback build. ` +
|
|
120
|
+
`Install ${missing}, or set SPROUTBOAT_UWS_TARBALL to a valid archive.`,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
102
125
|
const outDir = dirname(input.outPath);
|
|
103
126
|
await mkdir(outDir, { recursive: true });
|
|
104
127
|
const generatedPath = resolve(outDir, "worker.generated.js");
|
package/src/surface.ts
CHANGED
|
@@ -25,6 +25,7 @@ export const ENV_VARS: readonly EnvVar[] = [
|
|
|
25
25
|
{ name: "SPROUTBOAT_API_URL", purpose: "Control-plane URL. Overrides the saved active endpoint." },
|
|
26
26
|
{ name: "SPROUTBOAT_TOKEN", purpose: "API token. Overrides the saved credential for the endpoint." },
|
|
27
27
|
{ name: "SPROUTBOAT_ZIG", purpose: "Path to a Zig binary to use instead of downloading the pinned one." },
|
|
28
|
+
{ name: "SPROUTBOAT_UWS_TARBALL", purpose: "Path to a prebuilt uWebSockets (x86_64-linux-musl) tarball to seed the Porffor cache with, instead of downloading it (removes the first-build git + make need)." },
|
|
28
29
|
{ name: "SPROUTBOAT_COMPILE_TIMEOUT_MS", purpose: "Porffor compile timeout in ms (default 600000)." },
|
|
29
30
|
{ name: "SPROUTBOAT_CONFIG_DIR", purpose: "Directory for credentials.json (default ~/.config/sproutboat)." },
|
|
30
31
|
{ name: "XDG_CONFIG_HOME", purpose: "Base for the default credentials dir when SPROUTBOAT_CONFIG_DIR is unset." },
|
package/src/toolchain.ts
CHANGED
|
@@ -30,6 +30,14 @@ const PORFFOR_COMMIT = "a415d19";
|
|
|
30
30
|
// uWebSockets commit Porffor alpha-4 fetches for the native-fetch server. Read
|
|
31
31
|
// from node_modules/porffor at build time; this is the fallback for the stamp.
|
|
32
32
|
const UWS_COMMIT = "360c276d";
|
|
33
|
+
const UWS_COMMIT_FULL = "360c276d609d59af56ae6932adb95154ace9f15f";
|
|
34
|
+
|
|
35
|
+
// `vendor/uwebsockets-<UWS_COMMIT>-musl.tar.xz` ships in the package: the
|
|
36
|
+
// checked-out, patched, `zig cc -target x86_64-linux-musl`-built uWebSockets
|
|
37
|
+
// tree (headers + `uSockets/uSockets.a`). Regenerate + re-pin the sha whenever
|
|
38
|
+
// the `porffor` pin (and thus UWS_COMMIT_FULL) changes — `bun tools/prebuild-uws.ts`
|
|
39
|
+
// or the `uws-prebuild` workflow.
|
|
40
|
+
const UWS_TARBALL_SHA256 = "e83736f3f8cf9d56a1ebe6ea61625a7af12386763374d47c14cff472ada7484a";
|
|
33
41
|
|
|
34
42
|
function platformKey(): string {
|
|
35
43
|
const arch = process.arch === "arm64" ? "aarch64" : process.arch === "x64" ? "x86_64" : null;
|
|
@@ -82,6 +90,66 @@ export async function ensureZig(): Promise<string> {
|
|
|
82
90
|
return bin;
|
|
83
91
|
}
|
|
84
92
|
|
|
93
|
+
function uwsCommitFull(): string {
|
|
94
|
+
try {
|
|
95
|
+
const src = readFileSync(resolve(porfforRoot(), "compiler/uwebsockets.js"), "utf8");
|
|
96
|
+
return /UWS_COMMIT\s*=\s*['"]([0-9a-f]{40})/.exec(src)?.[1] ?? UWS_COMMIT_FULL;
|
|
97
|
+
} catch {
|
|
98
|
+
return UWS_COMMIT_FULL;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Thrown when the prebuilt uWebSockets archive is missing or fails its checksum. */
|
|
103
|
+
export class UwsUnavailableError extends Error {}
|
|
104
|
+
|
|
105
|
+
/** Path to the vendored prebuilt archive for the given short commit. */
|
|
106
|
+
export function uwsVendorArchive(short: string): string {
|
|
107
|
+
return resolve(import.meta.dir, "..", "vendor", `uwebsockets-${short}-musl.tar.xz`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Seed `~/.cache/porffor/deps/uWebSockets-<commit>-musl/` with the checked-out,
|
|
112
|
+
* patched, `x86_64-linux-musl`-built uWebSockets tree so Porffor's own
|
|
113
|
+
* `ensureUWebSockets` / `ensureUSocketsBuilt` short-circuit — the first build
|
|
114
|
+
* then needs no `git` and no `make`, only Zig.
|
|
115
|
+
*
|
|
116
|
+
* The archive ships in the package (`vendor/`). No-ops if the cache is already
|
|
117
|
+
* populated. Throws `UwsUnavailableError` if the archive is missing or fails its
|
|
118
|
+
* checksum; the caller decides whether to fall back to Porffor's git + make path.
|
|
119
|
+
*
|
|
120
|
+
* `SPROUTBOAT_UWS_TARBALL=/path/to/archive.tar.xz` overrides the vendored one.
|
|
121
|
+
*/
|
|
122
|
+
export async function ensureUWebSockets(): Promise<void> {
|
|
123
|
+
const commit = uwsCommitFull();
|
|
124
|
+
const short = commit.slice(0, 8);
|
|
125
|
+
const depsRoot = resolve(homedir(), ".cache/porffor/deps");
|
|
126
|
+
const dir = resolve(depsRoot, `uWebSockets-${commit}-musl`);
|
|
127
|
+
if (existsSync(resolve(dir, "src/App.h")) && existsSync(resolve(dir, "uSockets/uSockets.a"))) return;
|
|
128
|
+
|
|
129
|
+
const archive = process.env.SPROUTBOAT_UWS_TARBALL || uwsVendorArchive(short);
|
|
130
|
+
if (!existsSync(archive)) {
|
|
131
|
+
throw new UwsUnavailableError(
|
|
132
|
+
process.env.SPROUTBOAT_UWS_TARBALL
|
|
133
|
+
? `SPROUTBOAT_UWS_TARBALL=${archive} does not exist`
|
|
134
|
+
: `no vendored uWebSockets archive at ${archive} (porffor pin moved? run \`bun tools/prebuild-uws.ts\`)`,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
if (!process.env.SPROUTBOAT_UWS_TARBALL) {
|
|
138
|
+
const actual = await sha256File(archive);
|
|
139
|
+
if (actual !== UWS_TARBALL_SHA256) {
|
|
140
|
+
throw new UwsUnavailableError(`vendored uWebSockets sha256 mismatch\n expected ${UWS_TARBALL_SHA256}\n got ${actual}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
await mkdir(dir, { recursive: true });
|
|
145
|
+
const untar = Bun.spawn(["tar", "-xJf", archive, "-C", dir, "--strip-components=1"], { stdout: "pipe", stderr: "pipe" });
|
|
146
|
+
const [code, err] = await Promise.all([untar.exited, new Response(untar.stderr).text()]);
|
|
147
|
+
if (code !== 0) {
|
|
148
|
+
await rm(dir, { recursive: true, force: true });
|
|
149
|
+
throw new UwsUnavailableError(`could not extract vendored uWebSockets: ${err.trim()}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
85
153
|
/** Directory holding node_modules/porffor (walks up from this file). */
|
|
86
154
|
export function porfforRoot(start = import.meta.dir): string {
|
|
87
155
|
let dir = start;
|
|
Binary file
|