sproutboat 0.6.1 → 0.8.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/SURFACE.md +10 -3
- package/package.json +4 -2
- package/src/api-version.ts +32 -0
- package/src/bearssl.ts +180 -0
- package/src/broker.ts +372 -14
- package/src/build.ts +55 -1
- package/src/compile.ts +73 -5
- package/src/config.ts +25 -0
- package/src/main.ts +45 -4
- package/src/manifest.ts +20 -0
- package/src/native-fetch-prelude.js +127 -168
- package/src/patch-porffor.ts +104 -6
- package/src/sqlite.ts +99 -0
- package/src/standalone-build.ts +64 -0
- package/src/surface.ts +37 -2
- package/src/transport-broker.js +290 -0
- package/src/transport-embedded.js +1567 -0
- package/src/update-check.ts +1 -1
- package/src/wrap.ts +64 -2
- package/vendor/sqlite-amalgamation-3500400/shell.c +34019 -0
- package/vendor/sqlite-amalgamation-3500400/sqlite3.c +262899 -0
- package/vendor/sqlite-amalgamation-3500400/sqlite3.h +13775 -0
- package/vendor/sqlite-amalgamation-3500400/sqlite3ext.h +723 -0
- package/vendor/sqlite-amalgamation.zip +0 -0
package/src/update-check.ts
CHANGED
|
@@ -28,7 +28,7 @@ function cachePath(): string {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/** Numeric x.y.z compare; a trailing `-tag` (prerelease) sorts before its release. */
|
|
31
|
-
function isNewer(latest: string, current: string): boolean {
|
|
31
|
+
export function isNewer(latest: string, current: string): boolean {
|
|
32
32
|
const parts = (v: string) =>
|
|
33
33
|
v
|
|
34
34
|
.split("-")[0]
|
package/src/wrap.ts
CHANGED
|
@@ -13,10 +13,41 @@
|
|
|
13
13
|
* imported — callers do `readFile(preludePath, "utf8")`. */
|
|
14
14
|
export const preludePath = new URL("./native-fetch-prelude.js", import.meta.url);
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* #15 — the two transports the prelude can be built with.
|
|
18
|
+
*
|
|
19
|
+
* Both define `__sbCall(reqJson) -> replyJson` and nothing else; every binding
|
|
20
|
+
* shim above that line is identical, which is what lets one conformance suite
|
|
21
|
+
* hold both honest. `broker` talks to the per-deployment broker over loopback
|
|
22
|
+
* (deployed, dev, phase-0 standalone); `embedded` compiles SQLite into the
|
|
23
|
+
* sprout and needs no second process at all.
|
|
24
|
+
*/
|
|
25
|
+
export type Transport = "broker" | "embedded";
|
|
26
|
+
export const transportPath = (transport: Transport): URL =>
|
|
27
|
+
new URL(transport === "embedded" ? "./transport-embedded.js" : "./transport-broker.js", import.meta.url);
|
|
28
|
+
|
|
29
|
+
/** Where the prelude expects its transport spliced in. */
|
|
30
|
+
export const TRANSPORT_MARKER =
|
|
31
|
+
"// TRANSPORT: wrap.ts splices one of transport-broker.js / transport-embedded.js here.";
|
|
32
|
+
|
|
16
33
|
// The server honours $PORT at runtime (patches/porffor-render.patch); this baked
|
|
17
34
|
// value is only a fallback for a directly-run binary.
|
|
18
35
|
const DEFAULT_PORT = 8080;
|
|
19
36
|
|
|
37
|
+
/**
|
|
38
|
+
* What an artifact with no `compatibilityDate` means. Artifacts built before
|
|
39
|
+
* the field existed keep the semantics of that day forever, because the binary
|
|
40
|
+
* is immutable and `rollback` can reactivate it at any time.
|
|
41
|
+
*
|
|
42
|
+
* How to use it: when a runtime behaviour has to change in a way that would
|
|
43
|
+
* break a deployed handler, don't change it unconditionally — gate it in the
|
|
44
|
+
* prelude on `__sbCompat >= "YYYY-MM-DD"` (ISO dates compare correctly as
|
|
45
|
+
* strings) and document the flip date. Old binaries carry their old date and
|
|
46
|
+
* keep the old behaviour; a project opts in by moving `compatibility_date` in
|
|
47
|
+
* its `sproutboat.jsonc` and rebuilding.
|
|
48
|
+
*/
|
|
49
|
+
export const BASELINE_COMPATIBILITY_DATE = "2026-08-26";
|
|
50
|
+
|
|
20
51
|
/**
|
|
21
52
|
* Binding names a project declares. `do` maps a binding name to a Durable Object
|
|
22
53
|
* class name; `crons` are schedule expressions with no name.
|
|
@@ -30,6 +61,9 @@ export type Bindings = {
|
|
|
30
61
|
queues: string[];
|
|
31
62
|
analytics: string[];
|
|
32
63
|
do: Array<{ binding: string; className: string }>;
|
|
64
|
+
/** #48 — worker-to-worker: binding name -> the project it calls. The hostname
|
|
65
|
+
* it resolves to is a runtime input, not part of the artifact. */
|
|
66
|
+
services: Array<{ binding: string; service: string }>;
|
|
33
67
|
crons: string[];
|
|
34
68
|
/** Static-asset binding name for `env.<NAME>.fetch(request)`; `""` when assets are edge-only. */
|
|
35
69
|
assets: string;
|
|
@@ -44,6 +78,7 @@ export const EMPTY_BINDINGS: Bindings = {
|
|
|
44
78
|
queues: [],
|
|
45
79
|
analytics: [],
|
|
46
80
|
do: [],
|
|
81
|
+
services: [],
|
|
47
82
|
crons: [],
|
|
48
83
|
assets: "",
|
|
49
84
|
};
|
|
@@ -58,6 +93,7 @@ function hasBindings(b: Bindings): boolean {
|
|
|
58
93
|
b.queues.length > 0 ||
|
|
59
94
|
b.analytics.length > 0 ||
|
|
60
95
|
b.do.length > 0 ||
|
|
96
|
+
b.services.length > 0 ||
|
|
61
97
|
b.assets !== ""
|
|
62
98
|
);
|
|
63
99
|
}
|
|
@@ -127,6 +163,10 @@ export function wrapNativeFetchHandler(
|
|
|
127
163
|
vars: Record<string, string> = {},
|
|
128
164
|
bindings: Bindings = EMPTY_BINDINGS,
|
|
129
165
|
port: number = DEFAULT_PORT,
|
|
166
|
+
compatibilityDate: string = BASELINE_COMPATIBILITY_DATE,
|
|
167
|
+
appName: string = "app",
|
|
168
|
+
/** #15 — assets baked into the module for a binary that has no files beside it. */
|
|
169
|
+
assets?: { manifest: unknown; files: Record<string, string> },
|
|
130
170
|
): string {
|
|
131
171
|
const neutralised = neutraliseExports(source);
|
|
132
172
|
if (neutralised === null || !/\bfetch\s*\(/.test(source)) {
|
|
@@ -134,15 +174,28 @@ export function wrapNativeFetchHandler(
|
|
|
134
174
|
}
|
|
135
175
|
|
|
136
176
|
const env = `const env = ${JSON.stringify(vars)};\nglobalThis.env = env;\n`;
|
|
177
|
+
// Baked, not a binding: the date belongs to the artifact, and a handler must
|
|
178
|
+
// not be able to change the semantics it was compiled against at runtime.
|
|
179
|
+
const compat =
|
|
180
|
+
`globalThis.__sbCompat = ${JSON.stringify(compatibilityDate)};\n` +
|
|
181
|
+
// #15 — the embedded transport derives its default data directory from this.
|
|
182
|
+
`globalThis.__sbAppName = ${JSON.stringify(appName)};\n` +
|
|
183
|
+
// #15 — and enforces the outbound allowlist itself, with no broker to do it.
|
|
184
|
+
`globalThis.__sbOutbound = ${JSON.stringify(bindings.outbound)};\n` +
|
|
185
|
+
(assets ? `globalThis.__sbAssets = ${JSON.stringify(assets)};\n` : "");
|
|
137
186
|
const wire = hasBindings(bindings) ? `__sbInstallBindings(env, ${JSON.stringify(bindings)});\n` : "";
|
|
138
187
|
const registerDO = bindings.do.length
|
|
139
188
|
? `__sbRegisterDO({ ${bindings.do.map((d) => `${d.className}: ${d.className}`).join(", ")} });\n`
|
|
140
189
|
: "";
|
|
190
|
+
// Cron / queue / alarm timers, for a transport that has no broker to deliver
|
|
191
|
+
// them. The broker transport defines this as a no-op, so the emitted module
|
|
192
|
+
// is the same either way.
|
|
193
|
+
const triggers = hasBindings(bindings) ? `__sbStartLocalTriggers(__sbHandlers, ${JSON.stringify(bindings)});\n` : "";
|
|
141
194
|
|
|
142
195
|
return (
|
|
143
|
-
`${prelude}\n${env}${wire}` +
|
|
196
|
+
`${prelude}\n${compat}${env}${wire}` +
|
|
144
197
|
`${neutralised}\n` +
|
|
145
|
-
`${registerDO}` +
|
|
198
|
+
`${registerDO}${triggers}` +
|
|
146
199
|
`export default {\n port: ${port},\n fetch(request) { return __sbEntry(__sbHandlers, request); }\n};\n`
|
|
147
200
|
);
|
|
148
201
|
}
|
|
@@ -182,6 +235,14 @@ export function readBindingsFromEnv(): Bindings {
|
|
|
182
235
|
const parsed: VarsJson = JSON.parse(raw);
|
|
183
236
|
if (!isVarsObject(parsed)) throw new Error("SPROUTBOAT_BINDINGS_JSON must be a JSON object");
|
|
184
237
|
const strings = (v: VarsJson): string[] => (Array.isArray(v) ? v.filter(isVarsString) : []);
|
|
238
|
+
const services: Array<{ binding: string; service: string }> = [];
|
|
239
|
+
if (Array.isArray(parsed.services)) {
|
|
240
|
+
for (const entry of parsed.services) {
|
|
241
|
+
if (isVarsObject(entry) && isVarsString(entry.binding) && isVarsString(entry.service)) {
|
|
242
|
+
services.push({ binding: entry.binding, service: entry.service });
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
185
246
|
const dos: Array<{ binding: string; className: string }> = [];
|
|
186
247
|
if (Array.isArray(parsed.do)) {
|
|
187
248
|
for (const entry of parsed.do) {
|
|
@@ -199,6 +260,7 @@ export function readBindingsFromEnv(): Bindings {
|
|
|
199
260
|
queues: strings(parsed.queues),
|
|
200
261
|
analytics: strings(parsed.analytics),
|
|
201
262
|
do: dos,
|
|
263
|
+
services,
|
|
202
264
|
crons: strings(parsed.crons),
|
|
203
265
|
assets: isVarsString(parsed.assets) ? parsed.assets : "",
|
|
204
266
|
};
|