@alxia/react-router 0.0.0-stage → 0.1.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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +289 -2
  3. package/dist/assets.d.ts +12 -0
  4. package/dist/assets.d.ts.map +1 -0
  5. package/dist/chunks/index-kfekhvgr.js +31 -0
  6. package/dist/chunks/index-kfekhvgr.js.map +10 -0
  7. package/dist/cli/index.d.ts +3 -0
  8. package/dist/cli/index.d.ts.map +1 -0
  9. package/dist/cli/index.js +160 -0
  10. package/dist/cli/index.js.map +13 -0
  11. package/dist/cli/main.d.ts +9 -0
  12. package/dist/cli/main.d.ts.map +1 -0
  13. package/dist/cli/reveal.d.ts +18 -0
  14. package/dist/cli/reveal.d.ts.map +1 -0
  15. package/dist/cli/template.d.ts +8 -0
  16. package/dist/cli/template.d.ts.map +1 -0
  17. package/dist/context.d.ts +70 -0
  18. package/dist/context.d.ts.map +1 -0
  19. package/dist/index.d.ts +4 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +166 -0
  22. package/dist/index.js.map +13 -0
  23. package/dist/react-router.d.ts +61 -0
  24. package/dist/react-router.d.ts.map +1 -0
  25. package/dist/server.d.ts +99 -0
  26. package/dist/server.d.ts.map +1 -0
  27. package/dist/vite/config.d.ts +33 -0
  28. package/dist/vite/config.d.ts.map +1 -0
  29. package/dist/vite/dev.d.ts +23 -0
  30. package/dist/vite/dev.d.ts.map +1 -0
  31. package/dist/vite/entry.d.ts +20 -0
  32. package/dist/vite/entry.d.ts.map +1 -0
  33. package/dist/vite/index.d.ts +33 -0
  34. package/dist/vite/index.d.ts.map +1 -0
  35. package/dist/vite/index.js +391 -0
  36. package/dist/vite/index.js.map +16 -0
  37. package/dist/vite/node.d.ts +16 -0
  38. package/dist/vite/node.d.ts.map +1 -0
  39. package/dist/vite/preview.d.ts +8 -0
  40. package/dist/vite/preview.d.ts.map +1 -0
  41. package/dist/vite/runtime.d.ts +8 -0
  42. package/dist/vite/runtime.d.ts.map +1 -0
  43. package/dist/vite/socket.d.ts +68 -0
  44. package/dist/vite/socket.d.ts.map +1 -0
  45. package/docs/README.md +12 -0
  46. package/docs/guide.md +843 -0
  47. package/docs/roadmap.md +96 -0
  48. package/docs/troubleshooting.md +832 -0
  49. package/package.json +77 -5
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Steve Tsala
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,290 @@
1
- # Temporary Holding Version
1
+ # @alxia/react-router
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [alxia](https://www.npmjs.com/package/@alxia/core) as the HTTP server of a
4
+ [React Router](https://reactrouter.com) framework app, server rendered,
5
+ under Bun. One Vite plugin and no server file: the pages are served by
6
+ alxia in `react-router dev` and from the build. When you want more,
7
+ `app/server.ts` adds the app's hooks — logger, compression, sessions,
8
+ guards — and an `/api` beside the pages, and each loader reads what those
9
+ hooks built, typed.
10
+
11
+ ## Quick start
12
+
13
+ Start from the official template, `bunx create-react-router@latest`, and
14
+ make four changes.
15
+
16
+ **1. Install** alxia and this package:
17
+
18
+ ```sh
19
+ bun add @alxia/core @alxia/react-router
20
+ ```
21
+
22
+ **2. Add the plugin** to `vite.config.ts`. It can go anywhere in `plugins`:
23
+
24
+ ```diff
25
+ +import { alxia } from "@alxia/react-router/vite";
26
+ import { reactRouter } from "@react-router/dev/vite";
27
+ import tailwindcss from "@tailwindcss/vite";
28
+ import { defineConfig } from "vite";
29
+
30
+ export default defineConfig({
31
+ - plugins: [tailwindcss(), reactRouter()],
32
+ + plugins: [tailwindcss(), reactRouter(), alxia()],
33
+ resolve: {
34
+ tsconfigPaths: true,
35
+ },
36
+ });
37
+ ```
38
+
39
+ **3. Run the scripts on Bun**, with a `bunfig.toml`:
40
+
41
+ ```toml
42
+ # bunfig.toml, beside package.json
43
+ [run]
44
+ bun = true
45
+ ```
46
+
47
+ The `react-router` CLI starts with `#!/usr/bin/env node`. Where a node is
48
+ installed, `bun run dev` would run it on Node, and alxia's server needs
49
+ Bun. `bun = true` makes `bun run` start it on Bun. Without it, the dev
50
+ server stops at startup with
51
+ [`alxia-react-router: react-router dev is running on Node, …`](https://github.com/softistx/alxia/blob/develop/packages/react-router/docs/troubleshooting.md#alxia-react-router--is-running-on-node-and-alxias-server-runs-on-bun-).
52
+
53
+ **4. Start with Bun**, in `package.json`:
54
+
55
+ ```diff
56
+ - "start": "react-router-serve ./build/server/index.js",
57
+ + "start": "bun build/server/index.js",
58
+ ```
59
+
60
+ With the `bunfig.toml` in place, the template's `dev`, `build` and
61
+ `typecheck` scripts are unchanged:
62
+
63
+ - **`bun run dev`**: every request Vite does not answer itself (pages,
64
+ data, `/api`, an upgrade to a `ws` route) reaches alxia, with HMR.
65
+ - **`bun run build`** writes `build/server/index.js`, a server you can run.
66
+ - **`bun run start`** serves the pages and the client build. It listens on
67
+ `PORT` (3000) and `HOST` (`0.0.0.0`), and stops on `SIGTERM`.
68
+
69
+ After a build, `bunx --bun vite preview` serves that built server through
70
+ Vite's preview server, as `bun run start` would: the pages, `/api`, the
71
+ client's files and the app's hooks. So does React Router's prerendering.
72
+
73
+ `@alxia/core` and `react-router` 8 are peers, and `vite` 7 or 8 is an
74
+ optional peer, for `/vite`. The package declares no dependency.
75
+
76
+ Its `typescript` peer is 6 or 7. The template's TypeScript 5.9 typechecks,
77
+ but `bun add` warns about it; `bun add -d typescript@^6` silences the
78
+ warning.
79
+
80
+ ## Customising: `app/server.ts`
81
+
82
+ Start from the default server, written out:
83
+
84
+ ```sh
85
+ bunx alxia-react-router reveal
86
+ ```
87
+
88
+ It writes `app/server.ts` (or the file `alxia({ entry })` names):
89
+ `createServer()` with `beforeAll`, `configure` and `getLoadContext`
90
+ commented, and the `Register` declaration. It refuses to overwrite a file
91
+ already there; `--force` overwrites it. Run it from the app's root, once
92
+ `@alxia/react-router` is installed.
93
+
94
+ The examples use `@alxia/logger` (`bun add @alxia/logger`); any plugin
95
+ works the same way. The plugin picks the file up in dev and in the build:
96
+
97
+ ```ts
98
+ // app/server.ts
99
+ import { logger } from '@alxia/logger';
100
+ import { createServer } from '@alxia/react-router';
101
+
102
+ const server = createServer({
103
+ configure: (app) =>
104
+ app
105
+ .use(logger())
106
+ .get('/api/health', ({ reply }) => reply.ok({ ok: true }))
107
+ .derive(({ request }) => {
108
+ const name = request.headers.get('x-user');
109
+ return { user: name === null ? null : { name } };
110
+ }),
111
+ });
112
+
113
+ export default server;
114
+
115
+ // What alxiaOf(context) reads in the loaders.
116
+ declare module '@alxia/react-router' {
117
+ interface Register {
118
+ server: typeof server;
119
+ }
120
+ }
121
+ ```
122
+
123
+ `configure` receives the alxia app and returns it, so what it builds is
124
+ typed. The plugin wires React Router's build, the mode and the client
125
+ folder.
126
+
127
+ A request runs through `beforeAll`, then the client's files, then
128
+ `configure`, then the pages. So a guard in `configure` does not block the
129
+ login page's JavaScript.
130
+
131
+ ## Reading the context in a loader
132
+
133
+ ```ts
134
+ // app/routes/home.tsx
135
+ import { alxiaOf } from '@alxia/react-router';
136
+ import type { Route } from './+types/home';
137
+
138
+ export function loader({ context }: Route.LoaderArgs) {
139
+ const { user, log } = alxiaOf(context); // typed: what configure built
140
+ log.info('home');
141
+ return { name: user?.name ?? 'anonymous' };
142
+ }
143
+ ```
144
+
145
+ How `alxiaOf(context)` is typed:
146
+
147
+ - with the `Register` declaration above, by that server: reading something
148
+ no hook derives is a compile error;
149
+ - without it, `alxiaOf<typeof server>(context)` names the server;
150
+ - with neither, `alxiaOf(context)` is `BaseContext`.
151
+
152
+ [More](https://github.com/softistx/alxia/blob/develop/packages/react-router/docs/guide.md#typing-the-loaders)
153
+
154
+ ## WebSockets
155
+
156
+ A `ws` route in `configure` connects under `react-router dev`, under
157
+ `vite preview` and from the build alike, with nothing else to write:
158
+
159
+ ```ts
160
+ // app/server.ts
161
+ import { createServer } from '@alxia/react-router';
162
+
163
+ export default createServer({
164
+ configure: (app) =>
165
+ app
166
+ .derive(({ request }) => ({ user: request.headers.get('x-user') }))
167
+ .ws('/api/echo', {}, {
168
+ open: (socket) => socket.send({ hello: socket.data.user ?? 'anonymous' }),
169
+ message: (socket, message) => socket.send({ echo: String(message) }),
170
+ }),
171
+ });
172
+ ```
173
+
174
+ In dev, the plugin hands each upgrade Vite does not claim (its HMR, its
175
+ `server.proxy`) to the app, run by `Bun.serve` as `listen` runs it: the
176
+ hooks before the route, a refusal's status, `socket.data`, `publish`. An
177
+ edit to the server is used from the next connection.
178
+ [More](https://github.com/softistx/alxia/blob/develop/packages/react-router/docs/guide.md#websockets)
179
+
180
+ ## Options
181
+
182
+ ```ts
183
+ // app/server.ts
184
+ import { logger } from '@alxia/logger';
185
+ import { createServer } from '@alxia/react-router';
186
+ import { greetingContext } from './context'; // createContext<string>('unset'), in app/context.ts
187
+
188
+ export default createServer({
189
+ beforeAll: (app) => app.use(logger()), // runs before the client's files too
190
+ configure: (app) => app.get('/api/health', ({ reply }) => reply.ok({ ok: true })),
191
+ getLoadContext: (_ctx, context) => context.set(greetingContext, 'hello'),
192
+ listen: { idleTimeout: 30 }, // its port and hostname, if given, win over PORT and HOST
193
+ onListen: (server) => console.log(`up on ${server.url}`),
194
+ });
195
+ ```
196
+
197
+ `build`, `mode` and `client` override what the plugin wires;
198
+ `client: false` serves no client files, so you can serve them yourself.
199
+ A server of your own, with no plugin, calls `reactRouter(app, { build,
200
+ client })` itself. [More](https://github.com/softistx/alxia/blob/develop/packages/react-router/docs/guide.md#escape-hatches)
201
+
202
+ ## The client's files
203
+
204
+ In a build, `build/client` is served before `configure`'s hooks:
205
+
206
+ | path | `Cache-Control` |
207
+ | --- | --- |
208
+ | `/assets/*`, the hashed bundles | `public, max-age=31536000, immutable` |
209
+ | every other top-level file or folder, the copies of `public/` | `public, max-age=3600` |
210
+
211
+ A missing asset gets alxia's JSON 404, not a page. In dev, Vite serves
212
+ these files.
213
+
214
+ ## Leaving the pages out of OpenAPI
215
+
216
+ The examples use `@alxia/openapi` (`bun add @alxia/openapi`):
217
+
218
+ ```ts
219
+ // app/server.ts
220
+ import { docs } from '@alxia/openapi';
221
+ import { createServer, isReactRouterRoute } from '@alxia/react-router';
222
+
223
+ export default createServer({
224
+ configure: (app) =>
225
+ app.use(docs(app, { info: { title: 'Shop', version: '1.0.0' }, exclude: isReactRouterRoute })),
226
+ });
227
+ ```
228
+
229
+ The catch-all adds nothing to the app's route table, so the typed client
230
+ never shows it. `isReactRouterRoute` names the catch-all and the client
231
+ files, so `@alxia/openapi` can leave them out.
232
+
233
+ ## Traps
234
+
235
+ - **A context key made in `app/` reaches the loaders only through the
236
+ plugin**, which builds the server with the routes. A server of your own,
237
+ without the plugin, holds another copy of the key: read alxia's context
238
+ with `alxiaOf`. [More](https://github.com/softistx/alxia/blob/develop/packages/react-router/docs/troubleshooting.md#error-no-value-found-for-context)
239
+ - **Bun's user agent is a bot to `isbot`**, so a test client gets the
240
+ finished page, never a stream. Send a browser's `user-agent`. [More](https://github.com/softistx/alxia/blob/develop/packages/react-router/docs/troubleshooting.md#a-streamed-page-arrives-in-one-piece)
241
+ - **An index route's action is `POST /?index`**; `POST /` gets React
242
+ Router's 405. [More](https://github.com/softistx/alxia/blob/develop/packages/react-router/docs/troubleshooting.md#you-made-a-post-request-to--but-did-not-provide-an-action-for-route-root-so-there-is-no-way-to-handle-the-request)
243
+ - **`@alxia/secure-headers`' default policy blocks the page's scripts**
244
+ and forms: give the pages a policy of their own. [More](https://github.com/softistx/alxia/blob/develop/packages/react-router/docs/troubleshooting.md#refused-to-execute-inline-script-because-it-violates-the-following-content-security-policy-directive-default-src-none)
245
+ - **Under `react-router dev`, `page()` and an HTTP request's
246
+ `ctx.server` are absent**: requests arrive through `app.fetch`. A
247
+ socket's upgrade has its server. [More](https://github.com/softistx/alxia/blob/develop/packages/react-router/docs/troubleshooting.md#ctxserver-is-undefined-under-react-router-dev)
248
+ - **An alxia route whose path covers a page takes it**, wherever it is
249
+ declared: `GET /:slug` answers `/about`. Keep alxia's routes under
250
+ `/api`. [More](https://github.com/softistx/alxia/blob/develop/packages/react-router/docs/troubleshooting.md#a-page-answers-alxias-json-404-or-405-instead-of-rendering)
251
+
252
+ ## API
253
+
254
+ | export | |
255
+ | --- | --- |
256
+ | `createServer(options?)` | the server of `app/server.ts`. `beforeAll`, `configure`, `getLoadContext`, `build`, `mode`, `client`, `listen`, `onListen` |
257
+ | `ServerOptions<Before, App>` | its options |
258
+ | `ReactRouterServer<App>` | what it returns: `create(wiring)` makes the app, `start(app)` listens |
259
+ | `ServerWiring` | what `create` takes: `build`, `mode`, `client` |
260
+ | `FreshApp` | the app `beforeAll`, or `configure` without it, receives |
261
+ | `alxiaOf<App>(context)` | what alxia's hooks built, in a loader, an action or a middleware. Typed by the registered server, by the type argument (a server or an app), or as `BaseContext` |
262
+ | `Register` | the interface to augment with `server: typeof server` |
263
+ | `RegisteredApp` | the app `alxiaOf` reads with no type argument |
264
+ | `RegisteredOf<R>` | the app a `Register`-shaped interface names: its server's, a fresh app, or `InvalidRegister` |
265
+ | `InvalidRegister` | what a `Register` naming neither a server nor an app reads as: every key of the app's own a compile error |
266
+ | `AppOf<Server>` | the app a server makes |
267
+ | `alxiaContext` | the React Router context key `alxiaOf` reads, set on every request |
268
+ | `reactRouter(app, options)` | the catch-all and the client's files, for a server of your own. `build`, `mode`, `getLoadContext`, `client` |
269
+ | `ReactRouterOptions<Ctx>` | its options |
270
+ | `isReactRouterRoute(route)` | whether this package declared a route, for OpenAPI's `exclude` |
271
+
272
+ From `@alxia/react-router/vite`:
273
+
274
+ | export | |
275
+ | --- | --- |
276
+ | `alxia(options?)` | the Vite plugin, for `react-router dev`, `react-router build` and `vite preview`. `entry` is the server file: `app/server.ts` by default, or the default server when there is none |
277
+ | `AlxiaOptions` | its options |
278
+
279
+ The `alxia-react-router` bin, run with `bunx`:
280
+
281
+ | command | |
282
+ | --- | --- |
283
+ | `reveal [--force]` | writes the default server to `app/server.ts`, or to `alxia({ entry })`'s file; refuses an existing file without `--force` |
284
+
285
+ ## Documentation
286
+
287
+ - [Guide](https://github.com/softistx/alxia/blob/develop/packages/react-router/docs/guide.md): the setup, how dev, the build and `vite preview` work, customising the server, typing the loaders, the app's own keys, escape hatches, WebSockets, the client's files, OpenAPI, testing and deploying.
288
+ - [Troubleshooting](https://github.com/softistx/alxia/blob/develop/packages/react-router/docs/troubleshooting.md): each message, and the traps that print none.
289
+ - [Roadmap](https://github.com/softistx/alxia/blob/develop/packages/react-router/docs/roadmap.md): what is coming, and what is not planned.
290
+ - [Example](https://github.com/softistx/alxia/tree/develop/examples/react-router): the official template, these three lines, then an `app/server.ts` with a session, an `/api`, secure headers and a streamed page.
@@ -0,0 +1,12 @@
1
+ /** A hashed asset never changes under its name. */
2
+ export declare const IMMUTABLE = "public, max-age=31536000, immutable";
3
+ /** A public file keeps its name across builds: revalidated after an hour. */
4
+ export declare const AN_HOUR = "public, max-age=3600";
5
+ interface Serving {
6
+ static(path: string, source: string, options: object): unknown;
7
+ file(path: string, file: string, options: object): unknown;
8
+ }
9
+ /** Declares the client build's routes on `app`. */
10
+ export declare function serveClient(app: Serving, client: string): void;
11
+ export {};
12
+ //# sourceMappingURL=assets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../src/assets.ts"],"names":[],"mappings":"AAQA,mDAAmD;AACnD,eAAO,MAAM,SAAS,wCAAwC,CAAC;AAC/D,6EAA6E;AAC7E,eAAO,MAAM,OAAO,yBAAyB,CAAC;AAE9C,UAAU,OAAO;IAChB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/D,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3D;AAED,mDAAmD;AACnD,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAsB9D"}
@@ -0,0 +1,31 @@
1
+ // src/vite/entry.ts
2
+ var NAME = "alxia-react-router";
3
+ var SERVER_BUILD = "virtual:react-router/server-build";
4
+ var PASS_THROUGH = `export * from '${SERVER_BUILD}';
5
+ `;
6
+ function serverEntry({
7
+ file,
8
+ label,
9
+ dev,
10
+ client
11
+ }) {
12
+ const lines = ["// Written by @alxia/react-router/vite."];
13
+ if (file === undefined) {
14
+ lines.push("import { createServer } from '@alxia/react-router';", "const server = createServer();");
15
+ } else {
16
+ lines.push(`import server from ${JSON.stringify(file)};`, "if (typeof server?.create !== 'function' || typeof server.start !== 'function') {", ` throw new TypeError(${JSON.stringify(`${NAME}: ${label} must export createServer() from @alxia/react-router as its default export: export default createServer({ … }).`)});`, "}");
17
+ }
18
+ if (dev) {
19
+ lines.push("export default server.create({", ` build: () => import('${SERVER_BUILD}'),`, "\tmode: 'development',", "});");
20
+ } else {
21
+ lines.push(`import * as build from '${SERVER_BUILD}';`, `export * from '${SERVER_BUILD}';`, "const app = server.create({", "\tbuild,", "\tmode: 'production',", ` client: new URL(${JSON.stringify(client ?? "../client")}, import.meta.url),`, "});", "export default app;", "if (import.meta.main) server.start(app);");
22
+ }
23
+ return `${lines.join(`
24
+ `)}
25
+ `;
26
+ }
27
+
28
+ export { NAME, PASS_THROUGH, serverEntry };
29
+
30
+ //# debugId=F6C7E7A2AF20914E64756E2164756E21
31
+ //# sourceMappingURL=index-kfekhvgr.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/vite/entry.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * The source of the module the plugin serves in dev and builds as\n * `build/server/index.js`: the server, made into the app with React\n * Router's build, and in a build listening when run.\n */\n\nexport const NAME = 'alxia-react-router';\n\n/** React Router's server build, as its Vite plugin serves it. */\nconst SERVER_BUILD = 'virtual:react-router/server-build';\n\n/** The input when the plugin is disabled: React Router's build alone. */\nexport const PASS_THROUGH = `export * from '${SERVER_BUILD}';\\n`;\n\nexport interface EntryOptions {\n\t/** The server file, absolute, or `undefined` for `createServer()` as it is. */\n\treadonly file: string | undefined;\n\t/** How an error names the file: relative to Vite's root. */\n\treadonly label: string;\n\t/** Under `react-router dev`: the build on every request, no client, no listening. */\n\treadonly dev: boolean;\n\t/** The client build, relative to the built file: `../client`. */\n\treadonly client: string | undefined;\n}\n\nexport function serverEntry({\n\tfile,\n\tlabel,\n\tdev,\n\tclient,\n}: EntryOptions): string {\n\tconst lines = ['// Written by @alxia/react-router/vite.'];\n\tif (file === undefined) {\n\t\tlines.push(\n\t\t\t\"import { createServer } from '@alxia/react-router';\",\n\t\t\t'const server = createServer();',\n\t\t);\n\t} else {\n\t\tlines.push(\n\t\t\t`import server from ${JSON.stringify(file)};`,\n\t\t\t\"if (typeof server?.create !== 'function' || typeof server.start !== 'function') {\",\n\t\t\t`\\tthrow new TypeError(${JSON.stringify(\n\t\t\t\t`${NAME}: ${label} must export createServer() from @alxia/react-router as its default export: export default createServer({ … }).`,\n\t\t\t)});`,\n\t\t\t'}',\n\t\t);\n\t}\n\tif (dev) {\n\t\tlines.push(\n\t\t\t'export default server.create({',\n\t\t\t`\\tbuild: () => import('${SERVER_BUILD}'),`,\n\t\t\t\"\\tmode: 'development',\",\n\t\t\t'});',\n\t\t);\n\t} else {\n\t\tlines.push(\n\t\t\t`import * as build from '${SERVER_BUILD}';`,\n\t\t\t// What a server build exports, beside the app: React Router reads it\n\t\t\t// back to prerender. The server file's other exports stay out of it.\n\t\t\t`export * from '${SERVER_BUILD}';`,\n\t\t\t'const app = server.create({',\n\t\t\t'\\tbuild,',\n\t\t\t\"\\tmode: 'production',\",\n\t\t\t`\\tclient: new URL(${JSON.stringify(client ?? '../client')}, import.meta.url),`,\n\t\t\t'});',\n\t\t\t'export default app;',\n\t\t\t// Run, not imported: prerendering, or a test, imports it.\n\t\t\t'if (import.meta.main) server.start(app);',\n\t\t);\n\t}\n\treturn `${lines.join('\\n')}\\n`;\n}\n"
6
+ ],
7
+ "mappings": ";AAMO,IAAM,OAAO;AAGpB,IAAM,eAAe;AAGd,IAAM,eAAe,kBAAkB;AAAA;AAavC,SAAS,WAAW;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,GACwB;AAAA,EACxB,MAAM,QAAQ,CAAC,yCAAyC;AAAA,EACxD,IAAI,SAAS,WAAW;AAAA,IACvB,MAAM,KACL,uDACA,gCACD;AAAA,EACD,EAAO;AAAA,IACN,MAAM,KACL,sBAAsB,KAAK,UAAU,IAAI,MACzC,qFACA,wBAAyB,KAAK,UAC7B,GAAG,SAAS,sHACb,OACA,GACD;AAAA;AAAA,EAED,IAAI,KAAK;AAAA,IACR,MAAM,KACL,kCACA,yBAA0B,mBAC1B,0BACA,KACD;AAAA,EACD,EAAO;AAAA,IACN,MAAM,KACL,2BAA2B,kBAG3B,kBAAkB,kBAClB,+BACA,YACA,yBACA,oBAAqB,KAAK,UAAU,UAAU,WAAW,wBACzD,OACA,uBAEA,0CACD;AAAA;AAAA,EAED,OAAO,GAAG,MAAM,KAAK;AAAA,CAAI;AAAA;AAAA;",
8
+ "debugId": "F6C7E7A2AF20914E64756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bun
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/env bun
2
+ import {
3
+ NAME
4
+ } from "../chunks/index-kfekhvgr.js";
5
+
6
+ // src/cli/reveal.ts
7
+ import { existsSync } from "node:fs";
8
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
9
+ import {
10
+ basename,
11
+ dirname,
12
+ isAbsolute,
13
+ join,
14
+ relative,
15
+ resolve
16
+ } from "node:path";
17
+
18
+ // src/cli/template.ts
19
+ var SERVER_FILE = `/**
20
+ * The alxia server of this React Router app: what alxia() from
21
+ * @alxia/react-router/vite runs when there is no server file, written out by
22
+ * \`alxia-react-router reveal\`. Uncomment an option to customise it.
23
+ */
24
+ import { createServer } from '@alxia/react-router';
25
+ // import { userAgentContext } from './context'; // for getLoadContext, below
26
+
27
+ const server = createServer({
28
+ // Runs first, on a new app, before the client's files too: a guard, a rate
29
+ // limit, a logger that should see every request. Returns the app.
30
+ // beforeAll: (app) => app,
31
+
32
+ // The app the pages run behind: its plugins, its hooks, its /api. What it
33
+ // builds is what alxiaOf(context) reads in the loaders. Returns the app.
34
+ // configure: (app) => app.get('/api/health', ({ reply }) => reply.ok({ ok: true })),
35
+
36
+ // Sets the app's own React Router context keys, ctx typed by configure's
37
+ // app. A key made with createContext() from react-router, in app/context.ts.
38
+ // getLoadContext: (ctx, context) => {
39
+ // context.set(userAgentContext, ctx.request.headers.get('user-agent'));
40
+ // },
41
+ });
42
+
43
+ export default server;
44
+
45
+ // What alxiaOf(context) reads in the loaders, with no type argument.
46
+ declare module '@alxia/react-router' {
47
+ interface Register {
48
+ server: typeof server;
49
+ }
50
+ }
51
+ `;
52
+
53
+ // src/cli/reveal.ts
54
+ var EXTENSIONS = [".ts", ".mts", ".cts", ".js", ".mjs", ".cjs"];
55
+ function configFile(root, base) {
56
+ return EXTENSIONS.map((extension) => join(root, `${base}${extension}`)).find((file) => existsSync(file));
57
+ }
58
+ async function sourceOf(file) {
59
+ const loader = /\.[cm]?ts$/.test(file) ? "ts" : "js";
60
+ return new Bun.Transpiler({ loader }).transformSync(await readFile(file, "utf8"));
61
+ }
62
+ function literal(source, { key, value, computed }) {
63
+ const found = source.match(value)?.[2];
64
+ if (found !== undefined)
65
+ return found;
66
+ if (key.test(source))
67
+ throw new Error(computed);
68
+ return;
69
+ }
70
+ async function revealTarget(root) {
71
+ const vite = configFile(root, "vite.config");
72
+ if (vite === undefined) {
73
+ throw new Error(`${NAME}: no vite.config.ts in ${root}. Run reveal from the app's root, beside vite.config.ts.`);
74
+ }
75
+ const entry = literal(await sourceOf(vite), {
76
+ key: /\balxia\(\s*\{[^}]*?\bentry\s*:/s,
77
+ value: /\balxia\(\s*\{[^}]*?\bentry\s*:\s*(['"`])([^'"`$]+)\1/s,
78
+ computed: `${NAME}: ${basename(vite)} gives alxia() an entry reveal cannot read. Write it as a string literal, alxia({ entry: 'app/server.ts' }), and run reveal again.`
79
+ });
80
+ if (entry !== undefined) {
81
+ return isAbsolute(entry) ? entry : resolve(root, entry);
82
+ }
83
+ const rr = configFile(root, "react-router.config");
84
+ const app = (rr === undefined ? undefined : literal(await sourceOf(rr), {
85
+ key: /\bappDirectory\s*:/,
86
+ value: /\bappDirectory\s*:\s*(['"`])([^'"`$]+)\1/,
87
+ computed: `${NAME}: ${basename(rr)} computes appDirectory, which reveal cannot read. Write it as a string literal, appDirectory: 'app', and run reveal again.`
88
+ })) ?? "app";
89
+ return resolve(root, app, "server.ts");
90
+ }
91
+ async function reveal(root, force) {
92
+ const file = await revealTarget(root);
93
+ const label = relative(root, file);
94
+ if (existsSync(file) && !force)
95
+ return { exists: label };
96
+ await mkdir(dirname(file), { recursive: true });
97
+ await writeFile(file, SERVER_FILE);
98
+ return { written: label };
99
+ }
100
+
101
+ // src/cli/main.ts
102
+ var USAGE = `Usage: ${NAME} <command> [options]
103
+
104
+ Commands:
105
+ reveal [--force] Write the server alxia() runs by default to app/server.ts,
106
+ or to alxia({ entry })'s file, to customise it. --force
107
+ overwrites a file already there.
108
+
109
+ Options:
110
+ --help Show this help.`;
111
+ async function main(args, root, output) {
112
+ const [command, ...options] = args;
113
+ if (command === undefined) {
114
+ output.err(USAGE);
115
+ return 1;
116
+ }
117
+ if (command === "--help" || command === "-h") {
118
+ output.out(USAGE);
119
+ return 0;
120
+ }
121
+ if (command !== "reveal") {
122
+ output.err(`${NAME}: unknown command ${command}.
123
+
124
+ ${USAGE}`);
125
+ return 1;
126
+ }
127
+ if (options.includes("--help") || options.includes("-h")) {
128
+ output.out(USAGE);
129
+ return 0;
130
+ }
131
+ const unknown = options.find((option) => option !== "--force");
132
+ if (unknown !== undefined) {
133
+ output.err(`${NAME}: unknown option ${unknown} for reveal.
134
+
135
+ ${USAGE}`);
136
+ return 1;
137
+ }
138
+ try {
139
+ const result = await reveal(root, options.includes("--force"));
140
+ if ("exists" in result) {
141
+ output.err(`${NAME}: ${result.exists} already exists, and reveal leaves it as it is. Run ${NAME} reveal --force to overwrite it.`);
142
+ return 1;
143
+ }
144
+ output.out(`${NAME}: wrote ${result.written}, the server alxia() runs by default.
145
+ Next: uncomment configure in ${result.written} to add the app's hooks and /api; bun run dev picks it up.`);
146
+ return 0;
147
+ } catch (error) {
148
+ output.err(error instanceof Error ? error.message : String(error));
149
+ return 1;
150
+ }
151
+ }
152
+
153
+ // src/cli/index.ts
154
+ process.exitCode = await main(process.argv.slice(2), process.cwd(), {
155
+ out: (line) => console.log(line),
156
+ err: (line) => console.error(line)
157
+ });
158
+
159
+ //# debugId=7E4161A01E7D668A64756E2164756E21
160
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,13 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/cli/reveal.ts", "../src/cli/template.ts", "../src/cli/main.ts", "../src/cli/index.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * `alxia-react-router reveal`: where the server file goes, read cheaply from\n * the app's own config files, and writing it there.\n */\nimport { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport {\n\tbasename,\n\tdirname,\n\tisAbsolute,\n\tjoin,\n\trelative,\n\tresolve,\n} from 'node:path';\nimport { NAME } from '../vite/entry';\nimport { SERVER_FILE } from './template';\n\nconst EXTENSIONS = ['.ts', '.mts', '.cts', '.js', '.mjs', '.cjs'];\n\n/** `vite.config.ts`, or the first of its other extensions found in `root`. */\nfunction configFile(root: string, base: string): string | undefined {\n\treturn EXTENSIONS.map((extension) => join(root, `${base}${extension}`)).find(\n\t\t(file) => existsSync(file),\n\t);\n}\n\n/** A config file's source, without its comments: Bun's transpiler drops them. */\nasync function sourceOf(file: string): Promise<string> {\n\tconst loader = /\\.[cm]?ts$/.test(file) ? 'ts' : 'js';\n\treturn new Bun.Transpiler({ loader }).transformSync(\n\t\tawait readFile(file, 'utf8'),\n\t);\n}\n\n/**\n * The string literal given to `key` in `source`; `undefined` when `key` is\n * not there. A `key` given anything else is refused with `computed`.\n */\nfunction literal(\n\tsource: string,\n\t{ key, value, computed }: { key: RegExp; value: RegExp; computed: string },\n): string | undefined {\n\tconst found = source.match(value)?.[2];\n\tif (found !== undefined) return found;\n\tif (key.test(source)) throw new Error(computed);\n\treturn undefined;\n}\n\n/**\n * The file the plugin would load as the server: `alxia({ entry })`'s, or\n * `server.ts` in React Router's app directory, `app/` unless its config\n * names another. Each is read as a string literal; one computed is refused,\n * since reveal would write a file the plugin does not load.\n */\nexport async function revealTarget(root: string): Promise<string> {\n\tconst vite = configFile(root, 'vite.config');\n\tif (vite === undefined) {\n\t\tthrow new Error(\n\t\t\t`${NAME}: no vite.config.ts in ${root}. Run reveal from the app's root, beside vite.config.ts.`,\n\t\t);\n\t}\n\tconst entry = literal(await sourceOf(vite), {\n\t\tkey: /\\balxia\\(\\s*\\{[^}]*?\\bentry\\s*:/s,\n\t\tvalue: /\\balxia\\(\\s*\\{[^}]*?\\bentry\\s*:\\s*(['\"`])([^'\"`$]+)\\1/s,\n\t\tcomputed: `${NAME}: ${basename(vite)} gives alxia() an entry reveal cannot read. Write it as a string literal, alxia({ entry: 'app/server.ts' }), and run reveal again.`,\n\t});\n\tif (entry !== undefined) {\n\t\treturn isAbsolute(entry) ? entry : resolve(root, entry);\n\t}\n\tconst rr = configFile(root, 'react-router.config');\n\tconst app =\n\t\t(rr === undefined\n\t\t\t? undefined\n\t\t\t: literal(await sourceOf(rr), {\n\t\t\t\t\tkey: /\\bappDirectory\\s*:/,\n\t\t\t\t\tvalue: /\\bappDirectory\\s*:\\s*(['\"`])([^'\"`$]+)\\1/,\n\t\t\t\t\tcomputed: `${NAME}: ${basename(rr)} computes appDirectory, which reveal cannot read. Write it as a string literal, appDirectory: 'app', and run reveal again.`,\n\t\t\t\t})) ?? 'app';\n\treturn resolve(root, app, 'server.ts');\n}\n\nexport type Revealed =\n\t| { readonly written: string }\n\t| { readonly exists: string };\n\n/**\n * Writes the server file into the app at `root`, unless one is already\n * there and `force` is not set. The path comes back relative to `root`.\n */\nexport async function reveal(root: string, force: boolean): Promise<Revealed> {\n\tconst file = await revealTarget(root);\n\tconst label = relative(root, file);\n\tif (existsSync(file) && !force) return { exists: label };\n\tawait mkdir(dirname(file), { recursive: true });\n\tawait writeFile(file, SERVER_FILE);\n\treturn { written: label };\n}\n",
6
+ "/**\n * The server file `alxia-react-router reveal` writes: `createServer()` as\n * the default server runs it, its options commented, and the `Register`\n * declaration that types the loaders.\n */\n\n/** The file, as written. Every commented option compiles once uncommented. */\nexport const SERVER_FILE = `/**\n * The alxia server of this React Router app: what alxia() from\n * @alxia/react-router/vite runs when there is no server file, written out by\n * \\`alxia-react-router reveal\\`. Uncomment an option to customise it.\n */\nimport { createServer } from '@alxia/react-router';\n// import { userAgentContext } from './context'; // for getLoadContext, below\n\nconst server = createServer({\n\t// Runs first, on a new app, before the client's files too: a guard, a rate\n\t// limit, a logger that should see every request. Returns the app.\n\t// beforeAll: (app) => app,\n\n\t// The app the pages run behind: its plugins, its hooks, its /api. What it\n\t// builds is what alxiaOf(context) reads in the loaders. Returns the app.\n\t// configure: (app) => app.get('/api/health', ({ reply }) => reply.ok({ ok: true })),\n\n\t// Sets the app's own React Router context keys, ctx typed by configure's\n\t// app. A key made with createContext() from react-router, in app/context.ts.\n\t// getLoadContext: (ctx, context) => {\n\t// \tcontext.set(userAgentContext, ctx.request.headers.get('user-agent'));\n\t// },\n});\n\nexport default server;\n\n// What alxiaOf(context) reads in the loaders, with no type argument.\ndeclare module '@alxia/react-router' {\n\tinterface Register {\n\t\tserver: typeof server;\n\t}\n}\n`;\n",
7
+ "/**\n * The `alxia-react-router` command: its arguments, what it prints, and its\n * exit code. `index.ts` runs it on the process's own.\n */\nimport { NAME } from '../vite/entry';\nimport { reveal } from './reveal';\n\nexport const USAGE = `Usage: ${NAME} <command> [options]\n\nCommands:\n reveal [--force] Write the server alxia() runs by default to app/server.ts,\n or to alxia({ entry })'s file, to customise it. --force\n overwrites a file already there.\n\nOptions:\n --help Show this help.`;\n\n/** Where the command prints: the process's streams, or a spec's. */\nexport interface Output {\n\treadonly out: (line: string) => void;\n\treadonly err: (line: string) => void;\n}\n\n/** Runs the command in `root`; resolves to the process's exit code. */\nexport async function main(\n\targs: readonly string[],\n\troot: string,\n\toutput: Output,\n): Promise<number> {\n\tconst [command, ...options] = args;\n\tif (command === undefined) {\n\t\toutput.err(USAGE);\n\t\treturn 1;\n\t}\n\tif (command === '--help' || command === '-h') {\n\t\toutput.out(USAGE);\n\t\treturn 0;\n\t}\n\tif (command !== 'reveal') {\n\t\toutput.err(`${NAME}: unknown command ${command}.\\n\\n${USAGE}`);\n\t\treturn 1;\n\t}\n\tif (options.includes('--help') || options.includes('-h')) {\n\t\toutput.out(USAGE);\n\t\treturn 0;\n\t}\n\tconst unknown = options.find((option) => option !== '--force');\n\tif (unknown !== undefined) {\n\t\toutput.err(`${NAME}: unknown option ${unknown} for reveal.\\n\\n${USAGE}`);\n\t\treturn 1;\n\t}\n\ttry {\n\t\tconst result = await reveal(root, options.includes('--force'));\n\t\tif ('exists' in result) {\n\t\t\toutput.err(\n\t\t\t\t`${NAME}: ${result.exists} already exists, and reveal leaves it as it is. Run ${NAME} reveal --force to overwrite it.`,\n\t\t\t);\n\t\t\treturn 1;\n\t\t}\n\t\toutput.out(\n\t\t\t`${NAME}: wrote ${result.written}, the server alxia() runs by default.\\nNext: uncomment configure in ${result.written} to add the app's hooks and /api; bun run dev picks it up.`,\n\t\t);\n\t\treturn 0;\n\t} catch (error) {\n\t\toutput.err(error instanceof Error ? error.message : String(error));\n\t\treturn 1;\n\t}\n}\n",
8
+ "#!/usr/bin/env bun\n/**\n * `alxia-react-router`, the package's bin: `bunx alxia-react-router reveal`\n * writes the default server into the app, to customise it.\n */\nimport { main } from './main';\n\nprocess.exitCode = await main(process.argv.slice(2), process.cwd(), {\n\tout: (line) => console.log(line),\n\terr: (line) => console.error(line),\n});\n"
9
+ ],
10
+ "mappings": "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",
11
+ "debugId": "7E4161A01E7D668A64756E2164756E21",
12
+ "names": []
13
+ }
@@ -0,0 +1,9 @@
1
+ export declare const USAGE = "Usage: alxia-react-router <command> [options]\n\nCommands:\n reveal [--force] Write the server alxia() runs by default to app/server.ts,\n or to alxia({ entry })'s file, to customise it. --force\n overwrites a file already there.\n\nOptions:\n --help Show this help.";
2
+ /** Where the command prints: the process's streams, or a spec's. */
3
+ export interface Output {
4
+ readonly out: (line: string) => void;
5
+ readonly err: (line: string) => void;
6
+ }
7
+ /** Runs the command in `root`; resolves to the process's exit code. */
8
+ export declare function main(args: readonly string[], root: string, output: Output): Promise<number>;
9
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,KAAK,mUAQkB,CAAC;AAErC,oEAAoE;AACpE,MAAM,WAAW,MAAM;IACtB,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC;AAED,uEAAuE;AACvE,wBAAsB,IAAI,CACzB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CAuCjB"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The file the plugin would load as the server: `alxia({ entry })`'s, or
3
+ * `server.ts` in React Router's app directory, `app/` unless its config
4
+ * names another. Each is read as a string literal; one computed is refused,
5
+ * since reveal would write a file the plugin does not load.
6
+ */
7
+ export declare function revealTarget(root: string): Promise<string>;
8
+ export type Revealed = {
9
+ readonly written: string;
10
+ } | {
11
+ readonly exists: string;
12
+ };
13
+ /**
14
+ * Writes the server file into the app at `root`, unless one is already
15
+ * there and `force` is not set. The path comes back relative to `root`.
16
+ */
17
+ export declare function reveal(root: string, force: boolean): Promise<Revealed>;
18
+ //# sourceMappingURL=reveal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reveal.d.ts","sourceRoot":"","sources":["../../src/cli/reveal.ts"],"names":[],"mappings":"AAgDA;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBhE;AAED,MAAM,MAAM,QAAQ,GACjB;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC5B;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/B;;;GAGG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAO5E"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The server file `alxia-react-router reveal` writes: `createServer()` as
3
+ * the default server runs it, its options commented, and the `Register`
4
+ * declaration that types the loaders.
5
+ */
6
+ /** The file, as written. Every commented option compiles once uncommented. */
7
+ export declare const SERVER_FILE = "/**\n * The alxia server of this React Router app: what alxia() from\n * @alxia/react-router/vite runs when there is no server file, written out by\n * `alxia-react-router reveal`. Uncomment an option to customise it.\n */\nimport { createServer } from '@alxia/react-router';\n// import { userAgentContext } from './context'; // for getLoadContext, below\n\nconst server = createServer({\n\t// Runs first, on a new app, before the client's files too: a guard, a rate\n\t// limit, a logger that should see every request. Returns the app.\n\t// beforeAll: (app) => app,\n\n\t// The app the pages run behind: its plugins, its hooks, its /api. What it\n\t// builds is what alxiaOf(context) reads in the loaders. Returns the app.\n\t// configure: (app) => app.get('/api/health', ({ reply }) => reply.ok({ ok: true })),\n\n\t// Sets the app's own React Router context keys, ctx typed by configure's\n\t// app. A key made with createContext() from react-router, in app/context.ts.\n\t// getLoadContext: (ctx, context) => {\n\t// \tcontext.set(userAgentContext, ctx.request.headers.get('user-agent'));\n\t// },\n});\n\nexport default server;\n\n// What alxiaOf(context) reads in the loaders, with no type argument.\ndeclare module '@alxia/react-router' {\n\tinterface Register {\n\t\tserver: typeof server;\n\t}\n}\n";
8
+ //# sourceMappingURL=template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/cli/template.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,8EAA8E;AAC9E,eAAO,MAAM,WAAW,iyCAgCvB,CAAC"}