sproutboat 0.49.0 → 0.51.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 +27 -0
- package/SURFACE.md +1 -1
- package/THIRD_PARTY_NOTICES.md +2 -2
- package/package.json +8 -8
- package/src/bearssl.ts +12 -5
- package/src/bundle.ts +94 -4
- package/src/main.ts +3 -1
- package/src/node-shims.ts +82 -0
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,33 @@ maintained going forward by the `release` skill.
|
|
|
10
10
|
|
|
11
11
|
## Unreleased
|
|
12
12
|
|
|
13
|
+
## [0.51.0] - 2026-11-03
|
|
14
|
+
|
|
15
|
+
Built on `@sproutboat/runtime` 0.42.0, `@sproutboat/toolchain` 0.23.0 and `@sproutboat/wire` 0.22.0. The change below starts at a new compatibility date; earlier dates answer as before.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- **`connect()` from `cloudflare:sockets` from `2026-11-17`**: TCP sockets with `readable`, `writable`, `opened`, `closed`, `close()` and `startTls()`, on a standalone binary, under `sproutboat dev` and on a platform deploy. The same unmodified Postgres (`pg`, `postgres`) and Redis driver code that runs on workerd runs here, with no differences from workerd in the driver suites. Outbound policy is the one `fetch()` uses. Before the date, importing `cloudflare:sockets` fails the build with `cloudflare:sockets needs compatibility_date 2026-11-17 or later`.
|
|
20
|
+
- **From the same date a driver bundles as it does for workerd**: the `workerd` export condition (`pg-cloudflare`, `postgres/cf`), a bundled `Buffer`, `process` and `global`, and shims for `net`, `tls`, `dns`, `fs` and `crypto` so the Node branch of a driver names `cloudflare:sockets` instead of failing the build. A named function expression that shadows a global builtin function is renamed, which sproutd admission needs.
|
|
21
|
+
|
|
22
|
+
## [0.50.0] - 2026-11-02
|
|
23
|
+
|
|
24
|
+
Built on `@sproutboat/runtime` 0.41.0, `@sproutboat/toolchain` 0.22.0 and `@sproutboat/wire` 0.21.4. Each change below that an existing app could observe starts at a new compatibility date; earlier dates answer as before.
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- **`fetch()` on a standalone binary runs off the event loop from `2026-11-16`**: 20 concurrent fetches to a 100 ms endpoint take about
|
|
29
|
+
105 ms instead of 2,040 ms, and other requests keep being served meanwhile. Requests now interleave across `await fetch()`, as in workerd,
|
|
30
|
+
which is why it starts at a new date; earlier dates answer as before.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- **Secrets from the environment decode as UTF-8 from `2026-11-07`** on a standalone binary. Before, the handler got the environment's bytes one char per byte, so `é` arrived as `é`.
|
|
35
|
+
|
|
36
|
+
### Performance
|
|
37
|
+
|
|
38
|
+
- **ES256 (ECDSA P-256) in `crypto.subtle` signs 2.2x and verifies 3.1x faster** (macOS arm64, standalone and broker: 2,563 to 5,621 signs and 1,318 to 4,050 verifies per second). BearSSL is pinned to master commit `7bea48e` instead of 0.6 (source: the bearssl.org gitweb snapshot, sha256 `ed397832…`) and the runtime selects its 64-bit P-256 code (`ec_p256_m64`) where the target has a 128-bit multiply, which includes both Linux targets. Results are unchanged, so there is no new compatibility date; every other primitive is as before.
|
|
39
|
+
|
|
13
40
|
## [0.49.0] - 2026-10-31
|
|
14
41
|
|
|
15
42
|
Built on `@sproutboat/runtime` 0.39.0, `@sproutboat/toolchain` 0.22.0 and `@sproutboat/wire` 0.21.2. Nothing an app can observe changes, so there is no new compatibility date.
|
package/SURFACE.md
CHANGED
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -10,7 +10,7 @@ to its upstream source.
|
|
|
10
10
|
| Zig | `0.16.0` | Acquired cross-compiler | MIT | https://ziglang.org |
|
|
11
11
|
| uWebSockets and uSockets | `360c276d609d59af56ae6932adb95154ace9f15f` | Embedded source and static archive | Apache-2.0 | https://github.com/uNetworking/uWebSockets |
|
|
12
12
|
| SQLite | `3.50.4` | Acquired for standalone builds | Public domain | https://sqlite.org |
|
|
13
|
-
| BearSSL | `
|
|
13
|
+
| BearSSL | `7bea48e` (master) | Acquired for standalone builds | MIT | https://bearssl.org |
|
|
14
14
|
|
|
15
15
|
## Integrity pins
|
|
16
16
|
|
|
@@ -19,7 +19,7 @@ to its upstream source.
|
|
|
19
19
|
| Porffor source archive | `1a62187a93356b36cb6ac703ce9770c8917e4fa65545fd7c33ee8a180ae5a4c9` |
|
|
20
20
|
| Embedded uWebSockets archive | `e83736f3f8cf9d56a1ebe6ea61625a7af12386763374d47c14cff472ada7484a` |
|
|
21
21
|
| SQLite amalgamation archive | `1d3049dd0f830a025a53105fc79fd2ab9431aea99e137809d064d8ee8356b032` |
|
|
22
|
-
| BearSSL source archive | `
|
|
22
|
+
| BearSSL source archive | `ed397832b1613582737d9c1ce01c1af74ad6a9e9c52a341e24433987cda0f372` |
|
|
23
23
|
| Mozilla CA bundle | `f66dff1bdf8f96060b8177976f8b7d9254bc89bc4db933d769f7384d28480bc9` |
|
|
24
24
|
|
|
25
25
|
The complete Apache-2.0 notice for uWebSockets and uSockets is retained in the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sproutboat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.51.0",
|
|
4
4
|
"description": "Wrangler-shaped CLI for Sproutboat. Deploys workers to any control plane via --api-url / SPROUTBOAT_API_URL.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -64,9 +64,9 @@
|
|
|
64
64
|
"@sproutboat/artifact": "^0.3.0",
|
|
65
65
|
"@sproutboat/assets": "^0.3.0",
|
|
66
66
|
"@sproutboat/config": "^0.6.0",
|
|
67
|
-
"@sproutboat/runtime": "^0.
|
|
68
|
-
"@sproutboat/toolchain": "^0.
|
|
69
|
-
"@sproutboat/wire": "^0.
|
|
67
|
+
"@sproutboat/runtime": "^0.42.0",
|
|
68
|
+
"@sproutboat/toolchain": "^0.23.0",
|
|
69
|
+
"@sproutboat/wire": "^0.22.0"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@oxlint/plugins": "1.85.0",
|
|
@@ -78,10 +78,10 @@
|
|
|
78
78
|
"typescript": "7.0.2"
|
|
79
79
|
},
|
|
80
80
|
"optionalDependencies": {
|
|
81
|
-
"@sproutboat/cli-darwin-arm64": "0.
|
|
82
|
-
"@sproutboat/cli-darwin-x64": "0.
|
|
83
|
-
"@sproutboat/cli-linux-arm64": "0.
|
|
84
|
-
"@sproutboat/cli-linux-x64": "0.
|
|
81
|
+
"@sproutboat/cli-darwin-arm64": "0.51.0",
|
|
82
|
+
"@sproutboat/cli-darwin-x64": "0.51.0",
|
|
83
|
+
"@sproutboat/cli-linux-arm64": "0.51.0",
|
|
84
|
+
"@sproutboat/cli-linux-x64": "0.51.0"
|
|
85
85
|
},
|
|
86
86
|
"engines": {
|
|
87
87
|
"node": ">=18"
|
package/src/bearssl.ts
CHANGED
|
@@ -22,9 +22,16 @@ import { resolve } from "node:path";
|
|
|
22
22
|
import { LINUX_TARGETS, type BuildTarget } from "./targets";
|
|
23
23
|
import { resolveHostCompiler } from "./toolchain";
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
/**
|
|
26
|
+
* BearSSL master, pinned to a commit: 0.6 (2018) predates `ec_p256_m64`, the 64-bit P-256 code that
|
|
27
|
+
* crypto.subtle selects explicitly (ES256 sign 2.9x, verify 5.7x faster). There is no release after 0.6,
|
|
28
|
+
* so the source is the gitweb snapshot of this commit, pinned by the digest of its bytes.
|
|
29
|
+
*/
|
|
30
|
+
export const BEARSSL_COMMIT = "7bea48e5e850ab4cafbe68d3765cdaba13a86d6f";
|
|
31
|
+
export const BEARSSL_VERSION = BEARSSL_COMMIT.slice(0, 7);
|
|
32
|
+
const BEARSSL_TREE = `BearSSL-${BEARSSL_VERSION}`;
|
|
33
|
+
const BEARSSL_URL = `https://bearssl.org/gitweb/?p=BearSSL;a=snapshot;h=${BEARSSL_COMMIT};sf=tgz`;
|
|
34
|
+
const BEARSSL_SHA256 = "ed397832b1613582737d9c1ce01c1af74ad6a9e9c52a341e24433987cda0f372";
|
|
28
35
|
|
|
29
36
|
/**
|
|
30
37
|
* The Mozilla root set, as curl publishes it. Pinned by hash like everything
|
|
@@ -118,7 +125,7 @@ async function download(url: string, expected: string, to: string): Promise<Buff
|
|
|
118
125
|
/** Unpack the source tree once, and build the host tools we need from it. */
|
|
119
126
|
async function sourceTree(): Promise<string> {
|
|
120
127
|
const dir = cacheDir();
|
|
121
|
-
const tree = resolve(dir,
|
|
128
|
+
const tree = resolve(dir, BEARSSL_TREE);
|
|
122
129
|
return withBearsslLock(dir, "source", async () => {
|
|
123
130
|
const tarball = resolve(dir, "bearssl.tar.gz");
|
|
124
131
|
const sourceManifest = resolve(tree, ".sproutboat-source");
|
|
@@ -142,7 +149,7 @@ async function sourceTree(): Promise<string> {
|
|
|
142
149
|
await mkdir(stage, { recursive: true });
|
|
143
150
|
const untar = await run("tar", ["xzf", tarball, "-C", stage]);
|
|
144
151
|
if (untar.code !== 0) throw new Error(`could not unpack BearSSL: ${untar.stderr}`);
|
|
145
|
-
const stagedTree = resolve(stage,
|
|
152
|
+
const stagedTree = resolve(stage, BEARSSL_TREE);
|
|
146
153
|
if (!existsSync(resolve(stagedTree, "inc/bearssl.h"))) {
|
|
147
154
|
await rm(stage, { recursive: true, force: true });
|
|
148
155
|
throw new Error("BearSSL archive did not contain bearssl.h");
|
package/src/bundle.ts
CHANGED
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
* reaching for a Node API fails exactly as user code would.
|
|
13
13
|
*/
|
|
14
14
|
import { relative } from "node:path";
|
|
15
|
-
import {
|
|
15
|
+
import { SOCKETS_DATE } from "@sproutboat/runtime";
|
|
16
|
+
import { NODE_SHIMS, SOCKET_NODE_SHIMS } from "./node-shims";
|
|
16
17
|
|
|
17
18
|
export type BundleResult = {
|
|
18
19
|
/** One self-contained ESM module: what gets validated, hashed, and compiled. */
|
|
@@ -54,6 +55,86 @@ const nodeShimsPlugin: import("bun").BunPlugin = {
|
|
|
54
55
|
},
|
|
55
56
|
};
|
|
56
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Porffor lists the global builtin functions on the global object of every unit, and a function
|
|
60
|
+
* named like one of them (postgres: `export const escapeIdentifier = function escape(str) {}`)
|
|
61
|
+
* drops the entry from that unit alone, so a sproutd app that does it no longer matches the
|
|
62
|
+
* runtime's common code and is rejected at admission. A named function expression's name is only
|
|
63
|
+
* visible inside itself, so renaming it is safe unless the body calls itself by that name.
|
|
64
|
+
*/
|
|
65
|
+
const SHADOWED_BUILTIN_FUNCTIONS =
|
|
66
|
+
/([=(:,?&|]\s*|\breturn\s+)function\s+(escape|unescape|eval|isNaN|isFinite|parseInt|parseFloat|encodeURI|encodeURIComponent|decodeURI|decodeURIComponent)\s*\(/g;
|
|
67
|
+
|
|
68
|
+
/** What a driver reads off `process`: an empty environment and a microtask for nextTick. */
|
|
69
|
+
const PROCESS_STUB = `
|
|
70
|
+
const env = {};
|
|
71
|
+
function nextTick(fn, ...args) { Promise.resolve().then(() => fn(...args)); }
|
|
72
|
+
const process = { env, platform: "linux", arch: "wasm32", version: "v20.0.0", versions: {}, argv: [], browser: true, nextTick, emitWarning() {}, cwd() { return "/"; }, stderr: { write() {} }, stdout: { write() {} }, on() {}, off() {}, once() {}, emit() {} };
|
|
73
|
+
export default process;
|
|
74
|
+
`;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Node libraries use `Buffer` and `process` as free globals. Bun's browser target does not
|
|
78
|
+
* inject them for a CommonJS file, so a dependency that never imports Buffer would throw a
|
|
79
|
+
* ReferenceError at runtime. This gives every dependency file that uses one a local binding to the
|
|
80
|
+
* bundled implementation (the `buffer` package, which Porffor compiles; see the toolchain notes on
|
|
81
|
+
* typed array method dispatch). Applied only to node_modules, never to the handler's own code.
|
|
82
|
+
*/
|
|
83
|
+
const nodeGlobalsPlugin: import("bun").BunPlugin = {
|
|
84
|
+
name: "sproutboat-node-globals",
|
|
85
|
+
setup(build) {
|
|
86
|
+
build.onResolve({ filter: /^sb-node-process$/ }, () => ({ path: "sb-node-process", namespace: "sb-node-global" }));
|
|
87
|
+
build.onLoad({ filter: /.*/, namespace: "sb-node-global" }, () => ({ contents: PROCESS_STUB, loader: "js" }));
|
|
88
|
+
build.onLoad({ filter: /node_modules\/.*\.(?:c?js|mjs)$/ }, async (args) => {
|
|
89
|
+
const original = await Bun.file(args.path).text();
|
|
90
|
+
const text = original.replace(
|
|
91
|
+
SHADOWED_BUILTIN_FUNCTIONS,
|
|
92
|
+
(_m, lead: string, name: string) => `${lead}function ${name}_sb(`,
|
|
93
|
+
);
|
|
94
|
+
const usesBuffer =
|
|
95
|
+
/(?:^|[^.\w$])Buffer\s*[.(]/m.test(text) &&
|
|
96
|
+
!/\b(?:import|require)\b[^;\n]*\bBuffer\b/.test(text) &&
|
|
97
|
+
!/(?:const|let|var)\s+\{?\s*Buffer\b/.test(text);
|
|
98
|
+
const usesProcess =
|
|
99
|
+
/(?:^|[^.\w$])process\s*\./m.test(text) &&
|
|
100
|
+
!/\b(?:import|require)\b[^;\n]*\bprocess\b/.test(text) &&
|
|
101
|
+
!/(?:const|let|var|function)\s+\{?\s*process\b/.test(text);
|
|
102
|
+
const usesGlobal =
|
|
103
|
+
/(?:^|[^.\w$])global\s*\./m.test(text) && !/(?:const|let|var|function)\s+\{?\s*global\b/.test(text);
|
|
104
|
+
if (!usesBuffer && !usesProcess && !usesGlobal)
|
|
105
|
+
return text === original ? undefined : { contents: text, loader: "js" };
|
|
106
|
+
const esm = /^\s*(?:import|export)\s/m.test(text);
|
|
107
|
+
let prefix = "";
|
|
108
|
+
if (usesBuffer) prefix += esm ? `import { Buffer } from "buffer";\n` : `var { Buffer } = require("buffer");\n`;
|
|
109
|
+
if (usesProcess)
|
|
110
|
+
prefix += esm
|
|
111
|
+
? `import process from "sb-node-process";\n`
|
|
112
|
+
: `var process = require("sb-node-process").default;\n`;
|
|
113
|
+
if (usesGlobal) prefix += "var global = globalThis;\n";
|
|
114
|
+
return { contents: prefix + text, loader: "js" };
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const socketShimSpecifiers = new RegExp(
|
|
120
|
+
`^(${Object.keys(SOCKET_NODE_SHIMS)
|
|
121
|
+
.map((s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
|
|
122
|
+
.join("|")})$`,
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
/** The Node built-ins a driver names, replaced from the sockets date: see SOCKET_NODE_SHIMS. */
|
|
126
|
+
const socketShimsPlugin: import("bun").BunPlugin = {
|
|
127
|
+
name: "sproutboat-socket-shims",
|
|
128
|
+
setup(build) {
|
|
129
|
+
build.onResolve({ filter: socketShimSpecifiers }, (args) => ({ path: args.path, namespace: "sb-socket-shim" }));
|
|
130
|
+
build.onLoad({ filter: /.*/, namespace: "sb-socket-shim" }, (args) => ({
|
|
131
|
+
// SAFETY: onResolve above only routes specifiers built from SOCKET_NODE_SHIMS's own keys here.
|
|
132
|
+
contents: (SOCKET_NODE_SHIMS as Record<string, string>)[args.path],
|
|
133
|
+
loader: "js",
|
|
134
|
+
}));
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
|
|
57
138
|
/** Bun reports resolution failures on `AggregateError.errors`; its own message is just "Bundle failed". */
|
|
58
139
|
function bundleDetail(cause: unknown): string {
|
|
59
140
|
const errors = cause instanceof AggregateError ? cause.errors : [];
|
|
@@ -63,7 +144,15 @@ function bundleDetail(cause: unknown): string {
|
|
|
63
144
|
}
|
|
64
145
|
|
|
65
146
|
/** Bundle `entryPath` into a single module, resolving imports from `projectDir`. */
|
|
66
|
-
export async function bundleHandler(
|
|
147
|
+
export async function bundleHandler(
|
|
148
|
+
entryPath: string,
|
|
149
|
+
projectDir: string,
|
|
150
|
+
options: { compatibilityDate?: string } = {},
|
|
151
|
+
): Promise<BundleResult> {
|
|
152
|
+
// From the sockets date a driver bundles as it does for workerd: its "workerd" export condition,
|
|
153
|
+
// Node's free `Buffer`/`process`/`global` bound to bundled code, and net/tls/dns/fs/crypto shims
|
|
154
|
+
// that name cloudflare:sockets when the driver takes its Node path.
|
|
155
|
+
const sockets = options.compatibilityDate !== undefined && options.compatibilityDate >= SOCKETS_DATE;
|
|
67
156
|
let built: Awaited<ReturnType<typeof Bun.build>>;
|
|
68
157
|
try {
|
|
69
158
|
built = await Bun.build({
|
|
@@ -78,10 +167,11 @@ export async function bundleHandler(entryPath: string, projectDir: string): Prom
|
|
|
78
167
|
minify: false,
|
|
79
168
|
splitting: false,
|
|
80
169
|
sourcemap: "none",
|
|
81
|
-
|
|
170
|
+
conditions: sockets ? ["workerd", "worker"] : undefined,
|
|
171
|
+
plugins: sockets ? [nodeShimsPlugin, socketShimsPlugin, nodeGlobalsPlugin] : [nodeShimsPlugin],
|
|
82
172
|
// WorkerEntrypoint, DurableObject, RpcTarget and env are runtime globals; the
|
|
83
173
|
// compiler turns this import into them, so it stays in the bundle as written.
|
|
84
|
-
external: ["cloudflare:workers"],
|
|
174
|
+
external: ["cloudflare:workers", "cloudflare:sockets"],
|
|
85
175
|
});
|
|
86
176
|
} catch (cause) {
|
|
87
177
|
// An unresolvable specifier arrives as an AggregateError whose `errors`
|
package/src/main.ts
CHANGED
|
@@ -234,7 +234,9 @@ async function readProject(directory = process.cwd()) {
|
|
|
234
234
|
// smuggle in a Node API the handler is not allowed to touch.
|
|
235
235
|
let bundle: BundleResult;
|
|
236
236
|
try {
|
|
237
|
-
bundle = await bundleHandler(sourcePath, project.directory
|
|
237
|
+
bundle = await bundleHandler(sourcePath, project.directory, {
|
|
238
|
+
compatibilityDate: project.config.compatibility_date,
|
|
239
|
+
});
|
|
238
240
|
} catch (cause) {
|
|
239
241
|
fail(cause instanceof BundleError ? cause.message : String(cause));
|
|
240
242
|
}
|
package/src/node-shims.ts
CHANGED
|
@@ -386,3 +386,85 @@ export const NODE_SHIMS = {
|
|
|
386
386
|
"node:querystring": NODE_QUERYSTRING_SHIM,
|
|
387
387
|
"node:url": NODE_URL_SHIM,
|
|
388
388
|
} satisfies Record<string, string>;
|
|
389
|
+
|
|
390
|
+
// From the sockets date (2026-11-17), for drivers that carry a Node code path next to their Workers one.
|
|
391
|
+
/**
|
|
392
|
+
* node:net, node:tls, node:dns, node:fs and node:util/types for drivers that carry a Node code path
|
|
393
|
+
* next to their Workers one (pg, mysql2). The Workers path never calls them: every function throws
|
|
394
|
+
* a message that names cloudflare:sockets, so a driver that does take the Node path says why.
|
|
395
|
+
*/
|
|
396
|
+
const unavailable = (what: string) =>
|
|
397
|
+
`function unavailable() { throw new Error("${what} is not available in a compiled handler; use cloudflare:sockets (connect())"); }\n`;
|
|
398
|
+
const NODE_NET_SHIM =
|
|
399
|
+
unavailable("the net module") +
|
|
400
|
+
`class Socket { constructor() { unavailable(); } }
|
|
401
|
+
const ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$/;
|
|
402
|
+
function isIP(x) { return ipv4.test(String(x)) ? 4 : String(x).indexOf(":") >= 0 && /^[0-9a-fA-F:.]+$/.test(String(x)) ? 6 : 0; }
|
|
403
|
+
function connect() { unavailable(); }
|
|
404
|
+
export { Socket, isIP, connect };
|
|
405
|
+
export default { Socket, isIP, connect, createConnection: connect };
|
|
406
|
+
`;
|
|
407
|
+
const NODE_TLS_SHIM =
|
|
408
|
+
unavailable("the tls module") +
|
|
409
|
+
`function connect() { unavailable(); }
|
|
410
|
+
export { connect };
|
|
411
|
+
export default { connect };
|
|
412
|
+
`;
|
|
413
|
+
const NODE_DNS_SHIM =
|
|
414
|
+
unavailable("the dns module") +
|
|
415
|
+
`function lookup() { unavailable(); }
|
|
416
|
+
export { lookup };
|
|
417
|
+
export default { lookup, promises: { lookup } };
|
|
418
|
+
`;
|
|
419
|
+
const NODE_FS_SHIM =
|
|
420
|
+
unavailable("the fs module") +
|
|
421
|
+
`function readFileSync() { unavailable(); }
|
|
422
|
+
function existsSync() { return false; }
|
|
423
|
+
function statSync() { unavailable(); }
|
|
424
|
+
function createReadStream() { unavailable(); }
|
|
425
|
+
export { readFileSync, existsSync, statSync, createReadStream };
|
|
426
|
+
export default { readFileSync, existsSync, statSync, createReadStream };
|
|
427
|
+
`;
|
|
428
|
+
const NODE_UTIL_TYPES_SHIM = `
|
|
429
|
+
const isDate = (v) => v instanceof Date;
|
|
430
|
+
const isTypedArray = (v) => ArrayBuffer.isView(v) && !(v instanceof DataView);
|
|
431
|
+
const isUint8Array = (v) => v instanceof Uint8Array;
|
|
432
|
+
const isArrayBuffer = (v) => v instanceof ArrayBuffer;
|
|
433
|
+
const isPromise = (v) => v instanceof Promise;
|
|
434
|
+
const isRegExp = (v) => v instanceof RegExp;
|
|
435
|
+
export { isDate, isTypedArray, isUint8Array, isArrayBuffer, isPromise, isRegExp };
|
|
436
|
+
export default { isDate, isTypedArray, isUint8Array, isArrayBuffer, isPromise, isRegExp };
|
|
437
|
+
`;
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* node:crypto as a Workers handler has it: the Web Crypto global, not the Node API. pg asks for
|
|
441
|
+
* `crypto.webcrypto || globalThis.crypto` and for createHash("md5") inside a try, so a createHash
|
|
442
|
+
* that throws sends it to crypto.subtle. Bun's default polyfill (crypto-browserify) is 20k lines,
|
|
443
|
+
* and its elliptic-curve code does not compile under Porffor.
|
|
444
|
+
*/
|
|
445
|
+
const NODE_CRYPTO_SHIM = `
|
|
446
|
+
function unavailable() { throw new Error("this crypto function is not available; use crypto.subtle"); }
|
|
447
|
+
const webcrypto = globalThis.crypto;
|
|
448
|
+
function randomBytes(n) { return webcrypto.getRandomValues(new Uint8Array(n)); }
|
|
449
|
+
function randomUUID() { return webcrypto.randomUUID(); }
|
|
450
|
+
function createHash() { unavailable(); }
|
|
451
|
+
function createHmac() { unavailable(); }
|
|
452
|
+
export { webcrypto, randomBytes, randomUUID, createHash, createHmac };
|
|
453
|
+
export default { webcrypto, randomBytes, randomUUID, createHash, createHmac, getRandomValues: (a) => webcrypto.getRandomValues(a), subtle: webcrypto.subtle };
|
|
454
|
+
`;
|
|
455
|
+
|
|
456
|
+
/** Replaces Bun's browser polyfills, in a bundle at the sockets date or later (see bundle.ts). */
|
|
457
|
+
export const SOCKET_NODE_SHIMS = {
|
|
458
|
+
"node:crypto": NODE_CRYPTO_SHIM,
|
|
459
|
+
crypto: NODE_CRYPTO_SHIM,
|
|
460
|
+
"node:net": NODE_NET_SHIM,
|
|
461
|
+
net: NODE_NET_SHIM,
|
|
462
|
+
"node:tls": NODE_TLS_SHIM,
|
|
463
|
+
tls: NODE_TLS_SHIM,
|
|
464
|
+
"node:dns": NODE_DNS_SHIM,
|
|
465
|
+
dns: NODE_DNS_SHIM,
|
|
466
|
+
"node:fs": NODE_FS_SHIM,
|
|
467
|
+
fs: NODE_FS_SHIM,
|
|
468
|
+
"node:util/types": NODE_UTIL_TYPES_SHIM,
|
|
469
|
+
"util/types": NODE_UTIL_TYPES_SHIM,
|
|
470
|
+
} satisfies Record<string, string>;
|