@sproutboat/runtime 0.12.0 → 0.13.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/CHANGELOG.md +15 -2
- package/package.json +1 -1
- package/src/ingress-utf8.test.js +41 -0
- package/src/native-fetch-prelude.js +53 -16
- package/src/source.ts +1 -1
- package/src/workers-handler.test.js +59 -0
- package/test/native-crypto.ts +8 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @sproutboat/runtime
|
|
2
2
|
|
|
3
|
+
## 0.13.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 34083e1: Pass `(request, env, ctx)` to handlers built with compatibility dates from 2026-09-28. Older builds retain `(request, ctx)`. Add `Response.redirect()` for native fetch and decode request text as UTF-8 while preserving raw body bytes. The matching compiler Unicode fix shipped in `@sproutboat/toolchain@0.4.14`.
|
|
8
|
+
|
|
9
|
+
## 0.12.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- ed9640a: Point Porffor patch diagnostics, runtime comments, and historical notes to the
|
|
14
|
+
shared upstream notes in the Sproutboat platform repository.
|
|
15
|
+
|
|
3
16
|
## 0.12.0
|
|
4
17
|
|
|
5
18
|
### Minor Changes
|
|
@@ -131,11 +144,11 @@
|
|
|
131
144
|
|
|
132
145
|
Verified end-to-end against both transports (kitchen-sink's full conformance suite, 24/24 standalone + 27/27 broker) and with new unit tests for file-backed persistence, the `:memory:` fallback, and a real binary round-trip over the v1 wire protocol.
|
|
133
146
|
|
|
134
|
-
**Known limitation found while verifying this, not fixed here:** a binary `put()` body (bytes ≥ 0x80) is corrupted on the way *in*, before it ever reaches storage
|
|
147
|
+
**Known limitation found while verifying this, not fixed here:** a binary `put()` body (bytes ≥ 0x80) is corrupted on the way *in*, before it ever reaches storage: `porf_native_fetch_read_value` (the function every inline-C string read in the runtime goes through) always UTF-8-encodes a Latin-1-range string on read, which is correct for genuine text but wrong for opaque bytes coming off a raw HTTP body. This predates this change (the old SQLite-blob path had the identical corruption) and is not something the storage backend can fix. It needs an input-side counterpart to `#176`'s `x-sb-raw-body` marker, or real `ArrayBuffer`/`Uint8Array` body support (the upstream ask was filed as Porffor #386 and later retracted; see `https://github.com/baronunread/sproutboat/blob/main/patches/upstream/request-body-report-386.md`).
|
|
135
148
|
|
|
136
149
|
### Patch Changes
|
|
137
150
|
|
|
138
|
-
- 188d8e4: Fix (baronunread/sproutboat#184): a `put()` body (R2, both transports) with bytes ≥ 0x80 was corrupted on the way *in*, before storage
|
|
151
|
+
- 188d8e4: Fix (baronunread/sproutboat#184): a `put()` body (R2, both transports) with bytes ≥ 0x80 was corrupted on the way *in*, before storage: `porf_native_fetch_read_value` (the one function every inline-C string read in the runtime funnels through) always UTF-8-encodes a Latin-1-range value, correct for genuine text, wrong for opaque bytes off a raw HTTP upload. A 256-byte binary upload landed as 384 stored bytes.
|
|
139
152
|
|
|
140
153
|
`__sbR2PutRaw` (embedded) and `__sbCallBin` (broker, the only caller that ever passes it a real body) now try `porf_native_fetch_read_raw_bytes` first, falling back to the normal encoding path only for a genuine multi-byte JS string (which isn't a "bytestring" and so isn't eligible for the raw read anyway). This predates the #56 storage change — the old SQLite-blob `put()` called the identical `read_value`, so it was equally corrupted; the existing R2 conformance check never caught it because it only ever exercises a plain ASCII string.
|
|
141
154
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
const src = readFileSync(new URL("./native-fetch-prelude.js", import.meta.url), "utf8");
|
|
4
|
+
const decoder = src.slice(src.indexOf("function __sbFromUtf8("), src.indexOf("// sproutboat #181"));
|
|
5
|
+
const ingress = src.slice(
|
|
6
|
+
src.indexOf("function __sbDecodeIngress("),
|
|
7
|
+
src.indexOf("globalThis.__sbEntry ="),
|
|
8
|
+
);
|
|
9
|
+
if (!decoder || !ingress) throw new Error("Ingress decoder source not found");
|
|
10
|
+
// oxlint-disable-next-line no-function-constructor -- Exercise the text-only runtime prelude.
|
|
11
|
+
const load = new Function(`${decoder}\n${ingress}\nreturn __sbDecodeIngress;`);
|
|
12
|
+
const decode = load();
|
|
13
|
+
const raw = (text) =>
|
|
14
|
+
Array.from(new TextEncoder().encode(text), (byte) => String.fromCharCode(byte)).join("");
|
|
15
|
+
|
|
16
|
+
test("native ingress text and JSON decode Unicode without changing raw bytes", async () => {
|
|
17
|
+
const body = raw('{"id":"caffè-🚤"}');
|
|
18
|
+
const request = { body };
|
|
19
|
+
decode(request);
|
|
20
|
+
expect(await request.text()).toBe('{"id":"caffè-🚤"}');
|
|
21
|
+
expect(await request.json()).toEqual({ id: "caffè-🚤" });
|
|
22
|
+
expect(await request.text()).toBe('{"id":"caffè-🚤"}');
|
|
23
|
+
expect(request.body).toBe(body);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("native ingress retains empty bodies and JSON parse errors", () => {
|
|
27
|
+
const empty = { body: "" };
|
|
28
|
+
decode(empty);
|
|
29
|
+
expect(empty.text()).toBe("");
|
|
30
|
+
expect(() => empty.json()).toThrow();
|
|
31
|
+
const invalid = { body: "{" };
|
|
32
|
+
decode(invalid);
|
|
33
|
+
expect(() => invalid.json()).toThrow();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("a bodyless request keeps its existing methods", () => {
|
|
37
|
+
const text = () => "";
|
|
38
|
+
const request = { body: null, text };
|
|
39
|
+
decode(request);
|
|
40
|
+
expect(request.text).toBe(text);
|
|
41
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Prepended to every handler by tools/compile.ts, before Porffor's native-fetch
|
|
2
|
-
//
|
|
3
|
-
// gives URL (href/origin/pathname/search only) and Response without
|
|
4
|
-
// json(). This adds, additively, the rest of the WHATWG surface Worker code
|
|
2
|
+
// bundled handler. Porffor's runtime/fetch-globals.js (checked through alpha-10)
|
|
3
|
+
// gives URL (href/origin/pathname/search only) and Response without static
|
|
4
|
+
// json() or redirect(). This adds, additively, the rest of the WHATWG surface Worker code
|
|
5
5
|
// expects: URLSearchParams (read + write), URL.prototype.searchParams and the
|
|
6
6
|
// protocol/host/hostname/port/hash accessors, static Response.json,
|
|
7
7
|
// crypto.randomUUID / crypto.getRandomValues, crypto.subtle (digest + HMAC
|
|
@@ -9,14 +9,16 @@
|
|
|
9
9
|
// formData()/bytes() (#60). Each is feature-detected; delete a block once
|
|
10
10
|
// Porffor ships that global. crypto.scryptVerify (#153) is a Sproutboat
|
|
11
11
|
// extension, not WHATWG, and stays.
|
|
12
|
-
// Tracked upstream
|
|
12
|
+
// Tracked in the platform's upstream notes:
|
|
13
|
+
// https://github.com/baronunread/sproutboat/blob/main/patches/upstream/whatwg-fetch-globals.md
|
|
13
14
|
//
|
|
14
15
|
// Declared before it is referenced: a getter body that names a later top-level
|
|
15
|
-
// class throws ReferenceError in Porffor
|
|
16
|
+
// class throws ReferenceError in Porffor. See:
|
|
17
|
+
// https://github.com/baronunread/sproutboat/blob/main/patches/upstream/class-declaration-scope.md
|
|
16
18
|
|
|
17
19
|
// Duck-typing helpers, `typeof`-free (the repo's anti-slop lint bans `typeof`;
|
|
18
20
|
// these express the same spec-mandated checks and are verified under Porffor
|
|
19
|
-
// alpha-
|
|
21
|
+
// alpha-10 by examples/kitchen-sink/harness.ts).
|
|
20
22
|
function __sbIsStr(v) {
|
|
21
23
|
return Object(v) !== v && v === String(v);
|
|
22
24
|
}
|
|
@@ -66,7 +68,7 @@ function __sbCpuMs() {
|
|
|
66
68
|
return res === "" ? 0 : parseFloat(res);
|
|
67
69
|
}
|
|
68
70
|
|
|
69
|
-
// #28 — stamp `x-sb-cpu-ms` onto a handler Response. Porffor alpha-
|
|
71
|
+
// #28 — stamp `x-sb-cpu-ms` onto a handler Response. Porffor alpha-10's
|
|
70
72
|
// native-fetch serializer only reads headers from the plain object passed to
|
|
71
73
|
// `new Response(body, { headers })` — a later `.set()` or a `Headers` instance
|
|
72
74
|
// is ignored on the wire — so the metric is carried by rebuilding the Response
|
|
@@ -202,6 +204,16 @@ if (Response.json == null) {
|
|
|
202
204
|
};
|
|
203
205
|
}
|
|
204
206
|
|
|
207
|
+
// Porffor alpha-10 has no static Response.redirect().
|
|
208
|
+
if (Response.redirect == null) {
|
|
209
|
+
Response.redirect = function (url, status = 302) {
|
|
210
|
+
if (status !== 301 && status !== 302 && status !== 303 && status !== 307 && status !== 308) {
|
|
211
|
+
throw new RangeError("invalid redirect status");
|
|
212
|
+
}
|
|
213
|
+
return new Response("", { status, headers: { location: String(url) } });
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
205
217
|
// #59 — caches.default / caches.open(name), backed by the broker's cache.*
|
|
206
218
|
// ops (a sibling SQLite table, key on method+URL for v1 — no Vary yet).
|
|
207
219
|
// Deliberately NOT async: every other buffered-body method on this runtime
|
|
@@ -386,7 +398,8 @@ if (!("bytes" in Response.prototype)) {
|
|
|
386
398
|
// Porffor's URL exposes href / origin / pathname / search only. Add the rest of
|
|
387
399
|
// the WHATWG read surface, derived from `origin` (scheme://host[:port]).
|
|
388
400
|
// `hash` is always '' server-side — browsers strip the fragment before the
|
|
389
|
-
// request, so there is nothing to recover.
|
|
401
|
+
// request, so there is nothing to recover. See:
|
|
402
|
+
// https://github.com/baronunread/sproutboat/blob/main/patches/upstream/whatwg-fetch-globals.md
|
|
390
403
|
function __sbDefineURLAccessor(name, get) {
|
|
391
404
|
if (!(name in URL.prototype)) Object.defineProperty(URL.prototype, name, { configurable: true, get });
|
|
392
405
|
}
|
|
@@ -1816,7 +1829,8 @@ function __sbTriggerAuthed(request) {
|
|
|
1816
1829
|
// #163 — the connection's remote address, for `request.cf.clientIp`.
|
|
1817
1830
|
//
|
|
1818
1831
|
// `x-sb-remote-addr` is the TCP peer, appended by the server (see
|
|
1819
|
-
// patches/
|
|
1832
|
+
// https://github.com/baronunread/sproutboat/blob/main/patches/upstream/remote-address.md)
|
|
1833
|
+
// and stripped from anything a client sends, so it
|
|
1820
1834
|
// cannot be forged. With no proxy in front, that is the client. Behind one, set
|
|
1821
1835
|
// `SB_TRUSTED_PROXIES` to a comma-separated list of trusted CIDRs (or bare IPs):
|
|
1822
1836
|
// when the peer is trusted, the client is the rightmost `x-forwarded-for` entry
|
|
@@ -2011,7 +2025,26 @@ async function __sbQueueWithDrain(result, tasks) {
|
|
|
2011
2025
|
return new Response(JSON.stringify(r), { headers: { "content-type": "application/json" } });
|
|
2012
2026
|
}
|
|
2013
2027
|
|
|
2028
|
+
// Native HTTP ingress stores the body as one character per raw byte. Keep
|
|
2029
|
+
// that representation for binary consumers, but decode text lazily before
|
|
2030
|
+
// handing the request to user code. Request.text() in Porffor otherwise treats
|
|
2031
|
+
// the raw bytes as already-decoded characters and corrupts Unicode/HMAC input.
|
|
2032
|
+
function __sbDecodeIngress(request) {
|
|
2033
|
+
const raw = request.body;
|
|
2034
|
+
if (raw == null) return;
|
|
2035
|
+
let decoded = null;
|
|
2036
|
+
request.text = function () {
|
|
2037
|
+
if (decoded === null) decoded = __sbFromUtf8(raw);
|
|
2038
|
+
return decoded;
|
|
2039
|
+
};
|
|
2040
|
+
request.json = function () {
|
|
2041
|
+
if (decoded === null) decoded = __sbFromUtf8(raw);
|
|
2042
|
+
return JSON.parse(decoded);
|
|
2043
|
+
};
|
|
2044
|
+
}
|
|
2045
|
+
|
|
2014
2046
|
globalThis.__sbEntry = function (handlers, request) {
|
|
2047
|
+
__sbDecodeIngress(request);
|
|
2015
2048
|
const trigger = request.headers.get("x-sb-trigger");
|
|
2016
2049
|
if (!trigger) {
|
|
2017
2050
|
// #163/#128 — expose the request metadata the edge already has, the
|
|
@@ -2033,7 +2066,7 @@ globalThis.__sbEntry = function (handlers, request) {
|
|
|
2033
2066
|
// concurrent in-process requests.
|
|
2034
2067
|
//
|
|
2035
2068
|
// Sync handlers only. An async handler's promise is handed straight back:
|
|
2036
|
-
// Porffor alpha-
|
|
2069
|
+
// Porffor alpha-10's native-fetch server resolves the promise the handler
|
|
2037
2070
|
// itself returned, but never one derived from `.then()`, so chaining the
|
|
2038
2071
|
// tag on hangs the request forever. cpuMs is documented as absent for
|
|
2039
2072
|
// async handlers (see LogEvent in services/edge) — that is this.
|
|
@@ -2046,7 +2079,9 @@ globalThis.__sbEntry = function (handlers, request) {
|
|
|
2046
2079
|
};
|
|
2047
2080
|
let __res;
|
|
2048
2081
|
try {
|
|
2049
|
-
__res =
|
|
2082
|
+
__res = globalThis.__sbCompat >= "2026-09-28"
|
|
2083
|
+
? handlers.fetch(request, globalThis.env, ctx)
|
|
2084
|
+
: handlers.fetch(request, ctx);
|
|
2050
2085
|
} catch {
|
|
2051
2086
|
// #179 — a synchronous throw anywhere in the handler must not take the
|
|
2052
2087
|
// whole process (and every other in-flight request) down with it.
|
|
@@ -2073,10 +2108,10 @@ globalThis.__sbEntry = function (handlers, request) {
|
|
|
2073
2108
|
};
|
|
2074
2109
|
let __sres;
|
|
2075
2110
|
try {
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
ctx
|
|
2079
|
-
|
|
2111
|
+
const event = { cron: body.cron || "", scheduledTime: body.scheduledTime || Date.now(), noRetry() {} };
|
|
2112
|
+
__sres = globalThis.__sbCompat >= "2026-09-28"
|
|
2113
|
+
? handlers.scheduled(event, globalThis.env, ctx)
|
|
2114
|
+
: handlers.scheduled(event, ctx);
|
|
2080
2115
|
} catch {
|
|
2081
2116
|
// #179 — same rule as fetch: don't let a throw here crash the process.
|
|
2082
2117
|
return new Response("", { status: 204 });
|
|
@@ -2163,7 +2198,9 @@ async function __sbRunQueueBatch(handlers, body, ctx) {
|
|
|
2163
2198
|
},
|
|
2164
2199
|
};
|
|
2165
2200
|
try {
|
|
2166
|
-
const __qres =
|
|
2201
|
+
const __qres = globalThis.__sbCompat >= "2026-09-28"
|
|
2202
|
+
? handlers.queue(batch, globalThis.env, ctx)
|
|
2203
|
+
: handlers.queue(batch, ctx);
|
|
2167
2204
|
// Was fire-and-forget: an async `queue()` had this default-ack pass run (and
|
|
2168
2205
|
// the response go out) before its own `await`s did, so any ack()/retry()
|
|
2169
2206
|
// past the first one never counted.
|
package/src/source.ts
CHANGED
|
@@ -25,7 +25,7 @@ const alwaysForbidden: Array<[RegExp, string]> = [
|
|
|
25
25
|
// match would also flag it appearing in an ordinary string a dependency
|
|
26
26
|
// happens to construct (a doc link, a log message).
|
|
27
27
|
[/['"`]node:/, "Node, Bun, and Deno APIs are not supported"],
|
|
28
|
-
// Porffor
|
|
28
|
+
// The Porffor pin compiles `new Proxy(...)` and then ignores the handler: a
|
|
29
29
|
// trapped property reads back as `undefined`, with no throw. Rejecting it
|
|
30
30
|
// here is the difference between a build error and a 502 nobody can explain.
|
|
31
31
|
// It is why itty-router and other Proxy-based routers do not work yet.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
|
|
4
|
+
const source = readFileSync(new URL("./native-fetch-prelude.js", import.meta.url), "utf8");
|
|
5
|
+
// oxlint-disable-next-line no-function-constructor -- the prelude is prepended as text in production too
|
|
6
|
+
const load = new Function(
|
|
7
|
+
"Porffor",
|
|
8
|
+
`${source}\n__sbTriggerAuthed = () => true; return { entry: __sbEntry, queue: __sbRunQueueBatch };`,
|
|
9
|
+
);
|
|
10
|
+
const { entry, queue } = load({ c: () => "" });
|
|
11
|
+
const request = () => ({ headers: { get: () => null }, body: null });
|
|
12
|
+
|
|
13
|
+
test("the Workers compatibility date passes env and ctx in their standard positions", async () => {
|
|
14
|
+
globalThis.__sbCompat = "2026-09-28";
|
|
15
|
+
globalThis.env = { GREETING: "hello" };
|
|
16
|
+
const response = entry({ fetch(_request, env, ctx) {
|
|
17
|
+
return new Response(env.GREETING + ":" + (ctx.waitUntil instanceof Function));
|
|
18
|
+
} }, request());
|
|
19
|
+
expect(await response.text()).toBe("hello:true");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("earlier compatibility dates retain ctx as the second argument", async () => {
|
|
23
|
+
globalThis.__sbCompat = "2026-09-07";
|
|
24
|
+
const response = entry({ fetch(_request, ctx) {
|
|
25
|
+
return new Response(String(ctx.waitUntil instanceof Function));
|
|
26
|
+
} }, request());
|
|
27
|
+
expect(await response.text()).toBe("true");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("Response.redirect produces a Location header and empty body", async () => {
|
|
31
|
+
const response = Response.redirect("https://example.com/next", 301);
|
|
32
|
+
expect(response.status).toBe(301);
|
|
33
|
+
expect(response.headers.get("location")).toBe("https://example.com/next");
|
|
34
|
+
expect(await response.text()).toBe("");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("queue handlers receive env and ctx with the Workers compatibility date", async () => {
|
|
38
|
+
globalThis.__sbCompat = "2026-09-28";
|
|
39
|
+
globalThis.env = { GREETING: "hello" };
|
|
40
|
+
let seen = "";
|
|
41
|
+
await queue({ queue(_batch, env, ctx) {
|
|
42
|
+
seen = env.GREETING + ":" + (ctx.waitUntil instanceof Function);
|
|
43
|
+
} }, { messages: [] }, { waitUntil() {} });
|
|
44
|
+
expect(seen).toBe("hello:true");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("scheduled handlers receive env and ctx with the Workers compatibility date", () => {
|
|
48
|
+
globalThis.__sbCompat = "2026-09-28";
|
|
49
|
+
globalThis.env = { GREETING: "hello" };
|
|
50
|
+
let seen = "";
|
|
51
|
+
const response = entry({ scheduled(_event, env, ctx) {
|
|
52
|
+
seen = env.GREETING + ":" + (ctx.waitUntil instanceof Function);
|
|
53
|
+
} }, {
|
|
54
|
+
headers: { get: (name) => name === "x-sb-trigger" ? "scheduled" : null },
|
|
55
|
+
body: "{}",
|
|
56
|
+
});
|
|
57
|
+
expect(response.status).toBe(204);
|
|
58
|
+
expect(seen).toBe("hello:true");
|
|
59
|
+
});
|
package/test/native-crypto.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
|
-
import {
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
5
|
import { ensurePorffor } from "../../toolchain/src/acquire";
|
|
6
6
|
import { preludePath, TRANSPORT_MARKER, transportPath, wrapNativeFetchHandler } from "../src/wrap";
|
|
7
7
|
|
|
@@ -18,6 +18,7 @@ const expected = {
|
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
const handler = `
|
|
21
|
+
import { keyBytes } from "./crypto-helper.js";
|
|
21
22
|
const hex = (buffer) => {
|
|
22
23
|
const bytes = new Uint8Array(buffer);
|
|
23
24
|
let out = "";
|
|
@@ -28,7 +29,7 @@ export default {
|
|
|
28
29
|
async fetch() {
|
|
29
30
|
const key = await crypto.subtle.importKey(
|
|
30
31
|
"raw",
|
|
31
|
-
|
|
32
|
+
keyBytes(),
|
|
32
33
|
{ name: "HMAC", hash: "SHA-256" },
|
|
33
34
|
false,
|
|
34
35
|
["sign"],
|
|
@@ -67,9 +68,8 @@ const workdir = await mkdtemp(join(tmpdir(), "sb-native-crypto-"));
|
|
|
67
68
|
let server: ReturnType<typeof Bun.spawn> | undefined;
|
|
68
69
|
try {
|
|
69
70
|
const porffor = await ensurePorffor({ cacheRoot: workdir });
|
|
70
|
-
const esbuild = Bun.which("esbuild");
|
|
71
71
|
const node = Bun.which("node");
|
|
72
|
-
if (!
|
|
72
|
+
if (!node) throw new Error("native crypto test needs node on PATH");
|
|
73
73
|
|
|
74
74
|
const [core, broker] = await Promise.all([
|
|
75
75
|
readFile(preludePath, "utf8"),
|
|
@@ -79,12 +79,15 @@ try {
|
|
|
79
79
|
const prelude = core.replace(TRANSPORT_MARKER, broker);
|
|
80
80
|
const generated = join(workdir, "crypto.generated.js");
|
|
81
81
|
const binary = join(workdir, "crypto-native");
|
|
82
|
+
// This is a direct ESM import into Porffor, with no bundler in front of it.
|
|
83
|
+
await writeFile(join(workdir, "crypto-helper.js"),
|
|
84
|
+
"export const keyBytes = () => Uint8Array.from({ length: 16 }, (_, index) => index + 1);\n");
|
|
82
85
|
await writeFile(generated, wrapNativeFetchHandler(handler, prelude));
|
|
83
86
|
await rm(binary, { force: true });
|
|
84
87
|
|
|
85
88
|
const compile = Bun.spawn([node, resolve(porffor, "runtime/index.js"), "native", generated, "-o", binary, "-s", "-O0"], {
|
|
86
89
|
cwd: workdir,
|
|
87
|
-
env:
|
|
90
|
+
env: process.env,
|
|
88
91
|
stdout: "pipe",
|
|
89
92
|
stderr: "pipe",
|
|
90
93
|
});
|