oxidejs 0.2.1 → 0.2.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.
package/README.md CHANGED
@@ -113,27 +113,6 @@ Same factory as Vite: client stubs, `/__oxide/action`, and `dist/server.js`.
113
113
 
114
114
  ## Options
115
115
 
116
- ### `middleware` and `imports`
117
-
118
- \`\`\`ts
119
- oxide({
120
- middleware: ["@ilha/router/ssr"], // string or { module, imports }
121
- imports: ["./side-effects"], // side-effect modules loaded at startup
122
- })
123
- \`\`\`
124
-
125
- Middleware handlers run in production before the action gate; the same specifiers are loaded through the SSR graph in dev, so dev and prod behave identically. Middleware entries may carry their own \`imports\`.
126
-
127
- ### Other server options
128
-
129
- | Option | Type | Default | Description |
130
- | ------------- | ------ | ------- | -------------------------------------------------------------------- |
131
- | \`bodyLimit\` | number | 1048576 | Max request body size (Node preset); larger requests get 413 |
132
- | \`notFound\` | string | — | Custom HTML 404 body when no route or asset matches |
133
- | \`env\` | object | — | Passed as \`env\` to \`fetch(request, env, ctx)\` on the Node preset |
134
-
135
- ## Options
136
-
137
116
  | Option | Default | Notes |
138
117
  | ------------------------------ | ------------------------ | ------------------------------------------------------------------------------------------- |
139
118
  | `preset` | `"fetch"` | `"fetch"` or `"celld"` |
@@ -150,23 +129,22 @@ Middleware handlers run in production before the action gate; the same specifier
150
129
  | `emitConfig` | `true` on `celld` | Set `false` to skip `wrangler.jsonc` |
151
130
  | `actions` | `"http"` | `"ws"` needs `crossws`; object form: `{ transport, path, sameOrigin }` (`sameOrigin: true`) |
152
131
  | `actionHeaders` | — | Static headers on the HTTP client |
153
- | `middleware` | `[]` | Default-exported production fetch middleware, run in order before actions and server entry |
132
+ | `middleware` | `[]` | Fetch middleware, run in order before actions and the server entry |
133
+ | `imports` | `[]` | Modules imported for side effects at server startup |
134
+ | `bodyLimit` | `1048576` | Max Node request body size; larger requests get 413 |
135
+ | `notFound` | — | Custom HTML 404 body when no route or asset matches |
136
+ | `env` | — | Node preset value passed to `fetch(request, env, ctx)` |
154
137
 
155
- `middleware` modules receive `(request, { env, ctx })`. They run in array order before actions, the server entry, and assets. Return a `Response` to stop the chain or `undefined` to continue.
138
+ ### `middleware` and `imports`
156
139
 
157
140
  ```ts
