oxidejs 0.2.3 → 0.2.4

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
@@ -12,6 +12,8 @@ dist/
12
12
 
13
13
  v1 targets **Vite** and **Rsbuild** via unplugin. Other bundlers are out of scope for now.
14
14
 
15
+ The `oxidejs` entry exports runtime helpers (`action`, `useRequest`, …). The bundler plugin lives at `oxidejs/vite` or `oxidejs/rsbuild` — keep those separate so `*.server.ts` can import `oxidejs` under `preset: "celld"` without pulling Node build tooling into the worker graph.
16
+
15
17
  ## Vite
16
18
 
17
19
  ```ts
package/dist/index.d.mts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { a as OxidejsWranglerOptions, i as OxidejsPreset, n as OxidejsActionTransport, o as ResolvedOptions, r as OxidejsOptions, t as OxidejsActionHeaders } from "./types-BM4NAnzy.mjs";
2
- import { UnpluginFactory } from "unplugin";
3
2
  //#region src/context.d.ts
4
3
  type ExecutionContext = {
5
4
  waitUntil?(promise: Promise<unknown>): void;
@@ -28,9 +27,4 @@ declare function action<Args extends unknown[], Result>(fn: (...args: Args) => R
28
27
  signal?: AbortSignal;
29
28
  }]) => Result);
30
29
  //#endregion
31
- //#region src/index.d.ts
32
- declare const unpluginFactory: UnpluginFactory<OxidejsOptions | undefined>;
33
- declare const oxidejs: import("unplugin").UnpluginInstance<OxidejsOptions | undefined, boolean>;
34
- declare const vite: (options?: OxidejsOptions | undefined) => import("vite").Plugin<any> | import("vite").Plugin<any>[];
35
- //#endregion
36
- export { type ActionContext, type ExecutionContext, type OxidejsActionHeaders, type OxidejsActionTransport, type OxidejsOptions, type OxidejsPreset, type OxidejsWranglerOptions, type ResolvedOptions, action, oxidejs as default, oxidejs, unpluginFactory, useCtx, useEnv, useFetchCtx, useRequest, vite };
30
+ export { type ActionContext, type ExecutionContext, type OxidejsActionHeaders, type OxidejsActionTransport, type OxidejsOptions, type OxidejsPreset, type OxidejsWranglerOptions, type ResolvedOptions, action, useCtx, useEnv, useFetchCtx, useRequest };
package/dist/index.mjs CHANGED
@@ -1,2 +1,49 @@
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
- export { action, oxidejs as default, oxidejs, unpluginFactory, useCtx, useEnv, useFetchCtx, useRequest, vite };
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ //#region src/context.ts
3
+ const ALS_KEY = Symbol.for("oxidejs.requestContext");
4
+ const FETCH_KEY = Symbol.for("oxidejs.fetch");
5
+ function als() {
6
+ const g = globalThis;
7
+ return g[ALS_KEY] ??= new AsyncLocalStorage();
8
+ }
9
+ function store() {
10
+ const current = als().getStore();
11
+ if (!current) throw new Error("oxidejs: request context is unavailable");
12
+ return current;
13
+ }
14
+ /** Current tacho or host request context. Throws outside request handling. */
15
+ function useCtx() {
16
+ return store();
17
+ }
18
+ /** Current server `Request`. Available in actions, SSR, and frame renders. */
19
+ function useRequest() {
20
+ return store().req;
21
+ }
22
+ /** Worker `env` from `fetch(request, env, ctx)`. `undefined` on the Node fetch preset. */
23
+ function useEnv() {
24
+ return store().env;
25
+ }
26
+ /** Worker `ctx` from `fetch(request, env, ctx)` (`waitUntil`). Not tacho `ctx`. `undefined` on Node. */
27
+ function useFetchCtx() {
28
+ return store().fetchCtx;
29
+ }
30
+ function runWithRequest(req, fn, extra) {
31
+ return als().run({
32
+ ...extra,
33
+ req
34
+ }, fn);
35
+ }
36
+ const HOOK_KEY = Symbol.for("oxidejs.runWithRequest");
37
+ globalThis[HOOK_KEY] ??= (req, fn) => {
38
+ const extra = req[FETCH_KEY];
39
+ return runWithRequest(req, fn, extra);
40
+ };
41
+ /**
42
+ * Marks a `*.server.ts` export as a remote RPC action. Runtime identity; the
43
+ * second call signature adds the transport-only `{ signal }` argument.
44
+ */
45
+ function action(fn) {
46
+ return fn;
47
+ }
48
+ //#endregion
49
+ export { action, useCtx, useEnv, useFetchCtx, useRequest };
@@ -2,7 +2,6 @@ import { createUnplugin } from "unplugin";
2
2
  import path from "node:path";
3
3
  import { pathToFileURL } from "node:url";
4
4
  import fs from "node:fs";
5
- import { AsyncLocalStorage } from "node:async_hooks";
6
5
  //#region \0rolldown/runtime.js
7
6
  var __defProp = Object.defineProperty;
8
7
  var __exportAll = (all, no_symbols) => {
@@ -684,55 +683,8 @@ function applyRsbuildEnvironments(config, opts) {
684
683
  return config;
685
684
  }
686
685
  //#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) {
686
+ //#region src/plugin.ts
687
+ function actionMiddleware(loadRouter, path, sameOrigin, bodyLimit, onError) {
736
688
  return (req, res, next) => {
737
689
  if ((req.url ?? "").split("?")[0] !== path) {
738
690
  next();
@@ -748,10 +700,16 @@ function actionMiddleware(loadRouter, path, sameOrigin, bodyLimit) {
748
700
  ...sameOrigin ? { sameOrigin: true } : {}
749
701
  })(await nodeToWebRequest(req, bodyLimit)));
750
702
  })().catch((error) => {
703
+ if (res.headersSent) return;
751
704
  if (error instanceof RequestBodyTooLargeError) {
752
705
  res.statusCode = 413;
753
706
  res.end();
754
- } else next(error);
707
+ return;
708
+ }
709
+ onError?.(error);
710
+ res.statusCode = 503;
711
+ res.setHeader("content-type", "application/json");
712
+ res.end(JSON.stringify({ error: "oxide action handler failed" }));
755
713
  });
756
714
  };
757
715
  }
@@ -864,60 +822,102 @@ const unpluginFactory = (options) => {
864
822
  if (mod) env.moduleGraph.invalidateModule(mod);
865
823
  }
866
824
  };
825
+ const fetchRouter = async () => {
826
+ return (await server.ssrLoadModule(VIRTUAL_ACTIONS_ID)).default;
827
+ };
828
+ let routerReady;
829
+ const loadRouter = () => {
830
+ const current = routerReady ??= fetchRouter();
831
+ return current.catch((error) => {
832
+ if (routerReady === current) routerReady = void 0;
833
+ throw error;
834
+ });
835
+ };
836
+ const refreshRouter = () => {
837
+ routerReady = void 0;
838
+ };
867
839
  server.watcher.on("all", (_event, file) => {
868
- if (isServerFileId(file)) invalidateActions();
840
+ if (isServerFileId(file)) {
841
+ invalidateActions();
842
+ refreshRouter();
843
+ }
869
844
  });
870
- const loadRouter = async () => {
871
- return (await server.ssrLoadModule(VIRTUAL_ACTIONS_ID)).default;
845
+ const logActionError = (error) => {
846
+ server.config.logger.error("oxidejs: action handler failed: " + String(error));
872
847
  };
873
- const wireActions = () => server.middlewares.use(actionMiddleware(loadRouter, resolved.actionPath, resolved.actionSameOrigin, resolved.bodyLimit));
848
+ const wireActions = () => server.middlewares.use(actionMiddleware(loadRouter, resolved.actionPath, resolved.actionSameOrigin, resolved.bodyLimit, logActionError));
874
849
  if (resolved?.actions === "ws") {
875
850
  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;
851
+ return async () => {
852
+ try {
853
+ await loadRouter();
854
+ } catch (error) {
855
+ server.config.logger.error("oxidejs: failed to prewarm actions: " + String(error));
856
+ }
857
+ };
881
858
  }
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)));
859
+ let handlersPromise = Promise.resolve([]);
860
+ if ((resolved?.middleware?.length ?? 0) > 0 || (resolved?.imports?.length ?? 0) > 0) {
861
+ handlersPromise = (async () => {
862
+ try {
863
+ for (const spec of resolved.imports ?? []) await server.ssrLoadModule(spec);
864
+ const handlers = [];
865
+ for (const entry of resolved.middleware ?? []) {
866
+ const spec = typeof entry === "string" ? entry : entry.module;
867
+ const mod = await server.ssrLoadModule(spec);
868
+ if (typeof mod.default !== "function") continue;
869
+ const fn = mod.default;
870
+ handlers.push((request, context) => Promise.resolve(fn(request, context)));
871
+ }
872
+ return handlers;
873
+ } catch (error) {
874
+ server.config.logger.error("oxidejs: failed to wire dev middleware: " + String(error));
875
+ return [];
892
876
  }
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
- });
877
+ })();
878
+ server.middlewares.use((creq, cres, next) => {
879
+ if ((creq.url ?? "").split("?")[0] === resolved.actionPath) {
880
+ next();
881
+ return;
914
882
  }
883
+ (async () => {
884
+ try {
885
+ const handlers = await handlersPromise;
886
+ if (handlers.length === 0) {
887
+ next();
888
+ return;
889
+ }
890
+ const { nodeToWebRequest, sendWebResponseFrom } = await Promise.resolve().then(() => actions_exports);
891
+ const request = await nodeToWebRequest(creq, resolved.bodyLimit);
892
+ const context = {
893
+ env: resolved.env,
894
+ ctx: void 0
895
+ };
896
+ for (const handler of handlers) {
897
+ const hit = await handler(request, context);
898
+ if (hit) {
899
+ await sendWebResponseFrom(creq, cres, hit);
900
+ return;
901
+ }
902
+ }
903
+ next();
904
+ } catch (error) {
905
+ if (cres.headersSent) return;
906
+ cres.statusCode = error instanceof RequestBodyTooLargeError ? 413 : 500;
907
+ cres.end(error instanceof RequestBodyTooLargeError ? void 0 : String(error));
908
+ }
909
+ })();
910
+ });
911
+ }
912
+ wireActions();
913
+ return async () => {
914
+ try {
915
+ await loadRouter();
916
+ await handlersPromise;
915
917
  } catch (error) {
916
- server.config.logger.error("oxidejs: failed to wire dev middleware: " + String(error));
917
- } finally {
918
- wireActions();
918
+ server.config.logger.error("oxidejs: failed to prewarm dev server: " + String(error));
919
919
  }
920
- })();
920
+ };
921
921
  },
922
922
  configurePreviewServer(server) {
923
923
  if (resolved?.preset !== "fetch") return;
@@ -951,4 +951,4 @@ const unpluginFactory = (options) => {
951
951
  const oxidejs = /* @__PURE__ */ createUnplugin(unpluginFactory);
952
952
  const vite = /* @__PURE__ */ (() => oxidejs.vite)();
953
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 };
954
+ export { unpluginFactory as n, vite as r, oxidejs as t };
@@ -0,0 +1,8 @@
1
+ import { r as OxidejsOptions } from "./types-BM4NAnzy.mjs";
2
+ import { UnpluginFactory } from "unplugin";
3
+ //#region src/plugin.d.ts
4
+ declare const unpluginFactory: UnpluginFactory<OxidejsOptions | undefined>;
5
+ declare const oxidejs: import("unplugin").UnpluginInstance<OxidejsOptions | undefined, boolean>;
6
+ declare const vite: (options?: OxidejsOptions | undefined) => import("vite").Plugin<any> | import("vite").Plugin<any>[];
7
+ //#endregion
8
+ export { oxidejs as default, oxidejs, unpluginFactory, vite };
@@ -0,0 +1,2 @@
1
+ import { n as unpluginFactory, r as vite, t as oxidejs } from "./plugin-nO0yjtk0.mjs";
2
+ export { oxidejs as default, oxidejs, unpluginFactory, vite };
package/dist/rsbuild.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as oxidejs } from "./src-D-qdNVqg.mjs";
1
+ import { t as oxidejs } from "./plugin-nO0yjtk0.mjs";
2
2
  //#region src/rsbuild.ts
3
3
  var rsbuild_default = oxidejs.rsbuild;
4
4
  //#endregion
package/dist/vite.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as oxidejs } from "./src-D-qdNVqg.mjs";
1
+ import { t as oxidejs } from "./plugin-nO0yjtk0.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.3",
3
+ "version": "0.2.4",
4
4
  "description": "Vite/Rsbuild plugin. One build → dist/server.js + optional client. Server actions via *.server.ts.",
5
5
  "keywords": [
6
6
  "cloudflare",