@shaferllc/keel 0.59.0 → 0.68.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.
- package/AGENTS.md +167 -0
- package/README.md +30 -1
- package/bin/keel-mcp.mjs +9 -0
- package/dist/core/application.d.ts +5 -5
- package/dist/core/application.js +14 -2
- package/dist/core/auth.d.ts +47 -0
- package/dist/core/auth.js +77 -0
- package/dist/core/authorization.d.ts +9 -1
- package/dist/core/authorization.js +22 -2
- package/dist/core/cache.d.ts +82 -5
- package/dist/core/cache.js +181 -23
- package/dist/core/cli/stubs.d.ts +12 -0
- package/dist/core/cli/stubs.js +120 -0
- package/dist/core/container.d.ts +20 -0
- package/dist/core/container.js +52 -0
- package/dist/core/cors.d.ts +29 -0
- package/dist/core/cors.js +72 -0
- package/dist/core/crypto.d.ts +40 -4
- package/dist/core/crypto.js +66 -6
- package/dist/core/csrf.d.ts +25 -0
- package/dist/core/csrf.js +78 -0
- package/dist/core/database.d.ts +49 -4
- package/dist/core/database.js +89 -21
- package/dist/core/events.d.ts +129 -5
- package/dist/core/events.js +165 -7
- package/dist/core/health.d.ts +141 -0
- package/dist/core/health.js +226 -0
- package/dist/core/helpers.d.ts +15 -3
- package/dist/core/helpers.js +23 -3
- package/dist/core/index.d.ts +33 -18
- package/dist/core/index.js +16 -8
- package/dist/core/lock.d.ts +139 -0
- package/dist/core/lock.js +215 -0
- package/dist/core/logger.d.ts +82 -4
- package/dist/core/logger.js +141 -23
- package/dist/core/mail.d.ts +128 -7
- package/dist/core/mail.js +264 -16
- package/dist/core/model.d.ts +2 -0
- package/dist/core/model.js +16 -14
- package/dist/core/provider.d.ts +7 -0
- package/dist/core/provider.js +7 -0
- package/dist/core/queue.d.ts +134 -9
- package/dist/core/queue.js +304 -14
- package/dist/core/rate-limit.js +3 -0
- package/dist/core/relations.js +13 -13
- package/dist/core/request.d.ts +26 -0
- package/dist/core/request.js +77 -0
- package/dist/core/shield.d.ts +39 -0
- package/dist/core/shield.js +60 -0
- package/dist/core/social.d.ts +173 -0
- package/dist/core/social.js +337 -0
- package/dist/core/storage.d.ts +159 -6
- package/dist/core/storage.js +287 -7
- package/dist/core/tokens.d.ts +74 -0
- package/dist/core/tokens.js +155 -0
- package/dist/db/d1.d.ts +32 -0
- package/dist/db/d1.js +26 -0
- package/dist/db/libsql.d.ts +29 -0
- package/dist/db/libsql.js +32 -0
- package/dist/db/pg.d.ts +29 -0
- package/dist/db/pg.js +33 -0
- package/dist/mcp/server.d.ts +19 -0
- package/dist/mcp/server.js +355 -0
- package/docs/ai-manifest.json +2472 -0
- package/docs/ai.md +128 -0
- package/docs/architecture.md +331 -0
- package/docs/authentication.md +453 -0
- package/docs/authorization.md +167 -0
- package/docs/broadcasting.md +137 -0
- package/docs/broker.md +500 -0
- package/docs/cache.md +558 -0
- package/docs/configuration.md +311 -0
- package/docs/console.md +356 -0
- package/docs/container.md +467 -0
- package/docs/controllers.md +265 -0
- package/docs/cors.md +51 -0
- package/docs/database.md +530 -0
- package/docs/debugging.md +129 -0
- package/docs/decorators.md +127 -0
- package/docs/errors.md +395 -0
- package/docs/events.md +496 -0
- package/docs/examples/architecture-app.ts +27 -0
- package/docs/examples/authentication.ts +61 -0
- package/docs/examples/authorization.ts +79 -0
- package/docs/examples/broadcasting.ts +60 -0
- package/docs/examples/broker-cache-validate.ts +34 -0
- package/docs/examples/broker-fault-tolerance.ts +29 -0
- package/docs/examples/broker-middleware.ts +27 -0
- package/docs/examples/broker.ts +203 -0
- package/docs/examples/cache.ts +222 -0
- package/docs/examples/configuration.ts +81 -0
- package/docs/examples/container.ts +134 -0
- package/docs/examples/controllers.ts +86 -0
- package/docs/examples/database.ts +118 -0
- package/docs/examples/debugging.ts +41 -0
- package/docs/examples/decorators.ts +40 -0
- package/docs/examples/errors.ts +121 -0
- package/docs/examples/events.ts +204 -0
- package/docs/examples/factories.ts +84 -0
- package/docs/examples/hashing.ts +71 -0
- package/docs/examples/health.ts +94 -0
- package/docs/examples/helpers.ts +171 -0
- package/docs/examples/hooks.ts +54 -0
- package/docs/examples/inertia.ts +81 -0
- package/docs/examples/locks.ts +120 -0
- package/docs/examples/logger.ts +92 -0
- package/docs/examples/mail.ts +160 -0
- package/docs/examples/middleware.ts +119 -0
- package/docs/examples/migrations.ts +126 -0
- package/docs/examples/models.ts +239 -0
- package/docs/examples/notification.ts +124 -0
- package/docs/examples/providers.ts +123 -0
- package/docs/examples/queues.ts +254 -0
- package/docs/examples/rate-limiting.ts +42 -0
- package/docs/examples/redis.ts +99 -0
- package/docs/examples/request-response.ts +197 -0
- package/docs/examples/routing.ts +186 -0
- package/docs/examples/scheduling.ts +62 -0
- package/docs/examples/sessions.ts +102 -0
- package/docs/examples/static-files.ts +63 -0
- package/docs/examples/storage.ts +132 -0
- package/docs/examples/templates.ts +58 -0
- package/docs/examples/testing.ts +66 -0
- package/docs/examples/transformer.ts +141 -0
- package/docs/examples/transformers.ts +49 -0
- package/docs/examples/url-builder.ts +86 -0
- package/docs/examples/validation.ts +102 -0
- package/docs/examples/views.tsx +62 -0
- package/docs/examples/vite.ts +106 -0
- package/docs/factories.md +166 -0
- package/docs/getting-started.md +290 -0
- package/docs/hashing.md +259 -0
- package/docs/health.md +225 -0
- package/docs/helpers.md +347 -0
- package/docs/hono.md +186 -0
- package/docs/hooks.md +118 -0
- package/docs/inertia.md +241 -0
- package/docs/locks.md +323 -0
- package/docs/logger.md +290 -0
- package/docs/mail.md +678 -0
- package/docs/middleware.md +425 -0
- package/docs/migrations.md +476 -0
- package/docs/models.md +810 -0
- package/docs/notifications.md +474 -0
- package/docs/providers.md +363 -0
- package/docs/queues.md +679 -0
- package/docs/rate-limiting.md +155 -0
- package/docs/redis.md +178 -0
- package/docs/request-response.md +953 -0
- package/docs/routing.md +804 -0
- package/docs/scheduling.md +110 -0
- package/docs/security.md +85 -0
- package/docs/sessions.md +354 -0
- package/docs/social-auth.md +174 -0
- package/docs/static-files.md +211 -0
- package/docs/storage.md +450 -0
- package/docs/templates.md +315 -0
- package/docs/testing.md +125 -0
- package/docs/transformers.md +381 -0
- package/docs/url-builder.md +295 -0
- package/docs/validation.md +288 -0
- package/docs/views.md +267 -0
- package/docs/vite.md +434 -0
- package/llms-full.txt +17694 -0
- package/llms.txt +116 -0
- package/package.json +38 -7
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Factories & Seeders
|
|
2
|
+
|
|
3
|
+
Populate the database with realistic fixtures for tests and demos. A **factory**
|
|
4
|
+
describes how to build a model's attributes; a **seeder** orchestrates factories
|
|
5
|
+
(and raw writes) into a repeatable dataset. Both are edge-safe and dependency-free
|
|
6
|
+
— the built-in `Faker` needs no external library.
|
|
7
|
+
|
|
8
|
+
## Factories
|
|
9
|
+
|
|
10
|
+
Define a factory with the model and a definition function. The function receives
|
|
11
|
+
a `Faker` and the instance index, and returns the attributes:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { factory } from "@shaferllc/keel/core";
|
|
15
|
+
|
|
16
|
+
const users = factory(User, (f, i) => ({
|
|
17
|
+
name: f.name(),
|
|
18
|
+
email: f.email(),
|
|
19
|
+
}));
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Generate one with `keel make:factory User` (→ `database/factories/UserFactory.ts`).
|
|
23
|
+
|
|
24
|
+
### Building vs. persisting
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
users.make(); // a User instance, not saved
|
|
28
|
+
users.make({ name: "Ada" }); // with an override
|
|
29
|
+
await users.create(); // saved via Model.create, id back-filled
|
|
30
|
+
await users.create({ role: "admin" });
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Batches
|
|
34
|
+
|
|
35
|
+
`count(n)` sets how many the next call produces (an array):
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
const ten = await users.count(10).create(); // User[]
|
|
39
|
+
const drafts = users.count(3).make(); // User[] (unsaved)
|
|
40
|
+
|
|
41
|
+
// the index lets each row differ
|
|
42
|
+
factory(User, (f, i) => ({ email: `user${i}@x.com` })).count(5);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Faker
|
|
46
|
+
|
|
47
|
+
A small, seedable generator — enough for believable data without a dependency.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
const f = new Faker(); // random
|
|
51
|
+
const f = new Faker(42); // seeded — reproducible runs
|
|
52
|
+
|
|
53
|
+
f.name(); // "Grace Hopper"
|
|
54
|
+
f.firstName(); f.lastName();
|
|
55
|
+
f.email(); // "grace.hopper.1234@example.com"
|
|
56
|
+
f.word(); f.words(3); f.sentence(); f.paragraph();
|
|
57
|
+
f.number(1, 100); f.boolean();
|
|
58
|
+
f.pick(["a", "b", "c"]);
|
|
59
|
+
f.slug(); f.uuid();
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Seed a factory's faker for deterministic fixtures:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import { Faker } from "@shaferllc/keel/core";
|
|
66
|
+
users.usingFaker(new Faker(42));
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`Faker` uses an xorshift32 PRNG — fast, edge-safe, and seedable. (Its `uuid()` is
|
|
70
|
+
for fixtures, not security; use `crypto` for real identifiers.)
|
|
71
|
+
|
|
72
|
+
## Seeders
|
|
73
|
+
|
|
74
|
+
A seeder is a class with a `run()` method. Generate one with
|
|
75
|
+
`keel make:seeder Database`:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { Seeder } from "@shaferllc/keel/core";
|
|
79
|
+
|
|
80
|
+
class UserSeeder extends Seeder {
|
|
81
|
+
async run() {
|
|
82
|
+
await factory(User, (f) => ({ name: f.name(), email: f.email() }))
|
|
83
|
+
.count(10)
|
|
84
|
+
.create();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
class DatabaseSeeder extends Seeder {
|
|
89
|
+
async run() {
|
|
90
|
+
await this.call([UserSeeder]); // compose seeders in order
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Run one with the `seed` helper (after your connection is registered):
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import { seed } from "@shaferllc/keel/core";
|
|
99
|
+
import { setConnection } from "@shaferllc/keel/core";
|
|
100
|
+
|
|
101
|
+
setConnection(myConnection, "postgres");
|
|
102
|
+
await seed(DatabaseSeeder);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## In tests
|
|
106
|
+
|
|
107
|
+
Factories shine in tests — register a connection (or a mock), then build fixtures
|
|
108
|
+
inline:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
setConnection(testConnection, "sqlite");
|
|
112
|
+
|
|
113
|
+
const [author] = await factory(User, (f) => ({ name: f.name() })).count(1).create();
|
|
114
|
+
const post = await factory(Post, (f) => ({ title: f.sentence() })).create({
|
|
115
|
+
user_id: author.id,
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## API reference
|
|
120
|
+
|
|
121
|
+
### `factory(model, definition)`
|
|
122
|
+
|
|
123
|
+
`factory<T extends Model>(model, definition: (f: Faker, i: number) => Row): ModelFactory<T>`
|
|
124
|
+
|
|
125
|
+
Start a factory for `model`. The definition receives a `Faker` and the instance
|
|
126
|
+
index, and returns the attributes.
|
|
127
|
+
|
|
128
|
+
### `ModelFactory`
|
|
129
|
+
|
|
130
|
+
Returned by `factory()` (the `Factory` class, exported as `ModelFactory`).
|
|
131
|
+
|
|
132
|
+
| Method | Signature | Notes |
|
|
133
|
+
|--------|-----------|-------|
|
|
134
|
+
| `make` | `(overrides?) => T \| T[]` | build instance(s), unsaved |
|
|
135
|
+
| `create` | `(overrides?) => Promise<T \| T[]>` | persist via `Model.create` |
|
|
136
|
+
| `count` | `(n) => this` | how many the next `make`/`create` yields |
|
|
137
|
+
| `usingFaker` | `(faker) => this` | use a seeded `Faker` for reproducible data |
|
|
138
|
+
|
|
139
|
+
`count(1)` still returns an array; `count()` unset returns a single model.
|
|
140
|
+
|
|
141
|
+
### `Faker`
|
|
142
|
+
|
|
143
|
+
`new Faker(seed?)` — seedable (deterministic) xorshift32 generator.
|
|
144
|
+
|
|
145
|
+
| Method | Returns |
|
|
146
|
+
|--------|---------|
|
|
147
|
+
| `name` / `firstName` / `lastName` | string |
|
|
148
|
+
| `email` / `slug` / `uuid` | string |
|
|
149
|
+
| `word` / `words(n?)` / `sentence(n?)` / `paragraph(n?)` | string |
|
|
150
|
+
| `number(min?, max?)` | number |
|
|
151
|
+
| `boolean` | boolean |
|
|
152
|
+
| `pick(items)` | one element of `items` |
|
|
153
|
+
|
|
154
|
+
`uuid()` is for fixtures, not security — use `crypto` for real ids.
|
|
155
|
+
|
|
156
|
+
### `Seeder` / `seed(SeederClass)`
|
|
157
|
+
|
|
158
|
+
Abstract `Seeder` with an `async run()`; `protected call([...Seeders])` composes
|
|
159
|
+
others in order. `seed(DatabaseSeeder)` instantiates and runs one.
|
|
160
|
+
|
|
161
|
+
### Interfaces & types
|
|
162
|
+
|
|
163
|
+
#### `Definition`
|
|
164
|
+
|
|
165
|
+
`type Definition<T> = (faker: Faker, index: number) => Row` — the factory
|
|
166
|
+
definition function.
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
Keel is a house framework for Node.js — a small, legible MVC layer over
|
|
4
|
+
[Hono](./hono.md). This guide is a guided first hour: install it, stand up a
|
|
5
|
+
route, a controller, and a view, read some config, drive the console, and know
|
|
6
|
+
where to go next.
|
|
7
|
+
|
|
8
|
+
## Requirements
|
|
9
|
+
|
|
10
|
+
- Node.js **≥ 22**
|
|
11
|
+
- npm (ships with Node)
|
|
12
|
+
|
|
13
|
+
Keel targets modern Node and web-standard APIs, so a current runtime matters —
|
|
14
|
+
`22` is the floor.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
Keel ships as two repos: the **framework** (`@shaferllc/keel`, published to
|
|
19
|
+
npm) and a **starter app** (`shaferllc/keel-app`) you build on. Which path you
|
|
20
|
+
take depends on whether you're starting fresh or bolting Keel onto something
|
|
21
|
+
that already exists. See [Architecture](./architecture.md#two-repos-library-and-starter)
|
|
22
|
+
for why it's split this way.
|
|
23
|
+
|
|
24
|
+
### From the starter (recommended)
|
|
25
|
+
|
|
26
|
+
The fastest route to a running app is the starter — a working skeleton with the
|
|
27
|
+
folders, config, and scripts already wired:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
git clone https://github.com/shaferllc/keel-app.git my-app
|
|
31
|
+
cd my-app
|
|
32
|
+
npm install
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The starter depends on `@shaferllc/keel`, so you pick up framework updates with
|
|
36
|
+
an ordinary `npm update @shaferllc/keel` — your app code in `app/` stays put.
|
|
37
|
+
|
|
38
|
+
A fresh checkout ships with a working `.env`. To start from the template
|
|
39
|
+
instead:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
cp .env.example .env
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Into an existing app
|
|
46
|
+
|
|
47
|
+
Already have a Node project? Add the package:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm install @shaferllc/keel
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Everything Keel exposes comes from one entry point:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { Application, Router, config } from "@shaferllc/keel/core";
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
You supply the four convention folders yourself — `app/`, `config/`, `routes/`,
|
|
60
|
+
`bootstrap/` — plus an entry that calls `createApplication()`. The starter's
|
|
61
|
+
`bootstrap/app.ts` is the reference; copy it and trim to taste.
|
|
62
|
+
|
|
63
|
+
### Hacking on the framework itself
|
|
64
|
+
|
|
65
|
+
To work on Keel proper, clone the framework repo — it carries an example app you
|
|
66
|
+
can run directly:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
git clone https://github.com/shaferllc/keel.git
|
|
70
|
+
cd keel
|
|
71
|
+
npm install
|
|
72
|
+
npm run dev # example app on http://localhost:3000
|
|
73
|
+
npm run build # compile the package to dist/
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Run the server
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm run dev # tsx watch — restarts on change
|
|
80
|
+
# or
|
|
81
|
+
npm run serve # one-shot
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
You should see:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
⚓ Keel listening on http://localhost:3000
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Hit the sample routes the starter ships with:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
curl localhost:3000/ # {"framework":"Keel", ...}
|
|
94
|
+
curl localhost:3000/ping # {"pong":true}
|
|
95
|
+
curl localhost:3000/hello/Tom # Hello, Tom!
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Your first route
|
|
99
|
+
|
|
100
|
+
Routes live in `routes/web.ts`. The simplest is a **closure** — a function that
|
|
101
|
+
takes the request context `c` and returns a response:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
router.get("/status", (c) => c.json({ ok: true, time: Date.now() }));
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Save — `tsx watch` reloads — and visit `http://localhost:3000/status`.
|
|
108
|
+
|
|
109
|
+
A route handler can be a closure, a `[Controller, method]` tuple, or even a
|
|
110
|
+
ready-made `Response`. Closures are perfect for one-liners; reach for a
|
|
111
|
+
controller once there's real logic to house. Parameters come off the path with a
|
|
112
|
+
leading colon:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
router.get("/greet/:name", (c) => c.text(`Ahoy, ${c.req.param("name")}!`));
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
You don't have to thread `c` everywhere, either — Keel's [request
|
|
119
|
+
helpers](./request-response.md) reach the active request from anywhere, so the
|
|
120
|
+
same route reads:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
import { text, param } from "@shaferllc/keel/core";
|
|
124
|
+
|
|
125
|
+
router.get("/greet/:name", () => text(`Ahoy, ${param("name")}!`));
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
See [Routing](./routing.md) for names, groups, resource routes, param
|
|
129
|
+
constraints, and URL generation.
|
|
130
|
+
|
|
131
|
+
## Your first controller
|
|
132
|
+
|
|
133
|
+
Once a handler grows past a line or two, move it into a controller. Generate one
|
|
134
|
+
with the console:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
npm run keel make:controller Task
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
That writes `app/Controllers/TaskController.ts`:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
import type { Ctx } from "@shaferllc/keel/core";
|
|
144
|
+
|
|
145
|
+
export class TaskController {
|
|
146
|
+
index(c: Ctx) {
|
|
147
|
+
return c.json({ controller: "TaskController", action: "index" });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Wire it up in `routes/web.ts` with a `[Controller, method]` tuple. Keel resolves
|
|
153
|
+
the controller **out of the container**, so its constructor gets dependency
|
|
154
|
+
injection for free:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
import { TaskController } from "../app/Controllers/TaskController.js";
|
|
158
|
+
|
|
159
|
+
router.get("/tasks", [TaskController, "index"]);
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Confirm it's registered:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
npm run keel routes
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
GET /tasks TaskController@index
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Add more actions as plain methods, and give related routes their REST shape in
|
|
173
|
+
one call with `router.resource("tasks", TaskController)`. [Controllers](./controllers.md)
|
|
174
|
+
covers single-action controllers, lazy-loaded controllers, and how DI reaches
|
|
175
|
+
the constructor.
|
|
176
|
+
|
|
177
|
+
## Your first view
|
|
178
|
+
|
|
179
|
+
Keel views are [Hono JSX](./hono.md) components — plain functions that return
|
|
180
|
+
markup. They live by convention in `resources/views/`. Create
|
|
181
|
+
`resources/views/tasks.tsx`:
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
// @jsxImportSource hono/jsx
|
|
185
|
+
import type { FC } from "hono/jsx";
|
|
186
|
+
|
|
187
|
+
export const TasksPage: FC<{ count: number }> = ({ count }) => (
|
|
188
|
+
<main>
|
|
189
|
+
<h1>⚓ Tasks</h1>
|
|
190
|
+
<p>You have {count} task(s) aboard.</p>
|
|
191
|
+
</main>
|
|
192
|
+
);
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Render it from the controller with the `view()` helper — it renders the
|
|
196
|
+
component to a full HTML document and type-checks the props against the
|
|
197
|
+
component:
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
import type { Ctx } from "@shaferllc/keel/core";
|
|
201
|
+
import { view } from "@shaferllc/keel/core";
|
|
202
|
+
import { TasksPage } from "../../resources/views/tasks.js";
|
|
203
|
+
|
|
204
|
+
export class TaskController {
|
|
205
|
+
index(c: Ctx) {
|
|
206
|
+
return view(TasksPage, { count: 3 });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Note the `.js` import specifier for a `.tsx` file — that's the Node ESM
|
|
212
|
+
convention, and it's correct even though the file on disk is TypeScript. See
|
|
213
|
+
[Views](./views.md) for layouts, async components, and streaming.
|
|
214
|
+
|
|
215
|
+
## Configuration
|
|
216
|
+
|
|
217
|
+
Config files live in `config/` and each exports a default object. They're loaded
|
|
218
|
+
at boot under their filename, so `config/app.ts` is reachable as `config('app.*')`:
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
// config/app.ts
|
|
222
|
+
import { env } from "@shaferllc/keel/core";
|
|
223
|
+
|
|
224
|
+
export default {
|
|
225
|
+
name: env("APP_NAME", "Keel"),
|
|
226
|
+
env: env("APP_ENV", "local"),
|
|
227
|
+
debug: env("APP_DEBUG", true),
|
|
228
|
+
url: env("APP_URL", "http://localhost:3000"),
|
|
229
|
+
port: env("APP_PORT", 3000),
|
|
230
|
+
};
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`env()` reads a variable from `.env` (loaded at boot) with a typed fallback — it
|
|
234
|
+
coerces `"true"`/`"false"` to booleans and numeric strings to numbers when the
|
|
235
|
+
fallback is a number. Read config anywhere with the `config()` helper, using dot
|
|
236
|
+
notation and an optional fallback:
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
import { config } from "@shaferllc/keel/core";
|
|
240
|
+
|
|
241
|
+
config("app.name"); // "Keel"
|
|
242
|
+
config("app.port", 3000); // number, with a fallback
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Add a new config file by dropping it in `config/` — `config/mail.ts` becomes
|
|
246
|
+
`config('mail.*')` with no wiring. [Configuration](./configuration.md) has the
|
|
247
|
+
full story.
|
|
248
|
+
|
|
249
|
+
## The console
|
|
250
|
+
|
|
251
|
+
The `keel` console drives the app from the command line. In the starter, run it
|
|
252
|
+
through npm:
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
npm run keel routes # list every registered route
|
|
256
|
+
npm run keel serve --port 8080 # start the server on a chosen port
|
|
257
|
+
npm run keel make:controller Post # -> app/Controllers/PostController.ts
|
|
258
|
+
npm run keel make:provider Billing # -> app/Providers/BillingServiceProvider.ts
|
|
259
|
+
npm run keel make:middleware Auth # -> app/Http/Middleware/authMiddleware.ts
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
The `make:*` generators scaffold from the same stubs the framework uses, so
|
|
263
|
+
generated files are wired to the right folders and import from
|
|
264
|
+
`@shaferllc/keel/core`. `keel routes` is your map — run it whenever you're
|
|
265
|
+
unsure what's mounted. [The Console](./console.md) lists every command.
|
|
266
|
+
|
|
267
|
+
## Where to go next
|
|
268
|
+
|
|
269
|
+
You now have the shape of a Keel app: routes point at controllers, controllers
|
|
270
|
+
render views and read config, and the console scaffolds the pieces. The deep
|
|
271
|
+
guides pick up from here:
|
|
272
|
+
|
|
273
|
+
- [Architecture](./architecture.md) — how boot, the container, and the request
|
|
274
|
+
lifecycle fit together
|
|
275
|
+
- [The Service Container](./container.md) — how dependency injection works
|
|
276
|
+
- [Service Providers](./providers.md) — where you register your own services
|
|
277
|
+
- [Routing](./routing.md) — parameters, names, groups, resources, URLs
|
|
278
|
+
- [Controllers](./controllers.md) — actions, DI, single-action controllers
|
|
279
|
+
- [Views](./views.md) — JSX components, layouts, streaming
|
|
280
|
+
- [Middleware](./middleware.md) — global and per-route request filters
|
|
281
|
+
- [Request & Response](./request-response.md) — the helpers that reach the
|
|
282
|
+
active request
|
|
283
|
+
- [Database](./database.md) and [Models](./models.md) — the query builder and
|
|
284
|
+
the active-record layer on top of it
|
|
285
|
+
- [Configuration](./configuration.md) and [The Console](./console.md) — settings
|
|
286
|
+
and commands
|
|
287
|
+
|
|
288
|
+
When something isn't documented, open the source — the whole framework is a few
|
|
289
|
+
hundred readable lines in `src/core/`, and [Built on Hono](./hono.md) explains
|
|
290
|
+
what you inherit from the layer underneath.
|