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/src/wrap.ts
CHANGED
|
@@ -35,15 +35,73 @@ export type Bindings = {
|
|
|
35
35
|
assets: string;
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
-
export const EMPTY_BINDINGS: Bindings = {
|
|
38
|
+
export const EMPTY_BINDINGS: Bindings = {
|
|
39
|
+
kv: [],
|
|
40
|
+
secrets: [],
|
|
41
|
+
outbound: [],
|
|
42
|
+
d1: [],
|
|
43
|
+
r2: [],
|
|
44
|
+
queues: [],
|
|
45
|
+
analytics: [],
|
|
46
|
+
do: [],
|
|
47
|
+
crons: [],
|
|
48
|
+
assets: "",
|
|
49
|
+
};
|
|
39
50
|
|
|
40
51
|
function hasBindings(b: Bindings): boolean {
|
|
41
52
|
return (
|
|
42
|
-
b.kv.length > 0 ||
|
|
43
|
-
b.
|
|
53
|
+
b.kv.length > 0 ||
|
|
54
|
+
b.secrets.length > 0 ||
|
|
55
|
+
b.outbound.length > 0 ||
|
|
56
|
+
b.d1.length > 0 ||
|
|
57
|
+
b.r2.length > 0 ||
|
|
58
|
+
b.queues.length > 0 ||
|
|
59
|
+
b.analytics.length > 0 ||
|
|
60
|
+
b.do.length > 0 ||
|
|
61
|
+
b.assets !== ""
|
|
44
62
|
);
|
|
45
63
|
}
|
|
46
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Turn the module's exports into plain top-level declarations, so the handler
|
|
67
|
+
* object is reachable as `__sbHandlers` and Durable Object classes stay
|
|
68
|
+
* addressable by name.
|
|
69
|
+
*
|
|
70
|
+
* Two shapes reach us. A hand-written file exports inline
|
|
71
|
+
* (`export default { fetch }`), while a bundled one declares everything first
|
|
72
|
+
* and re-exports at the end (`export { src_default as default, Counter }`) —
|
|
73
|
+
* #89 made the second shape the normal case. Returns null when neither matches.
|
|
74
|
+
*/
|
|
75
|
+
export function neutraliseExports(source: string): string | null {
|
|
76
|
+
if (/\bexport\s+default\s*\{/.test(source)) {
|
|
77
|
+
return source
|
|
78
|
+
.replace(/^(\s*)export\s+default\s*/m, "$1const __sbHandlers = ")
|
|
79
|
+
.replace(/^export\s+(async\s+function|function|class|const|let|var)\b/gm, "$1");
|
|
80
|
+
}
|
|
81
|
+
// Not anchored to a line: a minified bundle puts the whole module on one
|
|
82
|
+
// line. Bundlers emit exactly one such block, at the end.
|
|
83
|
+
const blocks = [...source.matchAll(/export\s*\{([^}]*)\}\s*;?/g)];
|
|
84
|
+
const block = blocks[blocks.length - 1];
|
|
85
|
+
if (block === undefined) return null;
|
|
86
|
+
let handler: string | null = null;
|
|
87
|
+
const aliases: string[] = [];
|
|
88
|
+
for (const entry of block[1]
|
|
89
|
+
.split(",")
|
|
90
|
+
.map((part) => part.trim())
|
|
91
|
+
.filter(Boolean)) {
|
|
92
|
+
const parts = entry.match(/^(\S+)(?:\s+as\s+(\S+))?$/);
|
|
93
|
+
if (parts === null) continue;
|
|
94
|
+
const local = parts[1];
|
|
95
|
+
const exported = parts[2] ?? local;
|
|
96
|
+
if (exported === "default") handler = local;
|
|
97
|
+
// `export { Counter as Counter }` needs no alias; a renamed one does, so
|
|
98
|
+
// `durable_objects` in the config can still name the class it expects.
|
|
99
|
+
else if (exported !== local) aliases.push(`const ${exported} = ${local};`);
|
|
100
|
+
}
|
|
101
|
+
if (handler === null) return null;
|
|
102
|
+
return source.replace(block[0], [`const __sbHandlers = ${handler};`, ...aliases].join("\n"));
|
|
103
|
+
}
|
|
104
|
+
|
|
47
105
|
/**
|
|
48
106
|
* Build the final native-fetch module: the prelude (Web API shims + the broker
|
|
49
107
|
* binding shim + the trigger dispatcher), then `const env = {…}` with the baked
|
|
@@ -70,15 +128,10 @@ export function wrapNativeFetchHandler(
|
|
|
70
128
|
bindings: Bindings = EMPTY_BINDINGS,
|
|
71
129
|
port: number = DEFAULT_PORT,
|
|
72
130
|
): string {
|
|
73
|
-
|
|
131
|
+
const neutralised = neutraliseExports(source);
|
|
132
|
+
if (neutralised === null || !/\bfetch\s*\(/.test(source)) {
|
|
74
133
|
throw new Error("handler must default-export an object with a fetch(request) method");
|
|
75
134
|
}
|
|
76
|
-
// Neutralise the module's exports: its default object becomes `__sbHandlers`,
|
|
77
|
-
// and any `export class`/`function`/`const` (Durable Object classes, helpers)
|
|
78
|
-
// becomes a plain top-level declaration. Imports are already rejected upstream.
|
|
79
|
-
const neutralised = source
|
|
80
|
-
.replace(/^(\s*)export\s+default\s*/m, "$1const __sbHandlers = ")
|
|
81
|
-
.replace(/^export\s+(async\s+function|function|class|const|let|var)\b/gm, "$1");
|
|
82
135
|
|
|
83
136
|
const env = `const env = ${JSON.stringify(vars)};\nglobalThis.env = env;\n`;
|
|
84
137
|
const wire = hasBindings(bindings) ? `__sbInstallBindings(env, ${JSON.stringify(bindings)});\n` : "";
|
|
@@ -105,15 +158,16 @@ function isVarsString(value: VarsJson): value is string {
|
|
|
105
158
|
/** `SPROUTBOAT_VARS_JSON` (set by the build) → a validated flat string map. */
|
|
106
159
|
export function readVarsFromEnv(): Record<string, string> {
|
|
107
160
|
const raw = process.env.SPROUTBOAT_VARS_JSON;
|
|
108
|
-
|
|
109
|
-
if (!raw) return vars;
|
|
161
|
+
if (!raw) return {};
|
|
110
162
|
const parsed: VarsJson = JSON.parse(raw);
|
|
111
163
|
if (!isVarsObject(parsed)) throw new Error("SPROUTBOAT_VARS_JSON must be a JSON object");
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
164
|
+
return Object.fromEntries(
|
|
165
|
+
Object.entries(parsed).map(([key, value]): [string, string] => {
|
|
166
|
+
if (!/^[A-Z][A-Z0-9_]*$/.test(key) || !isVarsString(value))
|
|
167
|
+
throw new Error(`SPROUTBOAT_VARS_JSON.${key} must map an UPPER_SNAKE name to a string`);
|
|
168
|
+
return [key, value];
|
|
169
|
+
}),
|
|
170
|
+
);
|
|
117
171
|
}
|
|
118
172
|
|
|
119
173
|
/**
|