oxidejs 0.3.1 → 0.3.3

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