@alxia/create 0.1.0 → 0.1.2
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 +37 -6
- package/dist/copy.d.ts +24 -0
- package/dist/copy.d.ts.map +1 -0
- package/dist/index.js +61 -281
- package/dist/index.js.map +8 -9
- package/dist/main.d.ts.map +1 -1
- package/dist/registry.d.ts +28 -7
- package/dist/registry.d.ts.map +1 -1
- package/dist/versions.d.ts +0 -2
- package/dist/versions.d.ts.map +1 -1
- package/dist/write.d.ts +2 -1
- package/dist/write.d.ts.map +1 -1
- package/docs/README.md +3 -3
- package/docs/guide.md +104 -27
- package/docs/roadmap.md +35 -9
- package/docs/troubleshooting.md +58 -63
- package/package.json +5 -3
- package/templates/api/.dockerignore +6 -0
- package/templates/api/.env.example +3 -0
- package/templates/api/Dockerfile +22 -0
- package/templates/api/README.md +64 -0
- package/templates/api/gitignore +6 -0
- package/templates/api/package.json +21 -0
- package/templates/api/src/app.spec.ts +46 -0
- package/templates/api/src/app.ts +33 -0
- package/templates/api/src/server.ts +4 -0
- package/templates/api/tsconfig.json +25 -0
- package/templates/react-router/.dockerignore +4 -0
- package/templates/react-router/Dockerfile +29 -0
- package/templates/react-router/README.md +87 -0
- package/templates/react-router/_bunfig.toml +4 -0
- package/templates/react-router/app/app.css +15 -0
- package/templates/react-router/app/root.tsx +75 -0
- package/templates/react-router/app/routes/home.tsx +13 -0
- package/templates/react-router/app/routes.ts +3 -0
- package/templates/react-router/app/welcome/logo-dark.svg +23 -0
- package/templates/react-router/app/welcome/logo-light.svg +23 -0
- package/templates/react-router/app/welcome/welcome.tsx +89 -0
- package/templates/react-router/gitignore +7 -0
- package/templates/react-router/package.json +31 -0
- package/templates/react-router/public/favicon.ico +0 -0
- package/templates/react-router/react-router.config.ts +7 -0
- package/templates/react-router/tsconfig.json +26 -0
- package/templates/react-router/vite.config.ts +11 -0
- package/dist/templates/api.d.ts +0 -16
- package/dist/templates/api.d.ts.map +0 -1
- package/dist/templates/react-router.d.ts +0 -26
- package/dist/templates/react-router.d.ts.map +0 -1
package/docs/troubleshooting.md
CHANGED
|
@@ -26,17 +26,16 @@ nothing — the symptom.
|
|
|
26
26
|
|
|
27
27
|
**Writing the project**
|
|
28
28
|
|
|
29
|
-
- [`create-alxia: create-react-router's … is not what this @alxia/create expects: …`](#create-alxia-create-react-routers--is-not-what-this-alxiacreate-expects-)
|
|
30
|
-
- [`create-alxia: failed: create-react-router exited with 1.`](#create-alxia-failed-create-react-router-exited-with-1)
|
|
31
29
|
- [`create-alxia: failed: …`](#create-alxia-failed-)
|
|
32
30
|
- [`create-alxia: warning: the registry did not answer for …; kept the versions the template ships.`](#create-alxia-warning-the-registry-did-not-answer-for--kept-the-versions-the-template-ships)
|
|
33
31
|
- [`typescript: kept to ^6.0.3 || ^7.0.0, where the newest is 7.0.2; npm's latest, 8.0.0, is outside it`](#typescript-kept-to-603--700-where-the-newest-is-702-npms-latest-800-is-outside-it)
|
|
34
32
|
- [`zod: no release within ^4.2.0; kept ^4.2.0`](#zod-no-release-within-420-kept-420)
|
|
33
|
+
- [`@alxia/core: the registry has no release within ^0.3.1 yet; wrote ^0.3.0, the newest of ~0.3.0`](#alxiacore-the-registry-has-no-release-within-031-yet-wrote-030-the-newest-of-030)
|
|
35
34
|
- [`create-alxia: bun install failed; the files are written.`](#create-alxia-bun-install-failed-the-files-are-written)
|
|
36
35
|
|
|
37
36
|
**After**
|
|
38
37
|
|
|
39
|
-
- [
|
|
38
|
+
- [`error: lockfile had changes, but lockfile is frozen`](#error-lockfile-had-changes-but-lockfile-is-frozen)
|
|
40
39
|
- [The project's `@alxia/*` are older than npm's latest](#the-projects-alxia-are-older-than-npms-latest)
|
|
41
40
|
|
|
42
41
|
## Before it runs
|
|
@@ -148,47 +147,6 @@ project beside it and move the files in.
|
|
|
148
147
|
|
|
149
148
|
## Writing the project
|
|
150
149
|
|
|
151
|
-
### `create-alxia: create-react-router's … is not what this @alxia/create expects: …`
|
|
152
|
-
|
|
153
|
-
**When:** the `react-router` template, after `create-react-router` ran.
|
|
154
|
-
The message names the file — `package.json`, `vite.config.ts` or
|
|
155
|
-
`bunfig.toml` — and what was expected of it:
|
|
156
|
-
|
|
157
|
-
- `it is missing`: no `package.json` or `vite.config.ts`;
|
|
158
|
-
- `react-router in its dependencies, and the scripts dev: react-router dev, build: react-router build and a start`;
|
|
159
|
-
- `reactRouter() from "@react-router/dev/vite", called once in plugins: [...]`;
|
|
160
|
-
- `it already imports @alxia/react-router/vite`;
|
|
161
|
-
- `none, and there is one`: a `bunfig.toml` already.
|
|
162
|
-
|
|
163
|
-
**Why:** React Router's template changed since this `@alxia/create` was
|
|
164
|
-
released, and the edits that add alxia check each file before changing it
|
|
165
|
-
rather than write a project that does not start. What was written is
|
|
166
|
-
removed: the target directory is emptied when it was there, removed when
|
|
167
|
-
it was not. A parent directory the command created for it, as `a/b` for
|
|
168
|
-
`a/b/my-site`, stays.
|
|
169
|
-
|
|
170
|
-
**Fix:** run the newest `@alxia/create`, whose edits follow the newest
|
|
171
|
-
template:
|
|
172
|
-
|
|
173
|
-
```sh
|
|
174
|
-
bunx @alxia/create@latest my-site --template react-router
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
If that one refuses too, add alxia to React Router's template by hand, as
|
|
178
|
-
[`@alxia/react-router`'s README](https://www.npmjs.com/package/@alxia/react-router)
|
|
179
|
-
shows (`bun add`, `alxia()` in `vite.config.ts`, `start`), and open an
|
|
180
|
-
issue.
|
|
181
|
-
|
|
182
|
-
### `create-alxia: failed: create-react-router exited with 1.`
|
|
183
|
-
|
|
184
|
-
**When:** the `react-router` template, when `create-react-router` itself
|
|
185
|
-
failed; its own output, just above, says why. Most often the network:
|
|
186
|
-
`bunx` could not fetch it, or it could not fetch React Router's template
|
|
187
|
-
from GitHub.
|
|
188
|
-
|
|
189
|
-
**Fix:** what its output says, then run the command again: what was
|
|
190
|
-
written is removed.
|
|
191
|
-
|
|
192
150
|
### `create-alxia: failed: …`
|
|
193
151
|
|
|
194
152
|
**When:** any other error while writing the project, with the error's own
|
|
@@ -196,7 +154,22 @@ message after `failed:` — most often the file system: `EACCES` writing
|
|
|
196
154
|
into a directory the user cannot write to, `ENOSPC` with the disk full.
|
|
197
155
|
|
|
198
156
|
**Fix:** what the message names, then run the command again: what was
|
|
199
|
-
written is removed
|
|
157
|
+
written is removed — the target directory is emptied when it was there,
|
|
158
|
+
removed when it was not. A parent directory the command created for it, as
|
|
159
|
+
`a/b` for `a/b/my-site`, stays.
|
|
160
|
+
|
|
161
|
+
### `create-alxia: failed: the api template names @alxia/zod at workspace:, which this @alxia/create has no version for`
|
|
162
|
+
|
|
163
|
+
**When:** at once, before anything is written; the template and package
|
|
164
|
+
vary.
|
|
165
|
+
|
|
166
|
+
**Why:** a template names an `@alxia/*` package at `workspace:^`, which
|
|
167
|
+
the command replaces with the version it was published beside, and this
|
|
168
|
+
release has none for that package. It is a broken `@alxia/create`
|
|
169
|
+
release, not your setup.
|
|
170
|
+
|
|
171
|
+
**Fix:** run the previous release, `bunx @alxia/create@<version>`, and
|
|
172
|
+
report it on [GitHub](https://github.com/softistx/alxia/issues).
|
|
200
173
|
|
|
201
174
|
### `create-alxia: warning: the registry did not answer for …; kept the versions the template ships.`
|
|
202
175
|
|
|
@@ -211,7 +184,10 @@ that is React Router's, whose TypeScript may be older than alxia accepts.
|
|
|
211
184
|
|
|
212
185
|
**Fix:** once the registry answers, run the command again in an empty
|
|
213
186
|
directory, or move the packages named within alxia's ranges by hand:
|
|
214
|
-
`bun add -d typescript@^7 vite@^8`.
|
|
187
|
+
`bun add -d typescript@^7 vite@^8`. The message can name an `@alxia/*`
|
|
188
|
+
package too, on a mirror that does not hold alxia's: the range kept is the
|
|
189
|
+
one this `@alxia/create` was published with, which that mirror cannot
|
|
190
|
+
install either, so let it proxy npmjs.org. Behind a proxy, check `BUN_CONFIG_REGISTRY` or
|
|
215
191
|
`npm_config_registry`, which the command reads.
|
|
216
192
|
|
|
217
193
|
### `typescript: kept to ^6.0.3 || ^7.0.0, where the newest is 7.0.2; npm's latest, 8.0.0, is outside it`
|
|
@@ -231,11 +207,27 @@ once the major is tested, and the next `@alxia/create` takes it.
|
|
|
231
207
|
**When:** a notice: the registry answered for the package, but none of its
|
|
232
208
|
releases is in the range alxia's packages accept — a registry mirror that
|
|
233
209
|
holds only some versions, most often. The template's own version is kept.
|
|
210
|
+
An `@alxia/*` package prints it too when the registry holds none of the
|
|
211
|
+
range's minor, not even an older patch.
|
|
234
212
|
|
|
235
213
|
**Fix:** check what the registry holds (`bun pm view zod versions`), let
|
|
236
214
|
the mirror fetch the missing ones, or write a version within the range by
|
|
237
215
|
hand after the project is created.
|
|
238
216
|
|
|
217
|
+
### `@alxia/core: the registry has no release within ^0.3.1 yet; wrote ^0.3.0, the newest of ~0.3.0`
|
|
218
|
+
|
|
219
|
+
**When:** a notice, not an error, right after an alxia release: the
|
|
220
|
+
registry lists `@alxia/create`'s new version but not yet the
|
|
221
|
+
`@alxia/core` (or `@alxia/client`, `@alxia/react-router`) published beside
|
|
222
|
+
it. npm can take a few minutes to serve a version everywhere.
|
|
223
|
+
|
|
224
|
+
**Why:** a project written with `^0.3.1` would fail its `bun install` with
|
|
225
|
+
`No version matching "^0.3.1" found`. The command writes the newest release
|
|
226
|
+
of the same minor instead; `^0.3.0` still takes 0.3.1.
|
|
227
|
+
|
|
228
|
+
**Fix:** nothing to do. Once the registry serves the new version,
|
|
229
|
+
`bun update @alxia/core` moves the project to it.
|
|
230
|
+
|
|
239
231
|
### `create-alxia: bun install failed; the files are written.`
|
|
240
232
|
|
|
241
233
|
**When:** `bun install` exited non-zero in the new project; its output,
|
|
@@ -247,32 +239,35 @@ project is complete; only `node_modules` is missing.
|
|
|
247
239
|
|
|
248
240
|
## After
|
|
249
241
|
|
|
250
|
-
###
|
|
242
|
+
### `error: lockfile had changes, but lockfile is frozen`
|
|
251
243
|
|
|
252
|
-
**
|
|
253
|
-
`
|
|
244
|
+
**When:** `docker build` in a project stops at
|
|
245
|
+
`RUN bun install --frozen-lockfile` (`react-router`) or
|
|
246
|
+
`RUN bun install --frozen-lockfile --production` (`api`).
|
|
254
247
|
|
|
255
|
-
**Why:**
|
|
256
|
-
|
|
257
|
-
|
|
248
|
+
**Why:** the project's `Dockerfile` installs exactly what `bun.lock`
|
|
249
|
+
records, and `package.json` now asks for something it does not: a
|
|
250
|
+
dependency added or changed by hand, without `bun install`, or a `bun.lock` left
|
|
251
|
+
behind in a clone where `package.json` moved on without it.
|
|
258
252
|
|
|
259
|
-
**Fix:**
|
|
260
|
-
`bun
|
|
261
|
-
`
|
|
262
|
-
[`@alxia/react-router`'s guide](https://github.com/softistx/alxia/blob/develop/packages/react-router/docs/guide.md#deploying)
|
|
263
|
-
says.
|
|
253
|
+
**Fix:** run `bun install`, commit `bun.lock`, and build again. The other
|
|
254
|
+
traps of the `react-router` image, a write refused to the `bun` user among them, are in
|
|
255
|
+
[`@alxia/react-router`'s troubleshooting](https://github.com/softistx/alxia/blob/develop/packages/react-router/docs/troubleshooting.md#eacces-permission-denied-open-app).
|
|
264
256
|
|
|
265
257
|
### The project's `@alxia/*` are older than npm's latest
|
|
266
258
|
|
|
267
|
-
**Symptom:** a fresh project declares `@alxia/core` at `^0.3.
|
|
268
|
-
has `0.4.0
|
|
259
|
+
**Symptom:** a fresh project declares `@alxia/core` at `^0.3.4` while npm
|
|
260
|
+
has `0.4.0`, and the output said `@alxia/core: kept to ^0.3.1, where the
|
|
261
|
+
newest is 0.3.4; npm's latest, 0.4.0, is outside it`.
|
|
269
262
|
|
|
270
|
-
**Why:** alxia's packages
|
|
271
|
-
that ran was published with ([Versions](guide.md#versions))
|
|
272
|
-
of `@alxia/core` alone does not release a
|
|
263
|
+
**Why:** alxia's packages move only within the ranges the `@alxia/create`
|
|
264
|
+
that ran was published with ([Versions](guide.md#versions)): a new minor
|
|
265
|
+
is outside them, and a release of `@alxia/core` alone does not release a
|
|
266
|
+
new `@alxia/create`.
|
|
273
267
|
|
|
274
268
|
**Fix:** `bunx @alxia/create@latest` for the newest `@alxia/create`, and in
|
|
275
269
|
an existing project
|
|
276
|
-
`bun add @alxia/core@latest @alxia/client@latest
|
|
270
|
+
`bun add @alxia/core@latest @alxia/client@latest` (`api`) or
|
|
271
|
+
`bun add @alxia/core@latest @alxia/react-router@latest` (`react-router`), reading
|
|
277
272
|
[`@alxia/core`'s upgrading page](https://github.com/softistx/alxia/blob/develop/packages/core/docs/upgrading.md)
|
|
278
273
|
for what a minor changed.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alxia/create",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Start an alxia app: `bun create @alxia` writes an API with Zod, or React Router's official template served by alxia",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|
|
12
12
|
"docs",
|
|
13
|
+
"templates/api",
|
|
14
|
+
"templates/react-router",
|
|
13
15
|
"README.md",
|
|
14
16
|
"package.json",
|
|
15
17
|
"LICENSE"
|
|
@@ -28,7 +30,7 @@
|
|
|
28
30
|
},
|
|
29
31
|
"scripts": {
|
|
30
32
|
"build": "bun run ../../build.ts",
|
|
31
|
-
"test": "bun test src",
|
|
33
|
+
"test": "bun test ./src",
|
|
32
34
|
"typecheck": "tsc --noEmit"
|
|
33
35
|
},
|
|
34
36
|
"alxia": {
|
|
@@ -39,7 +41,7 @@
|
|
|
39
41
|
"devDependencies": {
|
|
40
42
|
"@alxia/client": "^0.2.1",
|
|
41
43
|
"@alxia/core": "^0.3.1",
|
|
42
|
-
"@alxia/react-router": "^0.
|
|
44
|
+
"@alxia/react-router": "^0.3.0",
|
|
43
45
|
"@types/bun": "^1.4.2",
|
|
44
46
|
"zod": "^4.6.5"
|
|
45
47
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Bun runs the app's TypeScript as it is: no build stage, the image holds
|
|
2
|
+
# src/ and the production dependencies. Pinned to Bun 1, the major alxia
|
|
3
|
+
# supports.
|
|
4
|
+
|
|
5
|
+
# The production dependencies alone, for the final image.
|
|
6
|
+
FROM oven/bun:1 AS production-dependencies
|
|
7
|
+
WORKDIR /app
|
|
8
|
+
COPY package.json bun.lock* bunfig.toml* ./
|
|
9
|
+
RUN bun install --frozen-lockfile --production
|
|
10
|
+
|
|
11
|
+
# The app and its production dependencies, run as Bun's own non-root user
|
|
12
|
+
# with the start script's command, written out so that Bun is the
|
|
13
|
+
# container's process. The server listens on PORT (3000); set API_KEY.
|
|
14
|
+
FROM oven/bun:1
|
|
15
|
+
WORKDIR /app
|
|
16
|
+
ENV NODE_ENV=production
|
|
17
|
+
COPY package.json ./
|
|
18
|
+
COPY --from=production-dependencies /app/node_modules ./node_modules
|
|
19
|
+
COPY src ./src
|
|
20
|
+
USER bun
|
|
21
|
+
EXPOSE 3000
|
|
22
|
+
CMD ["bun", "src/server.ts"]
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# An alxia API
|
|
2
|
+
|
|
3
|
+
An [alxia](https://github.com/softistx/alxia) app with
|
|
4
|
+
[Zod](https://zod.dev), made with `bun create @alxia`.
|
|
5
|
+
|
|
6
|
+
- `src/app.ts`: the app. `POST /todos` validates its body with Zod, and
|
|
7
|
+
its own hook, `requireKey`, answers 401 without the `x-api-key` header.
|
|
8
|
+
- `src/server.ts`: listens on `PORT`, 3000 by default.
|
|
9
|
+
- `src/app.spec.ts`: `app.request()` and `@alxia/client`, no port.
|
|
10
|
+
|
|
11
|
+
## Environment
|
|
12
|
+
|
|
13
|
+
Bun loads `.env` on every command. Copy `.env.example` to `.env` to set
|
|
14
|
+
`PORT` and `API_KEY`. Outside development, set `API_KEY`: its default,
|
|
15
|
+
`dev-key`, is for development only.
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
cp .env.example .env
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Develop
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
bun dev # http://localhost:3000, restarted on every change
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
curl -X POST localhost:3000/todos \
|
|
29
|
+
-H 'content-type: application/json' -H 'x-api-key: dev-key' \
|
|
30
|
+
-d '{"title":"Write a route"}'
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Test
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
bun test # src/app.spec.ts: in process, and through the typed client
|
|
37
|
+
bun run typecheck
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Build
|
|
41
|
+
|
|
42
|
+
Bun runs the TypeScript as it is: `bun start` serves `src/server.ts`, with
|
|
43
|
+
no build step. `bun run build` bundles the server and its dependencies into
|
|
44
|
+
one file, for a host with Bun and no `node_modules`:
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
bun run build # dist/server.js
|
|
48
|
+
bun dist/server.js
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Docker
|
|
52
|
+
|
|
53
|
+
The `Dockerfile` installs the production dependencies on `oven/bun:1` and
|
|
54
|
+
runs `src/server.ts` as the image's non-root `bun` user. It installs with
|
|
55
|
+
`--frozen-lockfile`, from the `bun.lock` that `bun install` wrote: commit
|
|
56
|
+
it.
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
docker build -t my-api .
|
|
60
|
+
docker run --rm -p 3000:3000 -e API_KEY=change-me my-api
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Next: [Getting started](https://github.com/softistx/alxia/blob/develop/packages/core/docs/guide/getting-started.md)
|
|
64
|
+
and the rest of [`@alxia/core`'s guide](https://github.com/softistx/alxia/tree/develop/packages/core/docs).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "my-api",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "bun --watch src/server.ts",
|
|
7
|
+
"build": "bun build src/server.ts --target=bun --outdir=dist",
|
|
8
|
+
"start": "bun src/server.ts",
|
|
9
|
+
"test": "bun test",
|
|
10
|
+
"typecheck": "tsc --noEmit"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@alxia/core": "workspace:^",
|
|
14
|
+
"zod": "^4.2.0"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@alxia/client": "workspace:^",
|
|
18
|
+
"@types/bun": "^1.4.2",
|
|
19
|
+
"typescript": "^6.0.3"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { expect, test } from 'bun:test';
|
|
2
|
+
import { client } from '@alxia/client';
|
|
3
|
+
import { apiKey, app } from './app';
|
|
4
|
+
|
|
5
|
+
const json = { 'content-type': 'application/json', 'x-api-key': apiKey };
|
|
6
|
+
|
|
7
|
+
test('creates a todo from JSON', async () => {
|
|
8
|
+
const response = await app.request('/todos', {
|
|
9
|
+
method: 'POST',
|
|
10
|
+
headers: json,
|
|
11
|
+
body: JSON.stringify({ title: 'Write a route' }),
|
|
12
|
+
});
|
|
13
|
+
expect(response.status).toBe(201);
|
|
14
|
+
expect(await response.json()).toEqual({
|
|
15
|
+
id: expect.any(Number),
|
|
16
|
+
title: 'Write a route',
|
|
17
|
+
done: false,
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('refuses an empty title with a 400 naming it', async () => {
|
|
22
|
+
const response = await app.request('/todos', {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
headers: json,
|
|
25
|
+
body: JSON.stringify({ title: '' }),
|
|
26
|
+
});
|
|
27
|
+
expect(response.status).toBe(400);
|
|
28
|
+
expect((await response.json()).issues[0].path).toEqual(['title']);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('the typed client reads each status the route answers', async () => {
|
|
32
|
+
// Given the app itself, the client calls its fetch in process.
|
|
33
|
+
const api = client(app, { headers: { 'x-api-key': apiKey } });
|
|
34
|
+
const created = await api.post('/todos', {
|
|
35
|
+
body: { title: 'Call it typed' },
|
|
36
|
+
});
|
|
37
|
+
if (created.status !== 201) throw new Error(`got ${created.status}`);
|
|
38
|
+
expect(created.data.title).toBe('Call it typed'); // data is the Todo schema's type
|
|
39
|
+
|
|
40
|
+
const anonymous = await client(app).post('/todos', {
|
|
41
|
+
body: { title: 'No key' },
|
|
42
|
+
});
|
|
43
|
+
expect(anonymous.status).toBe(401);
|
|
44
|
+
if (anonymous.status === 401)
|
|
45
|
+
expect(anonymous.data.error).toBe('unauthorized');
|
|
46
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { alxia, defineHook } from '@alxia/core';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
const Todo = z.object({ id: z.number(), title: z.string(), done: z.boolean() });
|
|
5
|
+
const NewTodo = z.object({ title: z.string().min(1) });
|
|
6
|
+
|
|
7
|
+
/** Set API_KEY in the environment: this default is for development. */
|
|
8
|
+
export const apiKey = Bun.env['API_KEY'] ?? 'dev-key';
|
|
9
|
+
|
|
10
|
+
// A hook of the routes it is given to: it answers 401 without the key, and
|
|
11
|
+
// that 401 joins the type of each, so the client reads it.
|
|
12
|
+
const requireKey = defineHook(({ request, reply }) =>
|
|
13
|
+
request.headers.get('x-api-key') === apiKey
|
|
14
|
+
? undefined
|
|
15
|
+
: reply(401, { error: 'unauthorized' as const }),
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const todos: z.infer<typeof Todo>[] = [];
|
|
19
|
+
|
|
20
|
+
export const app = alxia()
|
|
21
|
+
.decorate({ todos })
|
|
22
|
+
.post(
|
|
23
|
+
'/todos',
|
|
24
|
+
[requireKey],
|
|
25
|
+
{ body: NewTodo, response: { 201: Todo } },
|
|
26
|
+
({ body, todos, reply }) => {
|
|
27
|
+
const todo = { id: todos.length + 1, title: body.title, done: false };
|
|
28
|
+
todos.push(todo);
|
|
29
|
+
return reply.created(todo);
|
|
30
|
+
},
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
export type App = typeof app;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"types": ["bun"],
|
|
4
|
+
"lib": ["ESNext", "DOM"],
|
|
5
|
+
"target": "ESNext",
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"moduleDetection": "force",
|
|
8
|
+
"moduleResolution": "bundler",
|
|
9
|
+
"verbatimModuleSyntax": true,
|
|
10
|
+
"noEmit": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"strict": true,
|
|
14
|
+
"noImplicitOverride": true,
|
|
15
|
+
"noImplicitReturns": true,
|
|
16
|
+
"noFallthroughCasesInSwitch": true,
|
|
17
|
+
"noUncheckedIndexedAccess": true,
|
|
18
|
+
"exactOptionalPropertyTypes": true,
|
|
19
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
20
|
+
"noUnusedLocals": true,
|
|
21
|
+
"noUnusedParameters": true,
|
|
22
|
+
"forceConsistentCasingInFileNames": true
|
|
23
|
+
},
|
|
24
|
+
"include": ["src"]
|
|
25
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Bun builds the app and runs it: build/server/index.js is alxia's server,
|
|
2
|
+
# which runs on Bun. Pinned to Bun 1, the major alxia supports.
|
|
3
|
+
|
|
4
|
+
# The production dependencies alone, for the final image.
|
|
5
|
+
FROM oven/bun:1 AS production-dependencies
|
|
6
|
+
WORKDIR /app
|
|
7
|
+
COPY package.json bun.lock* bunfig.toml* ./
|
|
8
|
+
RUN bun install --frozen-lockfile --production
|
|
9
|
+
|
|
10
|
+
# Every dependency, then react-router build: build/client and
|
|
11
|
+
# build/server/index.js.
|
|
12
|
+
FROM oven/bun:1 AS build
|
|
13
|
+
WORKDIR /app
|
|
14
|
+
COPY package.json bun.lock* bunfig.toml* ./
|
|
15
|
+
RUN bun install --frozen-lockfile
|
|
16
|
+
COPY . .
|
|
17
|
+
RUN bun run build
|
|
18
|
+
|
|
19
|
+
# The build and the production dependencies, run as Bun's own non-root
|
|
20
|
+
# user. The server listens on PORT (3000) and HOST (0.0.0.0).
|
|
21
|
+
FROM oven/bun:1
|
|
22
|
+
WORKDIR /app
|
|
23
|
+
ENV NODE_ENV=production
|
|
24
|
+
COPY package.json ./
|
|
25
|
+
COPY --from=production-dependencies /app/node_modules ./node_modules
|
|
26
|
+
COPY --from=build /app/build ./build
|
|
27
|
+
USER bun
|
|
28
|
+
EXPOSE 3000
|
|
29
|
+
CMD ["bun", "build/server/index.js"]
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Welcome to React Router!
|
|
2
|
+
|
|
3
|
+
A modern, production-ready template for building full-stack React applications using React Router.
|
|
4
|
+
|
|
5
|
+
[](https://stackblitz.com/github/remix-run/react-router-templates/tree/main/default)
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- 🚀 Server-side rendering
|
|
10
|
+
- ⚡️ Hot Module Replacement (HMR)
|
|
11
|
+
- 📦 Asset bundling and optimization
|
|
12
|
+
- 🔄 Data loading and mutations
|
|
13
|
+
- 🔒 TypeScript by default
|
|
14
|
+
- 🎉 TailwindCSS for styling
|
|
15
|
+
- 📖 [React Router docs](https://reactrouter.com/)
|
|
16
|
+
|
|
17
|
+
## Getting Started
|
|
18
|
+
|
|
19
|
+
### Installation
|
|
20
|
+
|
|
21
|
+
Install the dependencies:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
bun install
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Development
|
|
28
|
+
|
|
29
|
+
Start the development server with HMR:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
bun dev
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Your application will be available at `http://localhost:5173`.
|
|
36
|
+
|
|
37
|
+
## Building for Production
|
|
38
|
+
|
|
39
|
+
Create a production build:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
bun run build
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Deployment
|
|
46
|
+
|
|
47
|
+
### Docker Deployment
|
|
48
|
+
|
|
49
|
+
To build and run using Docker:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
docker build -t my-app .
|
|
53
|
+
|
|
54
|
+
# Run the container
|
|
55
|
+
docker run -p 3000:3000 my-app
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The containerized application can be deployed to any platform that supports Docker, including:
|
|
59
|
+
|
|
60
|
+
- AWS ECS
|
|
61
|
+
- Google Cloud Run
|
|
62
|
+
- Azure Container Apps
|
|
63
|
+
- Digital Ocean App Platform
|
|
64
|
+
- Fly.io
|
|
65
|
+
- Railway
|
|
66
|
+
|
|
67
|
+
### DIY Deployment
|
|
68
|
+
|
|
69
|
+
The build is production-ready: `bun run start` runs `build/server/index.js` on Bun.
|
|
70
|
+
|
|
71
|
+
Make sure to deploy the output of `bun run build`
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
├── package.json
|
|
75
|
+
├── bun.lock
|
|
76
|
+
├── build/
|
|
77
|
+
│ ├── client/ # Static assets
|
|
78
|
+
│ └── server/ # Server-side code
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Styling
|
|
82
|
+
|
|
83
|
+
This template comes with [Tailwind CSS](https://tailwindcss.com/) already configured for a simple default starting experience. You can use whatever CSS framework you prefer.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
Built with ❤️ using React Router.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
|
|
3
|
+
@theme {
|
|
4
|
+
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif,
|
|
5
|
+
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
html,
|
|
9
|
+
body {
|
|
10
|
+
@apply bg-white dark:bg-gray-950;
|
|
11
|
+
|
|
12
|
+
@media (prefers-color-scheme: dark) {
|
|
13
|
+
color-scheme: dark;
|
|
14
|
+
}
|
|
15
|
+
}
|