158
- // src/auth.ts
159
- export default function auth(request: Request) {
160
- if (!request.headers.has("authorization")) {
161
- return new Response("Unauthorized", { status: 401 });
162
- }
163
- }
164
-
165
- // vite.config.ts
166
- oxide({ middleware: ["./src/auth.ts"] });
141
+ oxide({
142
+ middleware: ["@ilha/router/ssr"], // string or { module, imports }
143
+ imports: ["./side-effects"],
144
+ });
167
145
  ```
168
146
 
169
- This option applies to production builds; use Connect middleware in Vite or Rsbuild during development.
147
+ Middleware modules receive `(request, { env, ctx })`. They run before actions, the server entry, and assets. Return a `Response` to stop the chain or `undefined` to continue. Vite loads the same modules through its SSR graph in development. Middleware entries may carry their own `imports`.
170
148
 
171
149
  `main` is always `./server.js`. `assets` is added only when `index.html` exists. Unknown wrangler keys fail at build time.
172
150
 
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { a as useCtx, c as useRequest, i as action, n as unpluginFactory, o as useEnv, r as vite, s as useFetchCtx, t as oxidejs } from "./src-CsQ-6GnW.mjs";
1
+ import { a as useCtx, c as useRequest, i as action, n as unpluginFactory, o as useEnv, r as vite, s as useFetchCtx, t as oxidejs } from "./src-D-qdNVqg.mjs";
2
2
  export { action, oxidejs as default, oxidejs, unpluginFactory, useCtx, useEnv, useFetchCtx, useRequest, vite };
package/dist/rsbuild.mjs CHANGED
@@ -1,6 +1,5 @@
1
- import { t as oxidejs } from "./src-CsQ-6GnW.mjs";
1
+ import { t as oxidejs } from "./src-D-qdNVqg.mjs";
2
2
  //#region src/rsbuild.ts
3
- /** @experimental Rsbuild integration is not yet implemented. */
4
3
  var rsbuild_default = oxidejs.rsbuild;
5
4
  //#endregion
6
5
  export { rsbuild_default as default };
@@ -21,6 +21,7 @@ var actions_exports = /* @__PURE__ */ __exportAll({
21
21
  RESOLVED_VIRTUAL_ACTIONS_ID: () => RESOLVED_VIRTUAL_ACTIONS_ID,
22
22
  RESOLVED_VIRTUAL_CLIENT_ID: () => RESOLVED_VIRTUAL_CLIENT_ID,
23
23
  RESOLVED_VIRTUAL_WORKER_ID: () => RESOLVED_VIRTUAL_WORKER_ID,
24
+ RequestBodyTooLargeError: () => RequestBodyTooLargeError,
24
25
  VIRTUAL_ACTIONS_ID: () => VIRTUAL_ACTIONS_ID,
25
26
  VIRTUAL_CLIENT_ID: () => VIRTUAL_CLIENT_ID,
26
27
  VIRTUAL_WORKER_ID: () => VIRTUAL_WORKER_ID,
@@ -399,7 +400,8 @@ function loadClientStub(id) {
399
400
  exports: parseExportedNames(fs.readFileSync(file, "utf8"))
400
401
  });
401
402
  }
402
- async function nodeToWebRequest(req) {
403
+ var RequestBodyTooLargeError = class extends Error {};
404
+ async function nodeToWebRequest(req, maxBytes = Number.POSITIVE_INFINITY) {
403
405
  const url = `http://${req.headers.host ?? "localhost"}${req.url ?? "/"}`;
404
406
  const headers = new Headers();
405
407
  for (const [key, value] of Object.entries(req.headers)) {
@@ -417,7 +419,13 @@ async function nodeToWebRequest(req) {
417
419
  };
418
420
  if (method === "GET" || method === "HEAD") return new Request(url, init);
419
421
  const chunks = [];
420
- for await (const chunk of req) chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
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
+ }
421
429
  const body = Buffer.concat(chunks);
422
430
  if (body.length > 0) init.body = body;
423
431
  return new Request(url, init);
@@ -724,7 +732,7 @@ function action(fn) {
724
732
  }
725
733
  //#endregion
726
734
  //#region src/index.ts
727
- function actionMiddleware(loadRouter, path, sameOrigin) {
735
+ function actionMiddleware(loadRouter, path, sameOrigin, bodyLimit) {
728
736
  return (req, res, next) => {
729
737
  if ((req.url ?? "").split("?")[0] !== path) {
730
738
  next();
@@ -738,8 +746,13 @@ function actionMiddleware(loadRouter, path, sameOrigin) {
738
746
  await sendWebResponseFrom(req, res, await handle(await loadRouter(), {
739
747
  path,
740
748
  ...sameOrigin ? { sameOrigin: true } : {}
741
- })(await nodeToWebRequest(req)));
742
- })().catch(next);
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
+ });
743
756
  };
744
757
  }
