oxidejs 0.2.4 → 0.3.1
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 +24 -6
- package/dist/client-C-s6XXpS.mjs +148 -0
- package/dist/context-C1UFQ0Zc.d.mts +64 -0
- package/dist/context-zrTZyYpF.mjs +42 -0
- package/dist/index.d.mts +2 -29
- package/dist/index.mjs +103 -44
- package/dist/plugin-HZKRDuCS.mjs +602 -0
- package/dist/plugin.mjs +1 -1
- package/dist/rpc/client.d.mts +13 -0
- package/dist/rpc/client.mjs +2 -0
- package/dist/rpc-DzUkWWgQ.mjs +915 -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/plugin-nO0yjtk0.mjs +0 -954
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
import { A as pluginShouldStub, C as isServerFileId, D as nodeToWebRequest, E as moduleKey, M as sendWebResponseFrom, O as parseExportedNames, S as generateWorkerWrapper, T as matchesActionPath, b as generateClientModule, d as RESOLVED_VIRTUAL_CLIENT_ID, f as RESOLVED_VIRTUAL_WORKER_ID, g as VIRTUAL_WORKER_ID, j as scanServerFiles, k as parseStreamExports, l as ACTION_PATH, m as VIRTUAL_ACTIONS_ID, n as createActionHandler, p as RequestBodyTooLargeError, t as createWsHooks, u as RESOLVED_VIRTUAL_ACTIONS_ID, v as generateActionsClientModule, w as loadClientStub, x as generateClientStub, y as generateActionsModule } from "./rpc-DzUkWWgQ.mjs";
|
|
2
|
+
import { ensureWorkerDom } from "./worker-dom.mjs";
|
|
3
|
+
import { createUnplugin } from "unplugin";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
|
+
//#region src/core.ts
|
|
8
|
+
const CELLD_ALLOWED_KEYS = [
|
|
9
|
+
"name",
|
|
10
|
+
"main",
|
|
11
|
+
"compatibility_date",
|
|
12
|
+
"compatibility_flags",
|
|
13
|
+
"durable_objects",
|
|
14
|
+
"migrations",
|
|
15
|
+
"assets",
|
|
16
|
+
"services",
|
|
17
|
+
"vars"
|
|
18
|
+
];
|
|
19
|
+
const USER_FORBIDDEN_KEYS = ["main", "assets"];
|
|
20
|
+
function createEmitState() {
|
|
21
|
+
return { emitted: false };
|
|
22
|
+
}
|
|
23
|
+
function validateWranglerOptions(wrangler) {
|
|
24
|
+
const allowed = CELLD_ALLOWED_KEYS;
|
|
25
|
+
const invalid = Object.keys(wrangler).filter((key) => !allowed.includes(key));
|
|
26
|
+
if (invalid.length) throw new Error(`oxidejs: these wrangler keys are not supported by celld deploy: ${invalid.join(", ")}`);
|
|
27
|
+
const forbidden = USER_FORBIDDEN_KEYS.filter((key) => key in wrangler);
|
|
28
|
+
if (forbidden.length) throw new Error(`oxidejs: wrangler keys ${forbidden.join(", ")} are computed by the plugin and cannot be user-supplied`);
|
|
29
|
+
}
|
|
30
|
+
function assertContained(outDirAbs, childAbs, label) {
|
|
31
|
+
const outDir = path.resolve(outDirAbs);
|
|
32
|
+
const child = path.resolve(childAbs);
|
|
33
|
+
const relative = path.relative(outDir, child);
|
|
34
|
+
if (relative === "" || relative.startsWith("..") || path.isAbsolute(relative)) throw new Error(`oxidejs: ${label} must resolve inside outDir (got ${relative || "."})`);
|
|
35
|
+
}
|
|
36
|
+
function requireWranglerFields(wrangler) {
|
|
37
|
+
if (!wrangler?.name || !wrangler.compatibility_date) throw new Error("oxidejs: wrangler.name and wrangler.compatibility_date are required when emitConfig is true");
|
|
38
|
+
return wrangler;
|
|
39
|
+
}
|
|
40
|
+
function flattenInput(input) {
|
|
41
|
+
if (!input) return [];
|
|
42
|
+
if (typeof input === "string") return [input];
|
|
43
|
+
if (Array.isArray(input)) return input;
|
|
44
|
+
return Object.values(input);
|
|
45
|
+
}
|
|
46
|
+
function envInput(env) {
|
|
47
|
+
if (!env || typeof env !== "object") return;
|
|
48
|
+
const rec = env;
|
|
49
|
+
return rec.build?.rolldownOptions?.input || rec.build?.rollupOptions?.input || rec.build?.input || rec.input;
|
|
50
|
+
}
|
|
51
|
+
/** Vite client input: rolldown/rollup `input`, else `path.resolve(root, "index.html")`. */
|
|
52
|
+
function hasHtmlEntry(root, config) {
|
|
53
|
+
const cfg = config;
|
|
54
|
+
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) => {
|
|
55
|
+
const file = path.resolve(root, entry);
|
|
56
|
+
return file.endsWith(".html") && fs.existsSync(file);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function resolveActions(raw) {
|
|
60
|
+
if (raw === void 0 || typeof raw === "string") {
|
|
61
|
+
const transport = raw ?? "http";
|
|
62
|
+
if (transport !== "http" && transport !== "ws") throw new Error(`oxidejs: unknown actions transport "${String(transport)}"`);
|
|
63
|
+
return {
|
|
64
|
+
transport,
|
|
65
|
+
path: ACTION_PATH,
|
|
66
|
+
sameOrigin: true
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const transport = raw.transport ?? "http";
|
|
70
|
+
if (transport !== "http" && transport !== "ws") throw new Error(`oxidejs: unknown actions transport "${String(transport)}"`);
|
|
71
|
+
const path = raw.path ?? "/__oxide/action";
|
|
72
|
+
if (!path.startsWith("/") || path.includes("?")) throw new Error(`oxidejs: actions.path must start with "/" and contain no query string (got "${path}")`);
|
|
73
|
+
return {
|
|
74
|
+
transport,
|
|
75
|
+
path,
|
|
76
|
+
sameOrigin: raw.sameOrigin ?? true
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function resolveOptions(raw, root, config) {
|
|
80
|
+
const preset = raw?.preset ?? "fetch";
|
|
81
|
+
if (preset !== "fetch" && preset !== "celld") throw new Error(`oxidejs: unknown preset "${String(preset)}"`);
|
|
82
|
+
const { transport: actions, path: actionPath, sameOrigin: actionSameOrigin } = resolveActions(raw?.actions);
|
|
83
|
+
if (actions === "ws" && preset === "celld") throw new Error("oxidejs: actions: \"ws\" is not supported with preset: \"celld\"");
|
|
84
|
+
const workerEntry = raw?.workerEntry ?? "src/server.ts";
|
|
85
|
+
const outDirInput = raw?.outDir ?? "dist";
|
|
86
|
+
const clientDir = raw?.clientDir ?? "client";
|
|
87
|
+
const emitConfig = raw?.emitConfig ?? preset === "celld";
|
|
88
|
+
const rootAbs = path.resolve(root);
|
|
89
|
+
const outDir = path.resolve(rootAbs, outDirInput);
|
|
90
|
+
const workerEntryAbs = path.resolve(rootAbs, workerEntry);
|
|
91
|
+
const hasClient = hasHtmlEntry(rootAbs, config);
|
|
92
|
+
const hasPublic = fs.existsSync(path.join(rootAbs, "public"));
|
|
93
|
+
if (hasClient || hasPublic) assertContained(outDir, path.resolve(outDir, clientDir), "clientDir");
|
|
94
|
+
if (raw?.wrangler) validateWranglerOptions(raw.wrangler);
|
|
95
|
+
return {
|
|
96
|
+
root: rootAbs,
|
|
97
|
+
preset,
|
|
98
|
+
workerEntry,
|
|
99
|
+
workerEntryAbs,
|
|
100
|
+
outDir,
|
|
101
|
+
clientDir,
|
|
102
|
+
wrangler: emitConfig ? requireWranglerFields(raw?.wrangler) : raw?.wrangler,
|
|
103
|
+
emitConfig,
|
|
104
|
+
hasClient,
|
|
105
|
+
hasPublic,
|
|
106
|
+
actions,
|
|
107
|
+
actionPath,
|
|
108
|
+
actionSameOrigin,
|
|
109
|
+
actionHeaders: raw?.actionHeaders,
|
|
110
|
+
middleware: raw?.middleware ?? [],
|
|
111
|
+
imports: raw?.imports ?? [],
|
|
112
|
+
bodyLimit: raw?.bodyLimit ?? 1048576,
|
|
113
|
+
notFound: raw?.notFound,
|
|
114
|
+
env: raw?.env
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function copyPublicDir(opts) {
|
|
118
|
+
if (opts.preset !== "fetch") return;
|
|
119
|
+
const src = path.join(opts.root, "public");
|
|
120
|
+
if (!fs.existsSync(src)) return;
|
|
121
|
+
fs.cpSync(src, path.join(opts.outDir, opts.clientDir), {
|
|
122
|
+
recursive: true,
|
|
123
|
+
force: true
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function tryEmitWranglerConfig(opts, state) {
|
|
127
|
+
if (state.emitted || opts.emitConfig === false) return;
|
|
128
|
+
const wrangler = requireWranglerFields(opts.wrangler);
|
|
129
|
+
const serverFile = path.join(opts.outDir, "server.js");
|
|
130
|
+
const clientDirPath = path.join(opts.outDir, opts.clientDir);
|
|
131
|
+
if (!fs.existsSync(serverFile)) return;
|
|
132
|
+
if (opts.hasClient && !fs.existsSync(clientDirPath)) return;
|
|
133
|
+
assertContained(opts.outDir, serverFile, "main");
|
|
134
|
+
if (opts.hasClient) assertContained(opts.outDir, clientDirPath, "assets.directory");
|
|
135
|
+
const config = {
|
|
136
|
+
name: wrangler.name,
|
|
137
|
+
main: "./server.js",
|
|
138
|
+
compatibility_date: wrangler.compatibility_date,
|
|
139
|
+
compatibility_flags: [.../* @__PURE__ */ new Set([...wrangler.compatibility_flags ?? [], "nodejs_compat"])],
|
|
140
|
+
...wrangler.durable_objects ? { durable_objects: wrangler.durable_objects } : {},
|
|
141
|
+
...wrangler.migrations ? { migrations: wrangler.migrations } : {},
|
|
142
|
+
...wrangler.services ? { services: wrangler.services } : {},
|
|
143
|
+
...wrangler.vars ? { vars: wrangler.vars } : {},
|
|
144
|
+
...opts.hasClient ? { assets: {
|
|
145
|
+
directory: `./${opts.clientDir}`,
|
|
146
|
+
binding: "ASSETS"
|
|
147
|
+
} } : {}
|
|
148
|
+
};
|
|
149
|
+
fs.writeFileSync(path.join(opts.outDir, "wrangler.jsonc"), `${JSON.stringify(config, null, 2)}\n`);
|
|
150
|
+
state.emitted = true;
|
|
151
|
+
}
|
|
152
|
+
//#endregion
|
|
153
|
+
//#region src/worker-build.ts
|
|
154
|
+
/** Dev aliases so Vite transforms RPC helpers with the app's single effect copy. */
|
|
155
|
+
function oxideRpcAliases() {
|
|
156
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
157
|
+
const client = path.join(root, "src/rpc/client.ts");
|
|
158
|
+
if (!fs.existsSync(client)) return [];
|
|
159
|
+
return [{
|
|
160
|
+
find: /^oxidejs\/rpc\/client$/,
|
|
161
|
+
replacement: client
|
|
162
|
+
}, {
|
|
163
|
+
find: /^oxidejs\/rpc$/,
|
|
164
|
+
replacement: path.join(root, "src/rpc/index.ts")
|
|
165
|
+
}];
|
|
166
|
+
}
|
|
167
|
+
function mergeAliases(config, extra) {
|
|
168
|
+
if (extra.length === 0) return;
|
|
169
|
+
config.resolve ??= {};
|
|
170
|
+
const current = config.resolve.alias;
|
|
171
|
+
if (!current) {
|
|
172
|
+
config.resolve.alias = extra;
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (Array.isArray(current)) {
|
|
176
|
+
config.resolve.alias = [...current, ...extra];
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
config.resolve.alias = [...Object.entries(current).map(([find, replacement]) => ({
|
|
180
|
+
find,
|
|
181
|
+
replacement
|
|
182
|
+
})), ...extra];
|
|
183
|
+
}
|
|
184
|
+
const OPTIMIZE_DEPS = [
|
|
185
|
+
"effect",
|
|
186
|
+
"effect/unstable/rpc",
|
|
187
|
+
"effect/unstable/http",
|
|
188
|
+
"effect/unstable/socket",
|
|
189
|
+
"oxidejs",
|
|
190
|
+
"oxidejs/rpc/client"
|
|
191
|
+
];
|
|
192
|
+
function applyViteEnvironments(config, opts) {
|
|
193
|
+
config.builder ??= {};
|
|
194
|
+
config.resolve ??= {};
|
|
195
|
+
const dedupe = /* @__PURE__ */ new Set([
|
|
196
|
+
...Array.isArray(config.resolve.dedupe) ? config.resolve.dedupe : [],
|
|
197
|
+
"effect",
|
|
198
|
+
"oxidejs"
|
|
199
|
+
]);
|
|
200
|
+
config.resolve.dedupe = [...dedupe];
|
|
201
|
+
config.optimizeDeps ??= {};
|
|
202
|
+
const optimizeInclude = /* @__PURE__ */ new Set([...Array.isArray(config.optimizeDeps.include) ? config.optimizeDeps.include : [], ...OPTIMIZE_DEPS]);
|
|
203
|
+
config.optimizeDeps.include = [...optimizeInclude];
|
|
204
|
+
const celld = opts.preset === "celld";
|
|
205
|
+
mergeAliases(config, oxideRpcAliases());
|
|
206
|
+
config.environments ??= {};
|
|
207
|
+
config.environments["ssr"] = {
|
|
208
|
+
consumer: "server",
|
|
209
|
+
build: {
|
|
210
|
+
outDir: opts.outDir,
|
|
211
|
+
emptyOutDir: true,
|
|
212
|
+
ssr: true,
|
|
213
|
+
rolldownOptions: {
|
|
214
|
+
input: VIRTUAL_WORKER_ID,
|
|
215
|
+
external: celld ? [/^cloudflare:/] : [],
|
|
216
|
+
output: {
|
|
217
|
+
format: "es",
|
|
218
|
+
entryFileNames: "server.js"
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
rollupOptions: {
|
|
222
|
+
input: VIRTUAL_WORKER_ID,
|
|
223
|
+
external: celld ? [/^cloudflare:/] : [],
|
|
224
|
+
output: {
|
|
225
|
+
format: "es",
|
|
226
|
+
entryFileNames: "server.js"
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
resolve: celld ? {
|
|
231
|
+
conditions: ["worker"],
|
|
232
|
+
noExternal: true
|
|
233
|
+
} : { noExternal: ["effect", "oxidejs"] },
|
|
234
|
+
ssr: celld ? {
|
|
235
|
+
target: "webworker",
|
|
236
|
+
noExternal: true,
|
|
237
|
+
external: [/^cloudflare:/]
|
|
238
|
+
} : { noExternal: ["effect", "oxidejs"] }
|
|
239
|
+
};
|
|
240
|
+
config.build ??= {};
|
|
241
|
+
if (opts.hasClient) {
|
|
242
|
+
const clientOutDir = path.join(opts.outDir, opts.clientDir);
|
|
243
|
+
const existingClient = config.environments["client"];
|
|
244
|
+
config.environments["client"] = {
|
|
245
|
+
...existingClient,
|
|
246
|
+
consumer: "client",
|
|
247
|
+
build: {
|
|
248
|
+
...existingClient?.build,
|
|
249
|
+
outDir: clientOutDir,
|
|
250
|
+
emptyOutDir: true,
|
|
251
|
+
manifest: true
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
config.environments["ssr"].build.emptyOutDir = false;
|
|
255
|
+
config.build.outDir ??= clientOutDir;
|
|
256
|
+
config.build.manifest ??= true;
|
|
257
|
+
} else {
|
|
258
|
+
delete config.environments["client"];
|
|
259
|
+
config.appType = "custom";
|
|
260
|
+
config.build.outDir ??= opts.outDir;
|
|
261
|
+
config.build.emptyOutDir ??= true;
|
|
262
|
+
config.builder.buildApp ??= async (builder) => {
|
|
263
|
+
const server = builder.environments["ssr"];
|
|
264
|
+
if (server) await builder.build(server);
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
return config;
|
|
268
|
+
}
|
|
269
|
+
function applyRsbuildEnvironments(config, opts) {
|
|
270
|
+
config.environments ??= {};
|
|
271
|
+
if (opts.hasClient) {
|
|
272
|
+
const clientOutDir = path.join(opts.outDir, opts.clientDir);
|
|
273
|
+
const existingClient = config.environments["web"] ?? config.environments["client"];
|
|
274
|
+
config.environments["web"] = {
|
|
275
|
+
...existingClient,
|
|
276
|
+
output: {
|
|
277
|
+
...existingClient?.output,
|
|
278
|
+
target: "web",
|
|
279
|
+
distPath: {
|
|
280
|
+
...existingClient?.output?.distPath,
|
|
281
|
+
root: clientOutDir
|
|
282
|
+
},
|
|
283
|
+
manifest: true
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
} else {
|
|
287
|
+
delete config.environments["web"];
|
|
288
|
+
delete config.environments["client"];
|
|
289
|
+
}
|
|
290
|
+
const server = {
|
|
291
|
+
source: { entry: { server: {
|
|
292
|
+
import: VIRTUAL_WORKER_ID,
|
|
293
|
+
html: false
|
|
294
|
+
} } },
|
|
295
|
+
output: {
|
|
296
|
+
target: opts.preset === "celld" ? "web-worker" : "node",
|
|
297
|
+
filename: { js: "server.js" },
|
|
298
|
+
distPath: { root: opts.outDir }
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
if (opts.preset === "celld") server.resolve = { conditionNames: ["worker", "..."] };
|
|
302
|
+
config.environments["server"] = server;
|
|
303
|
+
return config;
|
|
304
|
+
}
|
|
305
|
+
//#endregion
|
|
306
|
+
//#region src/plugin.ts
|
|
307
|
+
function actionMiddleware(loadRouter, loadRpc, path, sameOrigin, bodyLimit, onError) {
|
|
308
|
+
return (req, res, next) => {
|
|
309
|
+
if (!matchesActionPath((req.url ?? "").split("?")[0] ?? "", path)) {
|
|
310
|
+
next();
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
(async () => {
|
|
314
|
+
const [mod, rpc] = await Promise.all([loadRouter(), loadRpc()]);
|
|
315
|
+
const response = await rpc.createActionHandler(mod.default, mod.actionsHandlers, {
|
|
316
|
+
path,
|
|
317
|
+
sameOrigin
|
|
318
|
+
})(await nodeToWebRequest(req, bodyLimit));
|
|
319
|
+
await sendWebResponseFrom(req, res, response);
|
|
320
|
+
})().catch((error) => {
|
|
321
|
+
if (res.headersSent) return;
|
|
322
|
+
if (error instanceof RequestBodyTooLargeError) {
|
|
323
|
+
res.statusCode = 413;
|
|
324
|
+
res.end();
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
onError?.(error);
|
|
328
|
+
res.statusCode = 503;
|
|
329
|
+
res.setHeader("content-type", "application/json");
|
|
330
|
+
res.end(JSON.stringify({ error: "oxide action handler failed" }));
|
|
331
|
+
});
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
function attachActionUpgrade(httpServer, loadRouter, loadRpc, path, sameOrigin) {
|
|
335
|
+
if (!httpServer) return;
|
|
336
|
+
import(
|
|
337
|
+
/* @vite-ignore */
|
|
338
|
+
"crossws/adapters/node"
|
|
339
|
+
).then(({ default: crossws }) => {
|
|
340
|
+
httpServer.on("upgrade", (req, socket, head) => {
|
|
341
|
+
if (!matchesActionPath((req.url ?? "").split("?")[0] ?? "", path)) return;
|
|
342
|
+
Promise.all([loadRouter(), loadRpc()]).then(([mod, rpc]) => crossws({ hooks: rpc.createWsHooks(mod.default, mod.actionsHandlers, {
|
|
343
|
+
path,
|
|
344
|
+
sameOrigin
|
|
345
|
+
}) }).handleUpgrade(req, socket, head)).catch(() => {
|
|
346
|
+
socket.destroy();
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
function previewMiddleware(file) {
|
|
352
|
+
return (req, res, next) => {
|
|
353
|
+
(async () => {
|
|
354
|
+
const mod = await import(
|
|
355
|
+
/* @vite-ignore */
|
|
356
|
+
pathToFileURL(file).href
|
|
357
|
+
);
|
|
358
|
+
await sendWebResponseFrom(req, res, await mod.default.fetch(await nodeToWebRequest(req)));
|
|
359
|
+
})().catch(next);
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
function loadActions(root) {
|
|
363
|
+
const code = generateActionsModule(scanServerFiles(root), { bust: true });
|
|
364
|
+
const dir = fs.mkdtempSync(path.join(root, ".oxide-actions-"));
|
|
365
|
+
const file = path.join(dir, "actions.mjs");
|
|
366
|
+
fs.writeFileSync(file, code);
|
|
367
|
+
return import(pathToFileURL(file).href).finally(() => {
|
|
368
|
+
fs.rmSync(dir, {
|
|
369
|
+
recursive: true,
|
|
370
|
+
force: true
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
const unpluginFactory = (options) => {
|
|
375
|
+
let resolved;
|
|
376
|
+
const emitState = createEmitState();
|
|
377
|
+
return {
|
|
378
|
+
name: "oxidejs",
|
|
379
|
+
enforce: "pre",
|
|
380
|
+
buildStart() {
|
|
381
|
+
resolved ??= resolveOptions(options, process.cwd());
|
|
382
|
+
emitState.emitted = false;
|
|
383
|
+
},
|
|
384
|
+
resolveId(id) {
|
|
385
|
+
if (id === "virtual:oxide/actions") return RESOLVED_VIRTUAL_ACTIONS_ID;
|
|
386
|
+
if (id === "virtual:oxide/worker") return RESOLVED_VIRTUAL_WORKER_ID;
|
|
387
|
+
if (id === "virtual:oxide/client") return RESOLVED_VIRTUAL_CLIENT_ID;
|
|
388
|
+
return null;
|
|
389
|
+
},
|
|
390
|
+
load(id, extra) {
|
|
391
|
+
if (id === RESOLVED_VIRTUAL_CLIENT_ID) {
|
|
392
|
+
const transport = resolved?.actions ?? (typeof options?.actions === "string" || options?.actions === void 0 ? options?.actions : options.actions.transport) ?? "http";
|
|
393
|
+
return generateClientModule(transport, resolved?.actionHeaders ?? options?.actionHeaders, resolved?.actionPath);
|
|
394
|
+
}
|
|
395
|
+
if (id === RESOLVED_VIRTUAL_WORKER_ID && pluginShouldStub(this, extra)) throw new Error(`oxidejs: ${VIRTUAL_WORKER_ID} is server-only`);
|
|
396
|
+
if (id === RESOLVED_VIRTUAL_ACTIONS_ID) {
|
|
397
|
+
const modules = scanServerFiles(resolved?.root ?? process.cwd());
|
|
398
|
+
if (pluginShouldStub(this, extra)) return generateActionsClientModule(modules);
|
|
399
|
+
for (const mod of modules) this.addWatchFile(mod.abs);
|
|
400
|
+
return generateActionsModule(modules);
|
|
401
|
+
}
|
|
402
|
+
if (id === RESOLVED_VIRTUAL_WORKER_ID) {
|
|
403
|
+
if (!resolved) return;
|
|
404
|
+
this.addWatchFile(resolved.workerEntryAbs);
|
|
405
|
+
const modules = scanServerFiles(resolved.root);
|
|
406
|
+
for (const mod of modules) this.addWatchFile(mod.abs);
|
|
407
|
+
return generateWorkerWrapper(resolved.workerEntryAbs, {
|
|
408
|
+
preset: resolved.preset,
|
|
409
|
+
clientDir: resolved.clientDir,
|
|
410
|
+
hasClient: resolved.hasClient,
|
|
411
|
+
hasPublic: resolved.hasPublic,
|
|
412
|
+
hasActions: modules.length > 0,
|
|
413
|
+
actions: resolved.actions,
|
|
414
|
+
actionPath: resolved.actionPath,
|
|
415
|
+
actionSameOrigin: resolved.actionSameOrigin,
|
|
416
|
+
middleware: resolved.middleware,
|
|
417
|
+
imports: resolved.imports,
|
|
418
|
+
bodyLimit: resolved.bodyLimit,
|
|
419
|
+
notFound: resolved.notFound,
|
|
420
|
+
env: resolved.env
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
if (isServerFileId(id) && pluginShouldStub(this, extra)) {
|
|
424
|
+
const file = id.split("?")[0] ?? id;
|
|
425
|
+
this.addWatchFile(file);
|
|
426
|
+
return loadClientStub(id);
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
transform(code, id, extra) {
|
|
430
|
+
if (!isServerFileId(id) || !pluginShouldStub(this, extra) || code.startsWith("// oxidejs:client-stub\n")) return;
|
|
431
|
+
return generateClientStub({
|
|
432
|
+
key: moduleKey(id.split("?")[0] ?? id),
|
|
433
|
+
exports: parseExportedNames(code),
|
|
434
|
+
streams: parseStreamExports(code)
|
|
435
|
+
});
|
|
436
|
+
},
|
|
437
|
+
vite: {
|
|
438
|
+
config(config) {
|
|
439
|
+
resolved = resolveOptions(options, typeof config.root === "string" ? config.root : process.cwd(), config);
|
|
440
|
+
applyViteEnvironments(config, resolved);
|
|
441
|
+
},
|
|
442
|
+
configureServer(server) {
|
|
443
|
+
if (resolved?.preset === "celld") {
|
|
444
|
+
ensureWorkerDom();
|
|
445
|
+
const ssr = server.environments.ssr;
|
|
446
|
+
const resolve = ssr?.config?.resolve;
|
|
447
|
+
if (resolve) {
|
|
448
|
+
resolve.conditions = [
|
|
449
|
+
"node",
|
|
450
|
+
"import",
|
|
451
|
+
"module",
|
|
452
|
+
"default"
|
|
453
|
+
];
|
|
454
|
+
resolve.noExternal = ["effect", "oxidejs"];
|
|
455
|
+
}
|
|
456
|
+
const ssrOpts = ssr?.config?.ssr;
|
|
457
|
+
if (ssrOpts && typeof ssrOpts === "object") {
|
|
458
|
+
ssrOpts.target = "node";
|
|
459
|
+
ssrOpts.noExternal = ["effect", "oxidejs"];
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
const invalidateActions = () => {
|
|
463
|
+
for (const env of Object.values(server.environments)) {
|
|
464
|
+
const mod = env.moduleGraph.getModuleById(RESOLVED_VIRTUAL_ACTIONS_ID);
|
|
465
|
+
if (mod) env.moduleGraph.invalidateModule(mod);
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
const fetchRouter = async () => {
|
|
469
|
+
return await server.ssrLoadModule(VIRTUAL_ACTIONS_ID);
|
|
470
|
+
};
|
|
471
|
+
let routerReady;
|
|
472
|
+
const loadRouter = () => {
|
|
473
|
+
const current = routerReady ??= fetchRouter();
|
|
474
|
+
return current.catch((error) => {
|
|
475
|
+
if (routerReady === current) routerReady = void 0;
|
|
476
|
+
throw error;
|
|
477
|
+
});
|
|
478
|
+
};
|
|
479
|
+
const refreshRouter = () => {
|
|
480
|
+
routerReady = void 0;
|
|
481
|
+
};
|
|
482
|
+
server.watcher.on("all", (_event, file) => {
|
|
483
|
+
if (isServerFileId(file)) {
|
|
484
|
+
invalidateActions();
|
|
485
|
+
refreshRouter();
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
const loadRpc = () => server.ssrLoadModule("oxidejs/rpc");
|
|
489
|
+
const logActionError = (error) => {
|
|
490
|
+
server.config.logger.error("oxidejs: action handler failed: " + String(error));
|
|
491
|
+
};
|
|
492
|
+
const wireActions = () => server.middlewares.use(actionMiddleware(loadRouter, loadRpc, resolved.actionPath, resolved.actionSameOrigin, resolved.bodyLimit, logActionError));
|
|
493
|
+
if (resolved?.actions === "ws") {
|
|
494
|
+
attachActionUpgrade(server.httpServer, loadRouter, loadRpc, resolved.actionPath, resolved.actionSameOrigin);
|
|
495
|
+
return async () => {
|
|
496
|
+
try {
|
|
497
|
+
await loadRouter();
|
|
498
|
+
} catch (error) {
|
|
499
|
+
server.config.logger.error("oxidejs: failed to prewarm actions: " + String(error));
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
let handlersPromise = Promise.resolve([]);
|
|
504
|
+
if ((resolved?.middleware?.length ?? 0) > 0 || (resolved?.imports?.length ?? 0) > 0) {
|
|
505
|
+
handlersPromise = (async () => {
|
|
506
|
+
try {
|
|
507
|
+
for (const spec of resolved.imports ?? []) await server.ssrLoadModule(spec);
|
|
508
|
+
const handlers = [];
|
|
509
|
+
for (const entry of resolved.middleware ?? []) {
|
|
510
|
+
const spec = typeof entry === "string" ? entry : entry.module;
|
|
511
|
+
const mod = await server.ssrLoadModule(spec);
|
|
512
|
+
if (typeof mod.default !== "function") continue;
|
|
513
|
+
const fn = mod.default;
|
|
514
|
+
handlers.push((request, context) => Promise.resolve(fn(request, context)));
|
|
515
|
+
}
|
|
516
|
+
return handlers;
|
|
517
|
+
} catch (error) {
|
|
518
|
+
server.config.logger.error("oxidejs: failed to wire dev middleware: " + String(error));
|
|
519
|
+
return [];
|
|
520
|
+
}
|
|
521
|
+
})();
|
|
522
|
+
server.middlewares.use((creq, cres, next) => {
|
|
523
|
+
if (matchesActionPath((creq.url ?? "").split("?")[0] ?? "", resolved.actionPath)) {
|
|
524
|
+
next();
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
(async () => {
|
|
528
|
+
try {
|
|
529
|
+
const handlers = await handlersPromise;
|
|
530
|
+
if (handlers.length === 0) {
|
|
531
|
+
next();
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
const { nodeToWebRequest, sendWebResponseFrom } = await import("./rpc-DzUkWWgQ.mjs").then((n) => n._);
|
|
535
|
+
const request = await nodeToWebRequest(creq, resolved.bodyLimit);
|
|
536
|
+
const context = {
|
|
537
|
+
env: resolved.env,
|
|
538
|
+
ctx: void 0
|
|
539
|
+
};
|
|
540
|
+
for (const handler of handlers) {
|
|
541
|
+
const hit = await handler(request, context);
|
|
542
|
+
if (hit) {
|
|
543
|
+
await sendWebResponseFrom(creq, cres, hit);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
next();
|
|
548
|
+
} catch (error) {
|
|
549
|
+
if (cres.headersSent) return;
|
|
550
|
+
cres.statusCode = error instanceof RequestBodyTooLargeError ? 413 : 500;
|
|
551
|
+
cres.end(error instanceof RequestBodyTooLargeError ? void 0 : String(error));
|
|
552
|
+
}
|
|
553
|
+
})();
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
wireActions();
|
|
557
|
+
return async () => {
|
|
558
|
+
try {
|
|
559
|
+
await loadRouter();
|
|
560
|
+
await handlersPromise;
|
|
561
|
+
} catch (error) {
|
|
562
|
+
server.config.logger.error("oxidejs: failed to prewarm dev server: " + String(error));
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
},
|
|
566
|
+
configurePreviewServer(server) {
|
|
567
|
+
if (resolved?.preset !== "fetch") return;
|
|
568
|
+
server.middlewares.use(previewMiddleware(path.join(resolved.outDir, "server.js")));
|
|
569
|
+
}
|
|
570
|
+
},
|
|
571
|
+
rsbuild: { setup(api) {
|
|
572
|
+
api.modifyRsbuildConfig((config) => {
|
|
573
|
+
resolved = resolveOptions(options, typeof config.root === "string" ? config.root : process.cwd(), config);
|
|
574
|
+
applyRsbuildEnvironments(config, resolved);
|
|
575
|
+
});
|
|
576
|
+
api.onBeforeStartDevServer(({ server }) => {
|
|
577
|
+
const loadRouter = async () => {
|
|
578
|
+
return loadActions(resolved?.root ?? process.cwd());
|
|
579
|
+
};
|
|
580
|
+
const loadRpc = async () => ({
|
|
581
|
+
createActionHandler,
|
|
582
|
+
createWsHooks
|
|
583
|
+
});
|
|
584
|
+
if (resolved?.actions === "ws") attachActionUpgrade(server.httpServer, loadRouter, loadRpc, resolved.actionPath, resolved.actionSameOrigin);
|
|
585
|
+
else server.middlewares.use(actionMiddleware(loadRouter, loadRpc, resolved.actionPath, resolved.actionSameOrigin, resolved.bodyLimit));
|
|
586
|
+
});
|
|
587
|
+
api.onBeforeStartPreviewServer?.(({ server }) => {
|
|
588
|
+
if (resolved?.preset !== "fetch") return;
|
|
589
|
+
server.middlewares.use(previewMiddleware(path.join(resolved.outDir, "server.js")));
|
|
590
|
+
});
|
|
591
|
+
} },
|
|
592
|
+
writeBundle() {
|
|
593
|
+
if (!resolved) return;
|
|
594
|
+
copyPublicDir(resolved);
|
|
595
|
+
tryEmitWranglerConfig(resolved, emitState);
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
};
|
|
599
|
+
const oxidejs = /* @__PURE__ */ createUnplugin(unpluginFactory);
|
|
600
|
+
const vite = /* @__PURE__ */ (() => oxidejs.vite)();
|
|
601
|
+
//#endregion
|
|
602
|
+
export { unpluginFactory as n, vite as r, oxidejs as t };
|
package/dist/plugin.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as unpluginFactory, r as vite, t as oxidejs } from "./plugin-
|
|
1
|
+
import { n as unpluginFactory, r as vite, t as oxidejs } from "./plugin-HZKRDuCS.mjs";
|
|
2
2
|
export { oxidejs as default, oxidejs, unpluginFactory, vite };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { t as OxidejsActionHeaders } from "../types-BM4NAnzy.mjs";
|
|
2
|
+
import { Rpc, RpcGroup } from "effect/unstable/rpc";
|
|
3
|
+
//#region src/rpc/client.d.ts
|
|
4
|
+
type RpcClientOptions = {
|
|
5
|
+
url: string;
|
|
6
|
+
transport?: "http" | "ws";
|
|
7
|
+
headers?: OxidejsActionHeaders;
|
|
8
|
+
};
|
|
9
|
+
type NestedClient = Record<string, Record<string, (...args: unknown[]) => Promise<unknown> | AsyncGenerator<unknown>>>;
|
|
10
|
+
type ActionGroup = RpcGroup.RpcGroup<Rpc.Any>;
|
|
11
|
+
declare function createClient(group: ActionGroup, options: RpcClientOptions): NestedClient;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { RpcClientOptions, createClient };
|