@alxia/react-router 0.7.4 → 0.8.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.
@@ -10,6 +10,7 @@ a loader, a message React Router or the browser prints, or an error from
10
10
  - [`alxia-react-router: … is running on Node, and alxia's server runs on Bun. …`](#alxia-react-router--is-running-on-node-and-alxias-server-runs-on-bun-)
11
11
  - [`ReferenceError: Bun is not defined`](#referenceerror-bun-is-not-defined)
12
12
  - [`alxiaOf(): this request has no alxia context. …`](#alxiaof-this-request-has-no-alxia-context-)
13
+ - [`TypeError: undefined is not an object (evaluating 'context.alxia.user')`](#typeerror-undefined-is-not-an-object-evaluating-contextalxiauser)
13
14
  - [`Error: No value found for context`](#error-no-value-found-for-context)
14
15
  - [`alxia-react-router: … must export createServer() from @alxia/react-router as its default export: …`](#alxia-react-router--must-export-createserver-from-alxiareact-router-as-its-default-export-)
15
16
  - [`alxia-react-router: the entry … does not exist. …`](#alxia-react-router-the-entry--does-not-exist-)
@@ -40,6 +41,7 @@ a loader, a message React Router or the browser prints, or an error from
40
41
 
41
42
  **Types**
42
43
 
44
+ - [`Property 'alxia' does not exist on type 'Readonly<RouterContextProvider>'`](#property-alxia-does-not-exist-on-type-readonlyroutercontextprovider)
43
45
  - [`Property '…' does not exist on type 'BaseContext & …'`](#property--does-not-exist-on-type-basecontext--)
44
46
  - [`Type '…' does not satisfy the constraint 'AnyAlxia | ReactRouterServer<AnyAlxia>'`](#type--does-not-satisfy-the-constraint-anyalxia--reactrouterserveranyalxia)
45
47
  - [`Type '(app: …) => void' is not assignable to type '(app: …) => AnyAlxia'`](#type-app---void-is-not-assignable-to-type-app---anyalxia)
@@ -57,7 +59,7 @@ a loader, a message React Router or the browser prints, or an error from
57
59
  - [The logger times a streamed page at a few milliseconds](#the-logger-times-a-streamed-page-at-a-few-milliseconds)
58
60
  - [A page, or a missing path, answers 401 behind a guard](#a-page-or-a-missing-path-answers-401-behind-a-guard)
59
61
  - [The logger, CORS or secure headers miss the client's files](#the-logger-cors-or-secure-headers-miss-the-clients-files)
60
- - [`ctx.server` is `undefined` under `react-router dev`](#ctxserver-is-undefined-under-react-router-dev)
62
+ - [A `page()` is not served under `react-router dev`](#a-page-is-not-served-under-react-router-dev)
61
63
  - [A `publish` under `react-router dev` misses the sockets opened before an edit](#a-publish-under-react-router-dev-misses-the-sockets-opened-before-an-edit)
62
64
  - [The build has a package's Node variant, not its `bun` one](#the-build-has-a-packages-node-variant-not-its-bun-one)
63
65
  - [`request.url` in a loader is `http://` and the internal host behind a proxy](#requesturl-in-a-loader-is-http-and-the-internal-host-behind-a-proxy)
@@ -109,7 +111,11 @@ with [the entry above](#alxia-react-router--is-running-on-node-and-alxias-server
109
111
  Error: alxiaOf(): this request has no alxia context. Serve the React Router app through alxia: add alxia() from @alxia/react-router/vite to vite.config.ts's plugins, or, with a server of your own, serve the build through reactRouter() from @alxia/react-router.
110
112
  ```
111
113
 
112
- **When:** a loader calls `alxiaOf` and `alxiaContext` was not set.
114
+ **When:** a loader calls `alxiaOf`, or reads `context.alxia`, and
115
+ `alxiaContext` was not set. `context.alxia` throws the same error: it reads
116
+ that key. With two copies of `@alxia/react-router` in one process (the
117
+ third case below), the getter is the copy loaded last, which reads its own
118
+ key: one copy is the fix for both.
113
119
 
114
120
  **Why:** one of three:
115
121
 
@@ -154,6 +160,34 @@ context.set(alxiaContext, { user: { name: 'Ada' } });
154
160
  await loader({ context, request: new Request('http://localhost/'), params: {} } as never);
155
161
  ```
156
162
 
163
+ ### `TypeError: undefined is not an object (evaluating 'context.alxia.user')`
164
+
165
+ **When:** a unit test calls a loader that reads `context.alxia` with a
166
+ `new RouterContextProvider()`, and nothing in the test imports
167
+ `@alxia/react-router`; destructured, `const { user } = context.alxia` fails
168
+ with `TypeError: Cannot destructure property 'user' from null or undefined
169
+ value`. A `clientLoader` or a `clientAction` reading it fails the same way
170
+ in the browser.
171
+
172
+ **Why:** `context.alxia` is a getter `@alxia/react-router` defines on React
173
+ Router's provider when it is first imported. The loader's module need not
174
+ import it, and the browser never loads it: the alxia context is the
175
+ server's.
176
+
177
+ **Fix:** in the test, set the key the getter reads, imported from
178
+ `@alxia/react-router`, which installs the getter:
179
+
180
+ ```ts
181
+ import { alxiaContext } from '@alxia/react-router';
182
+ import { RouterContextProvider } from 'react-router';
183
+
184
+ const context = new RouterContextProvider();
185
+ context.set(alxiaContext, { user: { name: 'Ada' }, server: undefined });
186
+ await loader({ context, request: new Request('http://localhost/'), params: {} } as never);
187
+ ```
188
+
189
+ On the client, read the data the server's loader returned instead.
190
+
157
191
  ### `Error: No value found for context`
158
192
 
159
193
  **When:** a loader, action or middleware calls `context.get(key)` with a
@@ -738,6 +772,42 @@ put the final stage back on `oven/bun:1`
738
772
 
739
773
  ## Types
740
774
 
775
+ ### `Property 'alxia' does not exist on type 'Readonly<RouterContextProvider>'`
776
+
777
+ ```text
778
+ app/routes/home.tsx: error TS2339: Property 'alxia' does not exist on type 'Readonly<RouterContextProvider>'.
779
+ ```
780
+
781
+ **When:** a loader or an action typed with the generated `Route.LoaderArgs`
782
+ or `Route.ActionArgs` reads `context.alxia`. At runtime it is there; only
783
+ `tsc` refuses it.
784
+
785
+ **Why:** this package augments `RouterContextProvider` as `react-router`
786
+ exports it, whose types are `dist/production`'s. The generated `+types`
787
+ files read their argument types from `react-router/internal`, whose types,
788
+ in react-router 8.4, point at `dist/development`: a second declaration of
789
+ the class, which no exported path reaches, so no augmentation can add
790
+ `alxia` to it.
791
+
792
+ **Fix:** wrap the loader in `withAlxia`, which hands it `alxia` typed the
793
+ same way, and keeps the generated `loaderData`:
794
+
795
+ ```ts
796
+ import { type AlxiaArgs, withAlxia } from '@alxia/react-router';
797
+ import type { Route } from './+types/home';
798
+
799
+ export const loader = withAlxia(({ alxia }: Route.LoaderArgs & AlxiaArgs) => {
800
+ return { name: alxia.user?.name ?? null };
801
+ });
802
+ ```
803
+
804
+ Or read `alxiaOf(context)` inside the plain function.
805
+
806
+ Or type the arguments with `LoaderFunctionArgs` from `react-router`, which
807
+ sees `context.alxia`, at the cost of the generated `params`. Typing it under
808
+ `Route.LoaderArgs` waits on react-router pointing both entries at the same
809
+ declarations.
810
+
741
811
  ### `Property '…' does not exist on type 'BaseContext & …'`
742
812
 
743
813
  ```text
@@ -1034,22 +1104,25 @@ createServer({
1034
1104
  });
1035
1105
  ```
1036
1106
 
1037
- ### `ctx.server` is `undefined` under `react-router dev`
1107
+ ### A `page()` is not served under `react-router dev`
1038
1108
 
1039
- A middleware or a route reads `ctx.server`, or the app declares a `page()`,
1040
- under `react-router dev` or `vite preview`; the same code works from
1041
- `bun build/server/index.js`.
1109
+ The app declares a `page()`, or reads `ctx.server.url`, `requestIP` or
1110
+ `timeout`, or `ctx.ip`, under `react-router dev` or `vite preview`; the same
1111
+ code works from `bun build/server/index.js`.
1042
1112
 
1043
1113
  **Why:** under `@alxia/react-router/vite`, Vite owns the dev server, and
1044
- the preview server under `vite preview`. An HTTP request reaches the app
1045
- through `app.fetch`, as in a test, not through `listen`: there is no
1046
- `Bun.serve` behind it, so `ctx.server` is `undefined`, the default
1047
- `ctx.ip` too, and a `page()` (Bun's HTML bundle) is not served. A socket's upgrade is the exception: the plugin relays it
1048
- to a `Bun.serve` of the app, so its middlewares read a server
1049
- ([WebSockets](guide.md#under-react-router-dev)).
1050
-
1051
- **Fix:** read `ctx.server` as optional, and publish to sockets from the
1052
- sockets themselves (`socket.publish`), which works in dev too:
1114
+ the preview server under `vite preview`: a `node:http` server, not a
1115
+ `Bun.Server`. An HTTP request reaches the app through
1116
+ `app.fetch(request, side)`, where `side` is the loopback `Bun.serve` the
1117
+ plugin relays the app's sockets to. So `ctx.server` is that side server: its
1118
+ `publish` reaches the app's sockets, but its `url` is the loopback port's,
1119
+ and its `requestIP` and `timeout` know nothing of a request Vite took, so
1120
+ the default `ctx.ip` is `undefined`. A `page()` (Bun's HTML bundle) is not
1121
+ served: Vite answers the HTML
1122
+ ([The server](guide.md#the-server-server)).
1123
+
1124
+ **Fix:** read `ctx.server` as optional, which also covers `app.request` in
1125
+ a test, and take nothing from its `url` or `requestIP` in dev:
1053
1126
 
1054
1127
  ```ts
1055
1128
  // app/server.ts
@@ -1072,7 +1145,7 @@ Two clients in one room; after an edit to `app/server.ts`, a message one
1072
1145
  sends no longer reaches the other.
1073
1146
 
1074
1147
  **Why:** an edit to the server makes a new app, and the plugin starts a
1075
- new `Bun.serve` for it on the next upgrade, so that new connections use
1148
+ new `Bun.serve` for it on the next upgrade or request, so that new connections use
1076
1149
  the new handlers. A socket opened before the edit stays on the previous
1077
1150
  server, with the handlers it opened with, and a topic is one server's.
1078
1151
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alxia/react-router",
3
- "version": "0.7.4",
3
+ "version": "0.8.0",
4
4
  "description": "alxia as the server of a React Router framework app: server rendering behind the app's hooks, loaders reading its typed context, the client's assets served",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -51,9 +51,9 @@
51
51
  ]
52
52
  },
53
53
  "devDependencies": {
54
- "@alxia/compress": "^0.3.8",
55
- "@alxia/core": "^0.13.0",
56
- "@alxia/openapi": "^0.6.7",
54
+ "@alxia/compress": "^0.3.9",
55
+ "@alxia/core": "^0.14.2",
56
+ "@alxia/openapi": "^0.6.8",
57
57
  "@react-router/dev": "^8.4.0",
58
58
  "@types/bun": "^1.4.2",
59
59
  "@types/react": "^19.3.0",
@@ -65,7 +65,7 @@
65
65
  "vite": "^7.0.0"
66
66
  },
67
67
  "peerDependencies": {
68
- "@alxia/core": "^0.13.0",
68
+ "@alxia/core": "^0.14.2",
69
69
  "react-router": "^8.0.0",
70
70
  "typescript": "^6.0.3 || ^7.0.0",
71
71
  "vite": "^7.0.0 || ^8.0.0"