745
758
  function attachActionUpgrade(httpServer, loadRouter, path, sameOrigin) {
@@ -857,40 +870,54 @@ const unpluginFactory = (options) => {
857
870
  const loadRouter = async () => {
858
871
  return (await server.ssrLoadModule(VIRTUAL_ACTIONS_ID)).default;
859
872
  };
860
- if ((resolved?.middleware?.length ?? 0) > 0 || (resolved?.imports?.length ?? 0) > 0) (async () => {
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 () => {
861
883
  try {
862
884
  for (const spec of resolved.imports ?? []) await server.ssrLoadModule(spec);
863
885
  const handlers = [];
864
886
  for (const entry of resolved.middleware ?? []) {
865
- const spec2 = typeof entry === "string" ? entry : entry.module;
866
- const mod = await server.ssrLoadModule(spec2);
887
+ const spec = typeof entry === "string" ? entry : entry.module;
888
+ const mod = await server.ssrLoadModule(spec);
867
889
  if (typeof mod.default !== "function") continue;
868
890
  const fn = mod.default;
869
- handlers.push((request) => Promise.resolve(fn(request)));
891
+ handlers.push((request, context) => Promise.resolve(fn(request, context)));
870
892
  }
871
- if (handlers.length === 0) return;
872
- const { nodeToWebRequest, sendWebResponseFrom } = await Promise.resolve().then(() => actions_exports);
873
- server.middlewares.use((creq, cres, next) => {
874
- (async () => {
875
- try {
876
- const request = await nodeToWebRequest(creq);
877
- for (const handler of handlers) {
878
- const hit = await handler(request);
879
- if (hit) return sendWebResponseFrom(creq, cres, hit);
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));
880
911
  }
881
- next();
882
- } catch (error) {
883
- cres.statusCode = 500;
884
- cres.end(String(error));
885
- }
886
- })();
887
- });
912
+ })();
913
+ });
914
+ }
888
915
  } catch (error) {
889
916
  server.config.logger.error("oxidejs: failed to wire dev middleware: " + String(error));
917
+ } finally {
918
+ wireActions();
890
919
  }
891
920
  })();
892
- if (resolved?.actions === "ws") attachActionUpgrade(server.httpServer, loadRouter, resolved.actionPath, resolved.actionSameOrigin);
893
- else server.middlewares.use(actionMiddleware(loadRouter, resolved.actionPath, resolved.actionSameOrigin));
894
921
  },
895
922
  configurePreviewServer(server) {
896
923
  if (resolved?.preset !== "fetch") return;
@@ -907,7 +934,7 @@ const unpluginFactory = (options) => {
907
934
  return (await loadActions(resolved?.root ?? process.cwd())).default;
908
935
  };
909
936
  if (resolved?.actions === "ws") attachActionUpgrade(server.httpServer, loadRouter, resolved.actionPath, resolved.actionSameOrigin);
910
- else server.middlewares.use(actionMiddleware(loadRouter, resolved.actionPath, resolved.actionSameOrigin));
937
+ else server.middlewares.use(actionMiddleware(loadRouter, resolved.actionPath, resolved.actionSameOrigin, resolved.bodyLimit));
911
938
  });
912
939
  api.onBeforeStartPreviewServer?.(({ server }) => {
913
940
  if (resolved?.preset !== "fetch") return;
package/dist/vite.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as oxidejs } from "./src-CsQ-6GnW.mjs";
1
+ import { t as oxidejs } from "./src-D-qdNVqg.mjs";
2
2
  //#region src/vite.ts
3
3
  var vite_default = oxidejs.vite;
4
4
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxidejs",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Vite/Rsbuild plugin. One build → dist/server.js + optional client. Server actions via *.server.ts.",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -59,7 +59,7 @@
59
59
  "unplugin": "^3.3.0"
60
60
  },
61
61
  "devDependencies": {
62
- "tacho": "^0.5.0"
62
+ "tacho": "^0.6.1"
63
63
  },
64
64
  "peerDependencies": {
65
65
  "@rsbuild/core": "*",