oxidejs 0.2.3 → 0.3.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 +8 -4
- package/dist/actions-DE6p5Cyp.mjs +504 -0
- package/dist/client-Bc4g9AEw.mjs +145 -0
- package/dist/context-C1UFQ0Zc.d.mts +64 -0
- package/dist/context-zrTZyYpF.mjs +42 -0
- package/dist/index.d.mts +2 -35
- package/dist/index.mjs +108 -2
- package/dist/plugin-IQa_lKkT.mjs +601 -0
- package/dist/plugin.d.mts +8 -0
- package/dist/plugin.mjs +2 -0
- package/dist/rpc/client.d.mts +13 -0
- package/dist/rpc/client.mjs +2 -0
- package/dist/rpc-DYFEdah8.mjs +382 -0
- package/dist/rpc.d.mts +75 -0
- package/dist/rpc.mjs +3 -0
- package/dist/rsbuild.mjs +1 -1
- package/dist/vite.mjs +1 -1
- package/dist/worker-dom/install.d.mts +1 -0
- package/dist/worker-dom/install.mjs +6 -0
- package/dist/worker-dom.d.mts +5 -0
- package/dist/worker-dom.mjs +16 -0
- package/package.json +19 -8
- package/virtual.d.ts +7 -4
- package/dist/src-D-qdNVqg.mjs +0 -954
package/dist/src-D-qdNVqg.mjs
DELETED
|
@@ -1,954 +0,0 @@
|
|
|
1
|
-
import { createUnplugin } from "unplugin";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { pathToFileURL } from "node:url";
|
|
4
|
-
import fs from "node:fs";
|
|
5
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
|
-
//#region \0rolldown/runtime.js
|
|
7
|
-
var __defProp = Object.defineProperty;
|
|
8
|
-
var __exportAll = (all, no_symbols) => {
|
|
9
|
-
let target = {};
|
|
10
|
-
for (var name in all) __defProp(target, name, {
|
|
11
|
-
get: all[name],
|
|
12
|
-
enumerable: true
|
|
13
|
-
});
|
|
14
|
-
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
15
|
-
return target;
|
|
16
|
-
};
|
|
17
|
-
//#endregion
|
|
18
|
-
//#region src/actions.ts
|
|
19
|
-
var actions_exports = /* @__PURE__ */ __exportAll({
|
|
20
|
-
ACTION_PATH: () => ACTION_PATH,
|
|
21
|
-
RESOLVED_VIRTUAL_ACTIONS_ID: () => RESOLVED_VIRTUAL_ACTIONS_ID,
|
|
22
|
-
RESOLVED_VIRTUAL_CLIENT_ID: () => RESOLVED_VIRTUAL_CLIENT_ID,
|
|
23
|
-
RESOLVED_VIRTUAL_WORKER_ID: () => RESOLVED_VIRTUAL_WORKER_ID,
|
|
24
|
-
RequestBodyTooLargeError: () => RequestBodyTooLargeError,
|
|
25
|
-
VIRTUAL_ACTIONS_ID: () => VIRTUAL_ACTIONS_ID,
|
|
26
|
-
VIRTUAL_CLIENT_ID: () => VIRTUAL_CLIENT_ID,
|
|
27
|
-
VIRTUAL_WORKER_ID: () => VIRTUAL_WORKER_ID,
|
|
28
|
-
generateActionsModule: () => generateActionsModule,
|
|
29
|
-
generateClientModule: () => generateClientModule,
|
|
30
|
-
generateClientStub: () => generateClientStub,
|
|
31
|
-
generateWorkerWrapper: () => generateWorkerWrapper,
|
|
32
|
-
isServerFileId: () => isServerFileId,
|
|
33
|
-
loadClientStub: () => loadClientStub,
|
|
34
|
-
moduleKey: () => moduleKey,
|
|
35
|
-
nodeToWebRequest: () => nodeToWebRequest,
|
|
36
|
-
parseExportedNames: () => parseExportedNames,
|
|
37
|
-
pluginShouldStub: () => pluginShouldStub,
|
|
38
|
-
scanServerFiles: () => scanServerFiles,
|
|
39
|
-
sendWebResponseFrom: () => sendWebResponseFrom,
|
|
40
|
-
shouldStubServerModule: () => shouldStubServerModule
|
|
41
|
-
});
|
|
42
|
-
const VIRTUAL_ACTIONS_ID = "virtual:oxide/actions";
|
|
43
|
-
const RESOLVED_VIRTUAL_ACTIONS_ID = `\0${VIRTUAL_ACTIONS_ID}`;
|
|
44
|
-
const VIRTUAL_WORKER_ID = "virtual:oxide/worker";
|
|
45
|
-
const RESOLVED_VIRTUAL_WORKER_ID = `\0${VIRTUAL_WORKER_ID}`;
|
|
46
|
-
const VIRTUAL_CLIENT_ID = "virtual:oxide/client";
|
|
47
|
-
const RESOLVED_VIRTUAL_CLIENT_ID = `\0${VIRTUAL_CLIENT_ID}`;
|
|
48
|
-
const ACTION_PATH = "/__oxide/action";
|
|
49
|
-
const IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
50
|
-
"node_modules",
|
|
51
|
-
"dist",
|
|
52
|
-
".git",
|
|
53
|
-
".wrangler"
|
|
54
|
-
]);
|
|
55
|
-
/** Only `export const name = action(...)` become remote RPC actions. Everything else stays server-local. */
|
|
56
|
-
const EXPORT_RE = /^\s*export\s+const\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s+)?action\s*\(/gm;
|
|
57
|
-
function isServerFileId(id) {
|
|
58
|
-
const file = id.split("?")[0]?.replace(/\\/g, "/") ?? "";
|
|
59
|
-
return [
|
|
60
|
-
".ts",
|
|
61
|
-
".tsx",
|
|
62
|
-
".js",
|
|
63
|
-
".jsx"
|
|
64
|
-
].some((ext) => file.endsWith(`.server${ext}`));
|
|
65
|
-
}
|
|
66
|
-
function moduleKey(absFile) {
|
|
67
|
-
return path.basename(absFile).replace(/\.server\.(?:[jt]sx?)$/i, "");
|
|
68
|
-
}
|
|
69
|
-
function parseExportedNames(source) {
|
|
70
|
-
const names = /* @__PURE__ */ new Set();
|
|
71
|
-
for (const match of source.matchAll(EXPORT_RE)) {
|
|
72
|
-
const name = match[1];
|
|
73
|
-
if (name) names.add(name);
|
|
74
|
-
}
|
|
75
|
-
return [...names];
|
|
76
|
-
}
|
|
77
|
-
function scanServerFiles(root) {
|
|
78
|
-
const files = [];
|
|
79
|
-
const walk = (dir) => {
|
|
80
|
-
let entries;
|
|
81
|
-
try {
|
|
82
|
-
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
83
|
-
} catch {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
for (const entry of entries) {
|
|
87
|
-
if (entry.name.startsWith(".")) continue;
|
|
88
|
-
const abs = path.join(dir, entry.name);
|
|
89
|
-
if (entry.isDirectory()) {
|
|
90
|
-
if (IGNORE_DIRS.has(entry.name)) continue;
|
|
91
|
-
walk(abs);
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
if (entry.isFile() && isServerFileId(entry.name)) files.push(abs);
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
walk(root);
|
|
98
|
-
const byKey = /* @__PURE__ */ new Map();
|
|
99
|
-
const modules = [];
|
|
100
|
-
for (const abs of files.sort()) {
|
|
101
|
-
const key = moduleKey(abs);
|
|
102
|
-
if (!key) throw new Error(`oxidejs: invalid server module name: ${abs}`);
|
|
103
|
-
const existing = byKey.get(key);
|
|
104
|
-
if (existing) throw new Error(`oxidejs: duplicate server module key "${key}": ${existing} and ${abs}`);
|
|
105
|
-
byKey.set(key, abs);
|
|
106
|
-
modules.push({
|
|
107
|
-
abs,
|
|
108
|
-
key,
|
|
109
|
-
exports: parseExportedNames(fs.readFileSync(abs, "utf8"))
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
return modules;
|
|
113
|
-
}
|
|
114
|
-
function generateClientModule(transport = "http", headers, path = ACTION_PATH) {
|
|
115
|
-
if (transport === "ws") return `import { createClient } from "tacho/client/ws";
|
|
116
|
-
const __proto = typeof location === "undefined" ? "ws:" : location.protocol === "https:" ? "wss:" : "ws:";
|
|
117
|
-
const __host = typeof location === "undefined" ? "localhost" : location.host;
|
|
118
|
-
export const client = createClient({ url: __proto + "//" + __host + ${JSON.stringify(path)} });
|
|
119
|
-
`;
|
|
120
|
-
const opts = { url: path };
|
|
121
|
-
if (headers) opts.headers = headers;
|
|
122
|
-
return `import { createClient } from "tacho/client/http";
|
|
123
|
-
export const client = createClient(${JSON.stringify(opts)});
|
|
124
|
-
`;
|
|
125
|
-
}
|
|
126
|
-
function generateClientStub(mod) {
|
|
127
|
-
const lines = [`// oxidejs:client-stub`, `import { client } from ${JSON.stringify(VIRTUAL_CLIENT_ID)};`];
|
|
128
|
-
for (const name of mod.exports) lines.push(`export const ${name} = (...args) => {
|
|
129
|
-
const opts = args.at(-1);
|
|
130
|
-
// ponytail: peel last { signal } only. A lone payload { signal: AbortSignal } is treated as CallOptions.
|
|
131
|
-
return opts && typeof opts === "object" && opts.signal instanceof AbortSignal && Object.keys(opts).length === 1
|
|
132
|
-
? client[${JSON.stringify(mod.key)}][${JSON.stringify(name)}](args.slice(0, -1), opts)
|
|
133
|
-
: client[${JSON.stringify(mod.key)}][${JSON.stringify(name)}](args);
|
|
134
|
-
};`);
|
|
135
|
-
return `${lines.join("\n")}\n`;
|
|
136
|
-
}
|
|
137
|
-
function generateActionsModule(modules, opts) {
|
|
138
|
-
const lines = [
|
|
139
|
-
`import { AsyncLocalStorage } from "node:async_hooks";`,
|
|
140
|
-
`import { tacho } from "tacho";`,
|
|
141
|
-
`const __alsKey = Symbol.for("oxidejs.requestContext");`,
|
|
142
|
-
`const __als = globalThis[__alsKey] ??= new AsyncLocalStorage();`
|
|
143
|
-
];
|
|
144
|
-
const aliases = modules.map((mod, i) => {
|
|
145
|
-
const alias = `__m${i}`;
|
|
146
|
-
const spec = opts?.bust === true ? `${mod.abs}?t=${fs.statSync(mod.abs).mtimeMs}` : mod.abs;
|
|
147
|
-
lines.push(`import * as ${alias} from ${JSON.stringify(spec)};`);
|
|
148
|
-
return {
|
|
149
|
-
alias,
|
|
150
|
-
mod
|
|
151
|
-
};
|
|
152
|
-
});
|
|
153
|
-
lines.push(`const rpc = tacho();`);
|
|
154
|
-
lines.push(`const actions = rpc({`);
|
|
155
|
-
for (const { alias, mod } of aliases) {
|
|
156
|
-
lines.push(` ${JSON.stringify(mod.key)}: {`);
|
|
157
|
-
for (const name of mod.exports) lines.push(` ${JSON.stringify(name)}: rpc.run(({ input, ctx }) => __als.run(ctx, () => {
|
|
158
|
-
const out = ${alias}[${JSON.stringify(name)}].apply(null, Array.isArray(input) ? input : []);
|
|
159
|
-
if (!out || typeof out !== "object" || typeof out.next !== "function") return out;
|
|
160
|
-
return {
|
|
161
|
-
next: (v) => __als.run(ctx, () => out.next(v)),
|
|
162
|
-
return: (v) => __als.run(ctx, () => out.return(v)),
|
|
163
|
-
throw: (e) => __als.run(ctx, () => out.throw(e)),
|
|
164
|
-
[Symbol.asyncIterator]() { return this; },
|
|
165
|
-
};
|
|
166
|
-
})),`);
|
|
167
|
-
lines.push(` },`);
|
|
168
|
-
}
|
|
169
|
-
lines.push(`});`);
|
|
170
|
-
lines.push(`export default actions;`);
|
|
171
|
-
lines.push(`export { actions };`);
|
|
172
|
-
return `${lines.join("\n")}\n`;
|
|
173
|
-
}
|
|
174
|
-
function pipeResponse(req, res, response) {
|
|
175
|
-
return new Promise((resolve, reject) => {
|
|
176
|
-
res.statusCode = response.status;
|
|
177
|
-
response.headers.forEach((value, key) => {
|
|
178
|
-
res.setHeader(key, value);
|
|
179
|
-
});
|
|
180
|
-
if (!response.body) {
|
|
181
|
-
res.end();
|
|
182
|
-
resolve();
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
const reader = response.body.getReader();
|
|
186
|
-
const abort = () => {
|
|
187
|
-
reader.cancel();
|
|
188
|
-
};
|
|
189
|
-
req.once("aborted", abort);
|
|
190
|
-
const pull = () => {
|
|
191
|
-
reader.read().then(({ done, value }) => {
|
|
192
|
-
if (done) {
|
|
193
|
-
req.off("aborted", abort);
|
|
194
|
-
res.end();
|
|
195
|
-
resolve();
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
if (value) res.write(value);
|
|
199
|
-
pull();
|
|
200
|
-
}, reject);
|
|
201
|
-
};
|
|
202
|
-
pull();
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
function generateWorkerWrapper(userWorkerAbs, opts = {}) {
|
|
206
|
-
const preset = opts.preset ?? "fetch";
|
|
207
|
-
const clientDir = opts.clientDir ?? "client";
|
|
208
|
-
const actionPath = opts.actionPath ?? "/__oxide/action";
|
|
209
|
-
const sameOrigin = opts.actionSameOrigin ?? false;
|
|
210
|
-
const serveAssets = preset === "fetch" && (opts.hasClient === true || opts.hasPublic === true);
|
|
211
|
-
const hasActions = opts.hasActions !== false;
|
|
212
|
-
const ws = hasActions && opts.actions === "ws";
|
|
213
|
-
const bodyLimit = opts.bodyLimit ?? 1048576;
|
|
214
|
-
const nfBlock = `const __nf = ${`() => new Response(${JSON.stringify(opts.notFound ?? "<h1>404 Not Found</h1>")}, { status: 404, headers: { "content-type": "text/html; charset=utf-8" } })`};\n`;
|
|
215
|
-
const assetBlock = serveAssets ? `import { readFile } from "node:fs/promises";
|
|
216
|
-
import { extname, join } from "node:path";
|
|
217
|
-
const __assets = join(import.meta.dirname, ${JSON.stringify(clientDir)});
|
|
218
|
-
const __types = { ".html": "text/html; charset=utf-8", ".js": "text/javascript", ".css": "text/css", ".json": "application/json", ".svg": "image/svg+xml", ".png": "image/png", ".ico": "image/x-icon", ".woff2": "font/woff2", ".webp": "image/webp" };
|
|
219
|
-
function __nav(request) {
|
|
220
|
-
const dest = request.headers.get("sec-fetch-dest");
|
|
221
|
-
if (dest) return dest === "document";
|
|
222
|
-
return (request.headers.get("accept") ?? "").includes("text/html");
|
|
223
|
-
}
|
|
224
|
-
function __cache(file) {
|
|
225
|
-
if (file === "index.html") return "no-cache";
|
|
226
|
-
return /[-.][0-9a-f]{8,}.[a-z0-9]+$/i.test(file) ? "public, max-age=31536000, immutable" : undefined;
|
|
227
|
-
}
|
|
228
|
-
function __rel(pathname, spa) {
|
|
229
|
-
if (pathname.includes("\0")) return;
|
|
230
|
-
let file;
|
|
231
|
-
try { file = decodeURIComponent(pathname); } catch { return; }
|
|
232
|
-
if (file.includes("\0")) return;
|
|
233
|
-
if (file === "/" || spa) file = "/index.html";
|
|
234
|
-
if (!file.startsWith("/") || file.split("/").includes("..")) return;
|
|
235
|
-
const rel = file.slice(1);
|
|
236
|
-
if (rel.startsWith("/")) return;
|
|
237
|
-
return rel;
|
|
238
|
-
}
|
|
239
|
-
async function __asset(request, spa) {
|
|
240
|
-
const file = __rel(new URL(request.url).pathname, spa);
|
|
241
|
-
if (!file) return;
|
|
242
|
-
try {
|
|
243
|
-
const body = await readFile(join(__assets, file));
|
|
244
|
-
const headers = { "content-type": __types[extname(file)] ?? "application/octet-stream" };
|
|
245
|
-
const cache = __cache(file);
|
|
246
|
-
if (cache) headers["cache-control"] = cache;
|
|
247
|
-
const etag = '"' + body.length.toString(16) + "-" + file + '"';
|
|
248
|
-
headers["etag"] = etag;
|
|
249
|
-
if (request.headers.get("if-none-match") === etag) {
|
|
250
|
-
return new Response(null, { status: 304, headers });
|
|
251
|
-
}
|
|
252
|
-
return new Response(body, { headers });
|
|
253
|
-
} catch {
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
` : "";
|
|
258
|
-
const envJson = JSON.stringify(opts.env ?? {});
|
|
259
|
-
const afterAction = serveAssets ? `if (typeof user.fetch === "function") {
|
|
260
|
-
const hit = await user.fetch(request, env ?? ${envJson}, ctx);
|
|
261
|
-
if (hit) return hit;
|
|
262
|
-
}
|
|
263
|
-
return (await __asset(request)) ?? (__nav(request) ? await __asset(request, true) : undefined) ?? __nf();` : `return typeof user.fetch === "function"
|
|
264
|
-
? user.fetch(request, env, ctx)
|
|
265
|
-
: __nf();`;
|
|
266
|
-
const listen = preset === "fetch" ? `
|
|
267
|
-
import { createServer } from "node:http";
|
|
268
|
-
import { pathToFileURL } from "node:url";
|
|
269
|
-
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
270
|
-
const port = Number(process.env.PORT) || 3000;
|
|
271
|
-
const bodyLimit = ${bodyLimit};
|
|
272
|
-
const server = createServer(async (req, res) => {
|
|
273
|
-
const url = \`http://\${req.headers.host ?? "localhost"}\${req.url ?? "/"}\`;
|
|
274
|
-
const headers = new Headers();
|
|
275
|
-
for (const [key, value] of Object.entries(req.headers)) {
|
|
276
|
-
if (value === undefined) continue;
|
|
277
|
-
if (Array.isArray(value)) for (const item of value) headers.append(key, item);
|
|
278
|
-
else headers.set(key, value);
|
|
279
|
-
}
|
|
280
|
-
const ac = new AbortController();
|
|
281
|
-
req.once("aborted", () => ac.abort());
|
|
282
|
-
const method = req.method ?? "GET";
|
|
283
|
-
const chunks = [];
|
|
284
|
-
let size = 0;
|
|
285
|
-
if (method !== "GET" && method !== "HEAD") for await (const chunk of req) {
|
|
286
|
-
size += chunk.length;
|
|
287
|
-
// Bound request buffering — unbounded bodies are a memory DoS vector.
|
|
288
|
-
if (size > ${bodyLimit}) { res.statusCode = 413; res.end(); return; }
|
|
289
|
-
chunks.push(chunk);
|
|
290
|
-
}
|
|
291
|
-
const init = { method, headers, signal: ac.signal };
|
|
292
|
-
if (chunks.length) init.body = Buffer.concat(chunks);
|
|
293
|
-
const response = await app.fetch(new Request(url, init));
|
|
294
|
-
res.statusCode = response.status;
|
|
295
|
-
response.headers.forEach((value, key) => res.setHeader(key, value));
|
|
296
|
-
if (!response.body) { res.end(); return; }
|
|
297
|
-
const reader = response.body.getReader();
|
|
298
|
-
for (;;) {
|
|
299
|
-
const { done, value } = await reader.read();
|
|
300
|
-
if (done) break;
|
|
301
|
-
if (value) res.write(value);
|
|
302
|
-
}
|
|
303
|
-
res.end();
|
|
304
|
-
});${ws ? `
|
|
305
|
-
import("crossws/adapters/node").then(({ default: crossws }) => {
|
|
306
|
-
const ws = crossws({ hooks: __ws });
|
|
307
|
-
server.on("upgrade", (req, socket, head) => {
|
|
308
|
-
if (req.url?.split("?")[0] === ${JSON.stringify(actionPath)}) ws.handleUpgrade(req, socket, head);
|
|
309
|
-
});
|
|
310
|
-
});` : ""}
|
|
311
|
-
server.listen(port, () => console.log(\`oxidejs listening on \${port}\`));
|
|
312
|
-
}
|
|
313
|
-
for (const signal of ["SIGTERM", "SIGINT"]) {
|
|
314
|
-
process.on(signal, () => {
|
|
315
|
-
server.close(() => process.exit(0));
|
|
316
|
-
setTimeout(() => process.exit(0), 5000).unref();
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
` : "";
|
|
320
|
-
const actionImports = hasActions ? ws ? `import { handle as handleWs } from "tacho/transport/ws";
|
|
321
|
-
import actions from ${JSON.stringify(VIRTUAL_ACTIONS_ID)};
|
|
322
|
-
const __ws = handleWs(actions, { path: ${JSON.stringify(actionPath)}${sameOrigin ? `, sameOrigin: true` : ``} });
|
|
323
|
-
` : `import { handle } from "tacho/transport/fetch";
|
|
324
|
-
import actions from ${JSON.stringify(VIRTUAL_ACTIONS_ID)};
|
|
325
|
-
const __fetch = Symbol.for("oxidejs.fetch");
|
|
326
|
-
const __rpc = handle(actions, { path: ${JSON.stringify(actionPath)}${sameOrigin ? `, sameOrigin: true` : ``}, createContext: (req) => req[__fetch] ?? {} });
|
|
327
|
-
` : "";
|
|
328
|
-
const actionGate = hasActions && !ws ? `if (new URL(request.url).pathname === ${JSON.stringify(actionPath)}) {
|
|
329
|
-
return __rpc(request);
|
|
330
|
-
}
|
|
331
|
-
` : "";
|
|
332
|
-
const ILHA_SSR_IMPLICIT = ["ilha:pages/server", "ilha:loaders"];
|
|
333
|
-
const middlewareImports = (opts.middleware ?? []).map((m) => typeof m === "string" ? {
|
|
334
|
-
module: m,
|
|
335
|
-
imports: m === "@ilha/router/ssr" ? ILHA_SSR_IMPLICIT : []
|
|
336
|
-
} : m).map((m, i) => (m.imports ?? []).map((spec) => `import ${JSON.stringify(spec)};`).join("\n") + `\nimport __mw${i} from ${JSON.stringify(m.module)};`).join("\n") + "\n";
|
|
337
|
-
const middlewareList = (opts.middleware ?? []).map((_, i) => `__mw${i}`).join(", ");
|
|
338
|
-
const middlewareGate = opts.middleware?.length ? `for (const __mw of [${middlewareList}]) {
|
|
339
|
-
const hit = await __mw(request, { env, ctx });
|
|
340
|
-
if (hit) return hit;
|
|
341
|
-
}
|
|
342
|
-
` : "";
|
|
343
|
-
return `${(opts.imports ?? []).map((spec) => `import ${JSON.stringify(spec)};`).join("\n")}export * from ${JSON.stringify(userWorkerAbs)};
|
|
344
|
-
import user from ${JSON.stringify(userWorkerAbs)};
|
|
345
|
-
${middlewareImports}${actionImports}${assetBlock}${nfBlock}const app = {
|
|
346
|
-
...user,
|
|
347
|
-
async fetch(request, env, ctx) {
|
|
348
|
-
request[__fetch] = { env, fetchCtx: ctx };
|
|
349
|
-
${middlewareGate}${actionGate}${afterAction}
|
|
350
|
-
},
|
|
351
|
-
};
|
|
352
|
-
export default app;
|
|
353
|
-
${listen}`;
|
|
354
|
-
}
|
|
355
|
-
const SERVER_TARGETS = /* @__PURE__ */ new Set([
|
|
356
|
-
"node",
|
|
357
|
-
"async-node",
|
|
358
|
-
"webworker",
|
|
359
|
-
"web-worker"
|
|
360
|
-
]);
|
|
361
|
-
const CLIENT_TARGETS = /* @__PURE__ */ new Set(["web", "browserslist"]);
|
|
362
|
-
const SERVER_NAMES = /* @__PURE__ */ new Set([
|
|
363
|
-
"server",
|
|
364
|
-
"ssr",
|
|
365
|
-
"worker",
|
|
366
|
-
"node"
|
|
367
|
-
]);
|
|
368
|
-
const CLIENT_NAMES = /* @__PURE__ */ new Set(["client", "web"]);
|
|
369
|
-
/** Stub unless the graph is a known server. Unknown graphs stub so *.server.ts never ships. */
|
|
370
|
-
function shouldStubServerModule(environment, extra) {
|
|
371
|
-
if (extra?.ssr) return false;
|
|
372
|
-
const consumer = environment?.config?.consumer ?? environment?.consumer;
|
|
373
|
-
if (consumer === "server") return false;
|
|
374
|
-
if (consumer === "client") return true;
|
|
375
|
-
const name = environment?.name;
|
|
376
|
-
if (name && CLIENT_NAMES.has(name)) return true;
|
|
377
|
-
if (name && SERVER_NAMES.has(name)) return false;
|
|
378
|
-
const targets = extra?.target == null ? [] : [extra.target].flat();
|
|
379
|
-
if (targets.some((target) => SERVER_TARGETS.has(target))) return false;
|
|
380
|
-
if (targets.some((target) => CLIENT_TARGETS.has(target))) return true;
|
|
381
|
-
return true;
|
|
382
|
-
}
|
|
383
|
-
function pluginShouldStub(pluginThis, options) {
|
|
384
|
-
const ctx = pluginThis;
|
|
385
|
-
const compiler = ctx.getNativeBuildContext?.()?.compiler;
|
|
386
|
-
const env = {};
|
|
387
|
-
const name = ctx.environment?.name ?? compiler?.name ?? compiler?.options?.name;
|
|
388
|
-
if (name) env.name = name;
|
|
389
|
-
if (ctx.environment?.consumer) env.consumer = ctx.environment.consumer;
|
|
390
|
-
if (ctx.environment?.config) env.config = ctx.environment.config;
|
|
391
|
-
const extra = {};
|
|
392
|
-
if (options?.ssr) extra.ssr = true;
|
|
393
|
-
if (compiler?.options?.target) extra.target = compiler.options.target;
|
|
394
|
-
return shouldStubServerModule(env, extra);
|
|
395
|
-
}
|
|
396
|
-
function loadClientStub(id) {
|
|
397
|
-
const file = id.split("?")[0] ?? id;
|
|
398
|
-
return generateClientStub({
|
|
399
|
-
key: moduleKey(file),
|
|
400
|
-
exports: parseExportedNames(fs.readFileSync(file, "utf8"))
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
var RequestBodyTooLargeError = class extends Error {};
|
|
404
|
-
async function nodeToWebRequest(req, maxBytes = Number.POSITIVE_INFINITY) {
|
|
405
|
-
const url = `http://${req.headers.host ?? "localhost"}${req.url ?? "/"}`;
|
|
406
|
-
const headers = new Headers();
|
|
407
|
-
for (const [key, value] of Object.entries(req.headers)) {
|
|
408
|
-
if (value === void 0) continue;
|
|
409
|
-
if (Array.isArray(value)) for (const item of value) headers.append(key, item);
|
|
410
|
-
else headers.set(key, value);
|
|
411
|
-
}
|
|
412
|
-
const ac = new AbortController();
|
|
413
|
-
req.once("aborted", () => ac.abort());
|
|
414
|
-
const method = req.method ?? "GET";
|
|
415
|
-
const init = {
|
|
416
|
-
method,
|
|
417
|
-
headers,
|
|
418
|
-
signal: ac.signal
|
|
419
|
-
};
|
|
420
|
-
if (method === "GET" || method === "HEAD") return new Request(url, init);
|
|
421
|
-
const chunks = [];
|
|
422
|
-
let size = 0;
|
|
423
|
-
for await (const chunk of req) {
|
|
424
|
-
const buffer = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
425
|
-
size += buffer.length;
|
|
426
|
-
if (size > maxBytes) throw new RequestBodyTooLargeError();
|
|
427
|
-
chunks.push(buffer);
|
|
428
|
-
}
|
|
429
|
-
const body = Buffer.concat(chunks);
|
|
430
|
-
if (body.length > 0) init.body = body;
|
|
431
|
-
return new Request(url, init);
|
|
432
|
-
}
|
|
433
|
-
async function sendWebResponseFrom(req, res, response) {
|
|
434
|
-
return pipeResponse(req, res, response);
|
|
435
|
-
}
|
|
436
|
-
//#endregion
|
|
437
|
-
//#region src/core.ts
|
|
438
|
-
const CELLD_ALLOWED_KEYS = [
|
|
439
|
-
"name",
|
|
440
|
-
"main",
|
|
441
|
-
"compatibility_date",
|
|
442
|
-
"compatibility_flags",
|
|
443
|
-
"durable_objects",
|
|
444
|
-
"migrations",
|
|
445
|
-
"assets",
|
|
446
|
-
"services",
|
|
447
|
-
"vars"
|
|
448
|
-
];
|
|
449
|
-
const USER_FORBIDDEN_KEYS = ["main", "assets"];
|
|
450
|
-
function createEmitState() {
|
|
451
|
-
return { emitted: false };
|
|
452
|
-
}
|
|
453
|
-
function validateWranglerOptions(wrangler) {
|
|
454
|
-
const allowed = CELLD_ALLOWED_KEYS;
|
|
455
|
-
const invalid = Object.keys(wrangler).filter((key) => !allowed.includes(key));
|
|
456
|
-
if (invalid.length) throw new Error(`oxidejs: these wrangler keys are not supported by celld deploy: ${invalid.join(", ")}`);
|
|
457
|
-
const forbidden = USER_FORBIDDEN_KEYS.filter((key) => key in wrangler);
|
|
458
|
-
if (forbidden.length) throw new Error(`oxidejs: wrangler keys ${forbidden.join(", ")} are computed by the plugin and cannot be user-supplied`);
|
|
459
|
-
}
|
|
460
|
-
function assertContained(outDirAbs, childAbs, label) {
|
|
461
|
-
const outDir = path.resolve(outDirAbs);
|
|
462
|
-
const child = path.resolve(childAbs);
|
|
463
|
-
const relative = path.relative(outDir, child);
|
|
464
|
-
if (relative === "" || relative.startsWith("..") || path.isAbsolute(relative)) throw new Error(`oxidejs: ${label} must resolve inside outDir (got ${relative || "."})`);
|
|
465
|
-
}
|
|
466
|
-
function requireWranglerFields(wrangler) {
|
|
467
|
-
if (!wrangler?.name || !wrangler.compatibility_date) throw new Error("oxidejs: wrangler.name and wrangler.compatibility_date are required when emitConfig is true");
|
|
468
|
-
return wrangler;
|
|
469
|
-
}
|
|
470
|
-
function flattenInput(input) {
|
|
471
|
-
if (!input) return [];
|
|
472
|
-
if (typeof input === "string") return [input];
|
|
473
|
-
if (Array.isArray(input)) return input;
|
|
474
|
-
return Object.values(input);
|
|
475
|
-
}
|
|
476
|
-
function envInput(env) {
|
|
477
|
-
if (!env || typeof env !== "object") return;
|
|
478
|
-
const rec = env;
|
|
479
|
-
return rec.build?.rolldownOptions?.input || rec.build?.rollupOptions?.input || rec.build?.input || rec.input;
|
|
480
|
-
}
|
|
481
|
-
/** Vite client input: rolldown/rollup `input`, else `path.resolve(root, "index.html")`. */
|
|
482
|
-
function hasHtmlEntry(root, config) {
|
|
483
|
-
const cfg = config;
|
|
484
|
-
return flattenInput(envInput(cfg?.environments?.["client"]) || envInput(cfg?.environments?.["web"]) || cfg?.build?.rolldownOptions?.input || cfg?.build?.rollupOptions?.input || path.resolve(root, "index.html")).some((entry) => {
|
|
485
|
-
const file = path.resolve(root, entry);
|
|
486
|
-
return file.endsWith(".html") && fs.existsSync(file);
|
|
487
|
-
});
|
|
488
|
-
}
|
|
489
|
-
function resolveActions(raw) {
|
|
490
|
-
if (raw === void 0 || typeof raw === "string") {
|
|
491
|
-
const transport = raw ?? "http";
|
|
492
|
-
if (transport !== "http" && transport !== "ws") throw new Error(`oxidejs: unknown actions transport "${String(transport)}"`);
|
|
493
|
-
return {
|
|
494
|
-
transport,
|
|
495
|
-
path: ACTION_PATH,
|
|
496
|
-
sameOrigin: true
|
|
497
|
-
};
|
|
498
|
-
}
|
|
499
|
-
const transport = raw.transport ?? "http";
|
|
500
|
-
if (transport !== "http" && transport !== "ws") throw new Error(`oxidejs: unknown actions transport "${String(transport)}"`);
|
|
501
|
-
const path = raw.path ?? "/__oxide/action";
|
|
502
|
-
if (!path.startsWith("/") || path.includes("?")) throw new Error(`oxidejs: actions.path must start with "/" and contain no query string (got "${path}")`);
|
|
503
|
-
return {
|
|
504
|
-
transport,
|
|
505
|
-
path,
|
|
506
|
-
sameOrigin: raw.sameOrigin ?? true
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
function resolveOptions(raw, root, config) {
|
|
510
|
-
const preset = raw?.preset ?? "fetch";
|
|
511
|
-
if (preset !== "fetch" && preset !== "celld") throw new Error(`oxidejs: unknown preset "${String(preset)}"`);
|
|
512
|
-
const { transport: actions, path: actionPath, sameOrigin: actionSameOrigin } = resolveActions(raw?.actions);
|
|
513
|
-
if (actions === "ws" && preset === "celld") throw new Error("oxidejs: actions: \"ws\" is not supported with preset: \"celld\"");
|
|
514
|
-
const workerEntry = raw?.workerEntry ?? "src/server.ts";
|
|
515
|
-
const outDirInput = raw?.outDir ?? "dist";
|
|
516
|
-
const clientDir = raw?.clientDir ?? "client";
|
|
517
|
-
const emitConfig = raw?.emitConfig ?? preset === "celld";
|
|
518
|
-
const rootAbs = path.resolve(root);
|
|
519
|
-
const outDir = path.resolve(rootAbs, outDirInput);
|
|
520
|
-
const workerEntryAbs = path.resolve(rootAbs, workerEntry);
|
|
521
|
-
const hasClient = hasHtmlEntry(rootAbs, config);
|
|
522
|
-
const hasPublic = fs.existsSync(path.join(rootAbs, "public"));
|
|
523
|
-
if (hasClient || hasPublic) assertContained(outDir, path.resolve(outDir, clientDir), "clientDir");
|
|
524
|
-
if (raw?.wrangler) validateWranglerOptions(raw.wrangler);
|
|
525
|
-
return {
|
|
526
|
-
root: rootAbs,
|
|
527
|
-
preset,
|
|
528
|
-
workerEntry,
|
|
529
|
-
workerEntryAbs,
|
|
530
|
-
outDir,
|
|
531
|
-
clientDir,
|
|
532
|
-
wrangler: emitConfig ? requireWranglerFields(raw?.wrangler) : raw?.wrangler,
|
|
533
|
-
emitConfig,
|
|
534
|
-
hasClient,
|
|
535
|
-
hasPublic,
|
|
536
|
-
actions,
|
|
537
|
-
actionPath,
|
|
538
|
-
actionSameOrigin,
|
|
539
|
-
actionHeaders: raw?.actionHeaders,
|
|
540
|
-
middleware: raw?.middleware ?? [],
|
|
541
|
-
imports: raw?.imports ?? [],
|
|
542
|
-
bodyLimit: raw?.bodyLimit ?? 1048576,
|
|
543
|
-
notFound: raw?.notFound,
|
|
544
|
-
env: raw?.env
|
|
545
|
-
};
|
|
546
|
-
}
|
|
547
|
-
function copyPublicDir(opts) {
|
|
548
|
-
if (opts.preset !== "fetch") return;
|
|
549
|
-
const src = path.join(opts.root, "public");
|
|
550
|
-
if (!fs.existsSync(src)) return;
|
|
551
|
-
fs.cpSync(src, path.join(opts.outDir, opts.clientDir), {
|
|
552
|
-
recursive: true,
|
|
553
|
-
force: true
|
|
554
|
-
});
|
|
555
|
-
}
|
|
556
|
-
function tryEmitWranglerConfig(opts, state) {
|
|
557
|
-
if (state.emitted || opts.emitConfig === false) return;
|
|
558
|
-
const wrangler = requireWranglerFields(opts.wrangler);
|
|
559
|
-
const serverFile = path.join(opts.outDir, "server.js");
|
|
560
|
-
const clientDirPath = path.join(opts.outDir, opts.clientDir);
|
|
561
|
-
if (!fs.existsSync(serverFile)) return;
|
|
562
|
-
if (opts.hasClient && !fs.existsSync(clientDirPath)) return;
|
|
563
|
-
assertContained(opts.outDir, serverFile, "main");
|
|
564
|
-
if (opts.hasClient) assertContained(opts.outDir, clientDirPath, "assets.directory");
|
|
565
|
-
const config = {
|
|
566
|
-
name: wrangler.name,
|
|
567
|
-
main: "./server.js",
|
|
568
|
-
compatibility_date: wrangler.compatibility_date,
|
|
569
|
-
...wrangler.compatibility_flags ? { compatibility_flags: wrangler.compatibility_flags } : {},
|
|
570
|
-
...wrangler.durable_objects ? { durable_objects: wrangler.durable_objects } : {},
|
|
571
|
-
...wrangler.migrations ? { migrations: wrangler.migrations } : {},
|
|
572
|
-
...wrangler.services ? { services: wrangler.services } : {},
|
|
573
|
-
...wrangler.vars ? { vars: wrangler.vars } : {},
|
|
574
|
-
...opts.hasClient ? { assets: {
|
|
575
|
-
directory: `./${opts.clientDir}`,
|
|
576
|
-
binding: "ASSETS"
|
|
577
|
-
} } : {}
|
|
578
|
-
};
|
|
579
|
-
fs.writeFileSync(path.join(opts.outDir, "wrangler.jsonc"), `${JSON.stringify(config, null, 2)}\n`);
|
|
580
|
-
state.emitted = true;
|
|
581
|
-
}
|
|
582
|
-
//#endregion
|
|
583
|
-
//#region src/worker-build.ts
|
|
584
|
-
function applyViteEnvironments(config, opts) {
|
|
585
|
-
config.builder ??= {};
|
|
586
|
-
config.environments ??= {};
|
|
587
|
-
const celld = opts.preset === "celld";
|
|
588
|
-
config.environments["ssr"] = {
|
|
589
|
-
consumer: "server",
|
|
590
|
-
build: {
|
|
591
|
-
outDir: opts.outDir,
|
|
592
|
-
emptyOutDir: true,
|
|
593
|
-
ssr: true,
|
|
594
|
-
rolldownOptions: {
|
|
595
|
-
input: VIRTUAL_WORKER_ID,
|
|
596
|
-
external: celld ? [/^cloudflare:/] : [],
|
|
597
|
-
output: {
|
|
598
|
-
format: "es",
|
|
599
|
-
entryFileNames: "server.js"
|
|
600
|
-
}
|
|
601
|
-
},
|
|
602
|
-
rollupOptions: {
|
|
603
|
-
input: VIRTUAL_WORKER_ID,
|
|
604
|
-
external: celld ? [/^cloudflare:/] : [],
|
|
605
|
-
output: {
|
|
606
|
-
format: "es",
|
|
607
|
-
entryFileNames: "server.js"
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
},
|
|
611
|
-
resolve: celld ? {
|
|
612
|
-
conditions: ["worker"],
|
|
613
|
-
noExternal: true
|
|
614
|
-
} : { noExternal: ["tacho"] },
|
|
615
|
-
ssr: celld ? {
|
|
616
|
-
target: "webworker",
|
|
617
|
-
noExternal: true,
|
|
618
|
-
external: [/^cloudflare:/]
|
|
619
|
-
} : { noExternal: ["tacho"] }
|
|
620
|
-
};
|
|
621
|
-
config.build ??= {};
|
|
622
|
-
if (opts.hasClient) {
|
|
623
|
-
const clientOutDir = path.join(opts.outDir, opts.clientDir);
|
|
624
|
-
const existingClient = config.environments["client"];
|
|
625
|
-
config.environments["client"] = {
|
|
626
|
-
...existingClient,
|
|
627
|
-
consumer: "client",
|
|
628
|
-
build: {
|
|
629
|
-
...existingClient?.build,
|
|
630
|
-
outDir: clientOutDir,
|
|
631
|
-
emptyOutDir: true,
|
|
632
|
-
manifest: true
|
|
633
|
-
}
|
|
634
|
-
};
|
|
635
|
-
config.environments["ssr"].build.emptyOutDir = false;
|
|
636
|
-
config.build.outDir ??= clientOutDir;
|
|
637
|
-
config.build.manifest ??= true;
|
|
638
|
-
} else {
|
|
639
|
-
delete config.environments["client"];
|
|
640
|
-
config.appType = "custom";
|
|
641
|
-
config.build.outDir ??= opts.outDir;
|
|
642
|
-
config.build.emptyOutDir ??= true;
|
|
643
|
-
config.builder.buildApp ??= async (builder) => {
|
|
644
|
-
const server = builder.environments["ssr"];
|
|
645
|
-
if (server) await builder.build(server);
|
|
646
|
-
};
|
|
647
|
-
}
|
|
648
|
-
return config;
|
|
649
|
-
}
|
|
650
|
-
function applyRsbuildEnvironments(config, opts) {
|
|
651
|
-
config.environments ??= {};
|
|
652
|
-
if (opts.hasClient) {
|
|
653
|
-
const clientOutDir = path.join(opts.outDir, opts.clientDir);
|
|
654
|
-
const existingClient = config.environments["web"] ?? config.environments["client"];
|
|
655
|
-
config.environments["web"] = {
|
|
656
|
-
...existingClient,
|
|
657
|
-
output: {
|
|
658
|
-
...existingClient?.output,
|
|
659
|
-
target: "web",
|
|
660
|
-
distPath: {
|
|
661
|
-
...existingClient?.output?.distPath,
|
|
662
|
-
root: clientOutDir
|
|
663
|
-
},
|
|
664
|
-
manifest: true
|
|
665
|
-
}
|
|
666
|
-
};
|
|
667
|
-
} else {
|
|
668
|
-
delete config.environments["web"];
|
|
669
|
-
delete config.environments["client"];
|
|
670
|
-
}
|
|
671
|
-
const server = {
|
|
672
|
-
source: { entry: { server: {
|
|
673
|
-
import: VIRTUAL_WORKER_ID,
|
|
674
|
-
html: false
|
|
675
|
-
} } },
|
|
676
|
-
output: {
|
|
677
|
-
target: opts.preset === "celld" ? "web-worker" : "node",
|
|
678
|
-
filename: { js: "server.js" },
|
|
679
|
-
distPath: { root: opts.outDir }
|
|
680
|
-
}
|
|
681
|
-
};
|
|
682
|
-
if (opts.preset === "celld") server.resolve = { conditionNames: ["worker", "..."] };
|
|
683
|
-
config.environments["server"] = server;
|
|
684
|
-
return config;
|
|
685
|
-
}
|
|
686
|
-
//#endregion
|
|
687
|
-
//#region src/context.ts
|
|
688
|
-
const ALS_KEY = Symbol.for("oxidejs.requestContext");
|
|
689
|
-
const FETCH_KEY = Symbol.for("oxidejs.fetch");
|
|
690
|
-
function als() {
|
|
691
|
-
const g = globalThis;
|
|
692
|
-
return g[ALS_KEY] ??= new AsyncLocalStorage();
|
|
693
|
-
}
|
|
694
|
-
function store() {
|
|
695
|
-
const current = als().getStore();
|
|
696
|
-
if (!current) throw new Error("oxidejs: request context is unavailable");
|
|
697
|
-
return current;
|
|
698
|
-
}
|
|
699
|
-
/** Current tacho or host request context. Throws outside request handling. */
|
|
700
|
-
function useCtx() {
|
|
701
|
-
return store();
|
|
702
|
-
}
|
|
703
|
-
/** Current server `Request`. Available in actions, SSR, and frame renders. */
|
|
704
|
-
function useRequest() {
|
|
705
|
-
return store().req;
|
|
706
|
-
}
|
|
707
|
-
/** Worker `env` from `fetch(request, env, ctx)`. `undefined` on the Node fetch preset. */
|
|
708
|
-
function useEnv() {
|
|
709
|
-
return store().env;
|
|
710
|
-
}
|
|
711
|
-
/** Worker `ctx` from `fetch(request, env, ctx)` (`waitUntil`). Not tacho `ctx`. `undefined` on Node. */
|
|
712
|
-
function useFetchCtx() {
|
|
713
|
-
return store().fetchCtx;
|
|
714
|
-
}
|
|
715
|
-
function runWithRequest(req, fn, extra) {
|
|
716
|
-
return als().run({
|
|
717
|
-
...extra,
|
|
718
|
-
req
|
|
719
|
-
}, fn);
|
|
720
|
-
}
|
|
721
|
-
const HOOK_KEY = Symbol.for("oxidejs.runWithRequest");
|
|
722
|
-
globalThis[HOOK_KEY] ??= (req, fn) => {
|
|
723
|
-
const extra = req[FETCH_KEY];
|
|
724
|
-
return runWithRequest(req, fn, extra);
|
|
725
|
-
};
|
|
726
|
-
/**
|
|
727
|
-
* Marks a `*.server.ts` export as a remote RPC action. Runtime identity; the
|
|
728
|
-
* second call signature adds the transport-only `{ signal }` argument.
|
|
729
|
-
*/
|
|
730
|
-
function action(fn) {
|
|
731
|
-
return fn;
|
|
732
|
-
}
|
|
733
|
-
//#endregion
|
|
734
|
-
//#region src/index.ts
|
|
735
|
-
function actionMiddleware(loadRouter, path, sameOrigin, bodyLimit) {
|
|
736
|
-
return (req, res, next) => {
|
|
737
|
-
if ((req.url ?? "").split("?")[0] !== path) {
|
|
738
|
-
next();
|
|
739
|
-
return;
|
|
740
|
-
}
|
|
741
|
-
(async () => {
|
|
742
|
-
const { handle } = await import(
|
|
743
|
-
/* @vite-ignore */
|
|
744
|
-
"tacho/transport/fetch"
|
|
745
|
-
);
|
|
746
|
-
await sendWebResponseFrom(req, res, await handle(await loadRouter(), {
|
|
747
|
-
path,
|
|
748
|
-
...sameOrigin ? { sameOrigin: true } : {}
|
|
749
|
-
})(await nodeToWebRequest(req, bodyLimit)));
|
|
750
|
-
})().catch((error) => {
|
|
751
|
-
if (error instanceof RequestBodyTooLargeError) {
|
|
752
|
-
res.statusCode = 413;
|
|
753
|
-
res.end();
|
|
754
|
-
} else next(error);
|
|
755
|
-
});
|
|
756
|
-
};
|
|
757
|
-
}
|
|
758
|
-
function attachActionUpgrade(httpServer, loadRouter, path, sameOrigin) {
|
|
759
|
-
if (!httpServer) return;
|
|
760
|
-
Promise.all([import(
|
|
761
|
-
/* @vite-ignore */
|
|
762
|
-
"tacho/transport/ws"
|
|
763
|
-
), import(
|
|
764
|
-
/* @vite-ignore */
|
|
765
|
-
"crossws/adapters/node"
|
|
766
|
-
)]).then(([{ handle }, { default: crossws }]) => {
|
|
767
|
-
httpServer.on("upgrade", (req, socket, head) => {
|
|
768
|
-
if ((req.url ?? "").split("?")[0] !== path) return;
|
|
769
|
-
loadRouter().then((router) => crossws({ hooks: handle(router, {
|
|
770
|
-
path,
|
|
771
|
-
sameOrigin
|
|
772
|
-
}) }).handleUpgrade(req, socket, head)).catch(() => {
|
|
773
|
-
socket.destroy();
|
|
774
|
-
});
|
|
775
|
-
});
|
|
776
|
-
});
|
|
777
|
-
}
|
|
778
|
-
function previewMiddleware(file) {
|
|
779
|
-
return (req, res, next) => {
|
|
780
|
-
(async () => {
|
|
781
|
-
await sendWebResponseFrom(req, res, await (await import(
|
|
782
|
-
/* @vite-ignore */
|
|
783
|
-
pathToFileURL(file).href
|
|
784
|
-
)).default.fetch(await nodeToWebRequest(req)));
|
|
785
|
-
})().catch(next);
|
|
786
|
-
};
|
|
787
|
-
}
|
|
788
|
-
function loadActions(root) {
|
|
789
|
-
const code = generateActionsModule(scanServerFiles(root), { bust: true });
|
|
790
|
-
return import(
|
|
791
|
-
/* @vite-ignore */
|
|
792
|
-
`data:text/javascript,${encodeURIComponent(code)}`
|
|
793
|
-
);
|
|
794
|
-
}
|
|
795
|
-
const unpluginFactory = (options) => {
|
|
796
|
-
let resolved;
|
|
797
|
-
const emitState = createEmitState();
|
|
798
|
-
return {
|
|
799
|
-
name: "oxidejs",
|
|
800
|
-
enforce: "pre",
|
|
801
|
-
buildStart() {
|
|
802
|
-
resolved ??= resolveOptions(options, process.cwd());
|
|
803
|
-
emitState.emitted = false;
|
|
804
|
-
},
|
|
805
|
-
resolveId(id) {
|
|
806
|
-
if (id === "virtual:oxide/actions") return RESOLVED_VIRTUAL_ACTIONS_ID;
|
|
807
|
-
if (id === "virtual:oxide/worker") return RESOLVED_VIRTUAL_WORKER_ID;
|
|
808
|
-
if (id === "virtual:oxide/client") return RESOLVED_VIRTUAL_CLIENT_ID;
|
|
809
|
-
return null;
|
|
810
|
-
},
|
|
811
|
-
load(id, extra) {
|
|
812
|
-
if (id === RESOLVED_VIRTUAL_CLIENT_ID) return generateClientModule(resolved?.actions ?? (typeof options?.actions === "string" || options?.actions === void 0 ? options?.actions : options.actions.transport) ?? "http", resolved?.actionHeaders ?? options?.actionHeaders, resolved?.actionPath);
|
|
813
|
-
if (id === RESOLVED_VIRTUAL_ACTIONS_ID || id === RESOLVED_VIRTUAL_WORKER_ID) {
|
|
814
|
-
if (pluginShouldStub(this, extra)) throw new Error(`oxidejs: ${id === RESOLVED_VIRTUAL_ACTIONS_ID ? VIRTUAL_ACTIONS_ID : VIRTUAL_WORKER_ID} is server-only`);
|
|
815
|
-
}
|
|
816
|
-
if (id === RESOLVED_VIRTUAL_ACTIONS_ID) {
|
|
817
|
-
const modules = scanServerFiles(resolved?.root ?? process.cwd());
|
|
818
|
-
for (const mod of modules) this.addWatchFile(mod.abs);
|
|
819
|
-
return generateActionsModule(modules);
|
|
820
|
-
}
|
|
821
|
-
if (id === RESOLVED_VIRTUAL_WORKER_ID) {
|
|
822
|
-
if (!resolved) return;
|
|
823
|
-
this.addWatchFile(resolved.workerEntryAbs);
|
|
824
|
-
const modules = scanServerFiles(resolved.root);
|
|
825
|
-
for (const mod of modules) this.addWatchFile(mod.abs);
|
|
826
|
-
return generateWorkerWrapper(resolved.workerEntryAbs, {
|
|
827
|
-
preset: resolved.preset,
|
|
828
|
-
clientDir: resolved.clientDir,
|
|
829
|
-
hasClient: resolved.hasClient,
|
|
830
|
-
hasPublic: resolved.hasPublic,
|
|
831
|
-
hasActions: modules.length > 0,
|
|
832
|
-
actions: resolved.actions,
|
|
833
|
-
actionPath: resolved.actionPath,
|
|
834
|
-
actionSameOrigin: resolved.actionSameOrigin,
|
|
835
|
-
middleware: resolved.middleware,
|
|
836
|
-
imports: resolved.imports,
|
|
837
|
-
bodyLimit: resolved.bodyLimit,
|
|
838
|
-
notFound: resolved.notFound,
|
|
839
|
-
env: resolved.env
|
|
840
|
-
});
|
|
841
|
-
}
|
|
842
|
-
if (isServerFileId(id) && pluginShouldStub(this, extra)) {
|
|
843
|
-
const file = id.split("?")[0] ?? id;
|
|
844
|
-
this.addWatchFile(file);
|
|
845
|
-
return loadClientStub(id);
|
|
846
|
-
}
|
|
847
|
-
},
|
|
848
|
-
transform(code, id, extra) {
|
|
849
|
-
if (!isServerFileId(id) || !pluginShouldStub(this, extra) || code.startsWith("// oxidejs:client-stub\n")) return;
|
|
850
|
-
return generateClientStub({
|
|
851
|
-
key: moduleKey(id.split("?")[0] ?? id),
|
|
852
|
-
exports: parseExportedNames(code)
|
|
853
|
-
});
|
|
854
|
-
},
|
|
855
|
-
vite: {
|
|
856
|
-
config(config) {
|
|
857
|
-
resolved = resolveOptions(options, typeof config.root === "string" ? config.root : process.cwd(), config);
|
|
858
|
-
applyViteEnvironments(config, resolved);
|
|
859
|
-
},
|
|
860
|
-
configureServer(server) {
|
|
861
|
-
const invalidateActions = () => {
|
|
862
|
-
for (const env of Object.values(server.environments)) {
|
|
863
|
-
const mod = env.moduleGraph.getModuleById(RESOLVED_VIRTUAL_ACTIONS_ID);
|
|
864
|
-
if (mod) env.moduleGraph.invalidateModule(mod);
|
|
865
|
-
}
|
|
866
|
-
};
|
|
867
|
-
server.watcher.on("all", (_event, file) => {
|
|
868
|
-
if (isServerFileId(file)) invalidateActions();
|
|
869
|
-
});
|
|
870
|
-
const loadRouter = async () => {
|
|
871
|
-
return (await server.ssrLoadModule(VIRTUAL_ACTIONS_ID)).default;
|
|
872
|
-
};
|
|
873
|
-
const wireActions = () => server.middlewares.use(actionMiddleware(loadRouter, resolved.actionPath, resolved.actionSameOrigin, resolved.bodyLimit));
|
|
874
|
-
if (resolved?.actions === "ws") {
|
|
875
|
-
attachActionUpgrade(server.httpServer, loadRouter, resolved.actionPath, resolved.actionSameOrigin);
|
|
876
|
-
return;
|
|
877
|
-
}
|
|
878
|
-
if ((resolved?.middleware?.length ?? 0) === 0 && (resolved?.imports?.length ?? 0) === 0) {
|
|
879
|
-
wireActions();
|
|
880
|
-
return;
|
|
881
|
-
}
|
|
882
|
-
(async () => {
|
|
883
|
-
try {
|
|
884
|
-
for (const spec of resolved.imports ?? []) await server.ssrLoadModule(spec);
|
|
885
|
-
const handlers = [];
|
|
886
|
-
for (const entry of resolved.middleware ?? []) {
|
|
887
|
-
const spec = typeof entry === "string" ? entry : entry.module;
|
|
888
|
-
const mod = await server.ssrLoadModule(spec);
|
|
889
|
-
if (typeof mod.default !== "function") continue;
|
|
890
|
-
const fn = mod.default;
|
|
891
|
-
handlers.push((request, context) => Promise.resolve(fn(request, context)));
|
|
892
|
-
}
|
|
893
|
-
if (handlers.length > 0) {
|
|
894
|
-
const { nodeToWebRequest, sendWebResponseFrom } = await Promise.resolve().then(() => actions_exports);
|
|
895
|
-
server.middlewares.use((creq, cres, next) => {
|
|
896
|
-
(async () => {
|
|
897
|
-
try {
|
|
898
|
-
const request = await nodeToWebRequest(creq, resolved.bodyLimit);
|
|
899
|
-
const context = {
|
|
900
|
-
env: resolved.env,
|
|
901
|
-
ctx: void 0
|
|
902
|
-
};
|
|
903
|
-
for (const handler of handlers) {
|
|
904
|
-
const hit = await handler(request, context);
|
|
905
|
-
if (hit) return sendWebResponseFrom(creq, cres, hit);
|
|
906
|
-
}
|
|
907
|
-
next();
|
|
908
|
-
} catch (error) {
|
|
909
|
-
cres.statusCode = error instanceof RequestBodyTooLargeError ? 413 : 500;
|
|
910
|
-
cres.end(error instanceof RequestBodyTooLargeError ? void 0 : String(error));
|
|
911
|
-
}
|
|
912
|
-
})();
|
|
913
|
-
});
|
|
914
|
-
}
|
|
915
|
-
} catch (error) {
|
|
916
|
-
server.config.logger.error("oxidejs: failed to wire dev middleware: " + String(error));
|
|
917
|
-
} finally {
|
|
918
|
-
wireActions();
|
|
919
|
-
}
|
|
920
|
-
})();
|
|
921
|
-
},
|
|
922
|
-
configurePreviewServer(server) {
|
|
923
|
-
if (resolved?.preset !== "fetch") return;
|
|
924
|
-
server.middlewares.use(previewMiddleware(path.join(resolved.outDir, "server.js")));
|
|
925
|
-
}
|
|
926
|
-
},
|
|
927
|
-
rsbuild: { setup(api) {
|
|
928
|
-
api.modifyRsbuildConfig((config) => {
|
|
929
|
-
resolved = resolveOptions(options, typeof config.root === "string" ? config.root : process.cwd(), config);
|
|
930
|
-
applyRsbuildEnvironments(config, resolved);
|
|
931
|
-
});
|
|
932
|
-
api.onBeforeStartDevServer(({ server }) => {
|
|
933
|
-
const loadRouter = async () => {
|
|
934
|
-
return (await loadActions(resolved?.root ?? process.cwd())).default;
|
|
935
|
-
};
|
|
936
|
-
if (resolved?.actions === "ws") attachActionUpgrade(server.httpServer, loadRouter, resolved.actionPath, resolved.actionSameOrigin);
|
|
937
|
-
else server.middlewares.use(actionMiddleware(loadRouter, resolved.actionPath, resolved.actionSameOrigin, resolved.bodyLimit));
|
|
938
|
-
});
|
|
939
|
-
api.onBeforeStartPreviewServer?.(({ server }) => {
|
|
940
|
-
if (resolved?.preset !== "fetch") return;
|
|
941
|
-
server.middlewares.use(previewMiddleware(path.join(resolved.outDir, "server.js")));
|
|
942
|
-
});
|
|
943
|
-
} },
|
|
944
|
-
writeBundle() {
|
|
945
|
-
if (!resolved) return;
|
|
946
|
-
copyPublicDir(resolved);
|
|
947
|
-
tryEmitWranglerConfig(resolved, emitState);
|
|
948
|
-
}
|
|
949
|
-
};
|
|
950
|
-
};
|
|
951
|
-
const oxidejs = /* @__PURE__ */ createUnplugin(unpluginFactory);
|
|
952
|
-
const vite = /* @__PURE__ */ (() => oxidejs.vite)();
|
|
953
|
-
//#endregion
|
|
954
|
-
export { useCtx as a, useRequest as c, action as i, unpluginFactory as n, useEnv as o, vite as r, useFetchCtx as s, oxidejs as t };
|