@shaferllc/keel 0.82.0 → 0.83.1

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 (150) hide show
  1. package/README.md +2 -2
  2. package/dist/billing/billable.d.ts +2 -3
  3. package/dist/billing/billable.js +2 -3
  4. package/dist/billing/billing.config.stub +1 -1
  5. package/dist/billing/builder.d.ts +2 -2
  6. package/dist/billing/builder.js +2 -2
  7. package/dist/billing/provider.d.ts +1 -1
  8. package/dist/billing/provider.js +2 -2
  9. package/dist/billing/subscription-item.d.ts +0 -1
  10. package/dist/billing/subscription-item.js +0 -1
  11. package/dist/core/decorators.d.ts +5 -7
  12. package/dist/core/decorators.js +5 -7
  13. package/dist/core/exceptions.d.ts +2 -3
  14. package/dist/core/exceptions.js +2 -3
  15. package/dist/core/model-query.d.ts +1 -1
  16. package/dist/core/model-query.js +1 -1
  17. package/dist/core/request-logger.d.ts +2 -2
  18. package/dist/core/request-logger.js +2 -2
  19. package/dist/core/request.js +1 -1
  20. package/dist/core/testing.d.ts +3 -3
  21. package/dist/core/testing.js +3 -3
  22. package/dist/db/d1-http.d.ts +34 -0
  23. package/dist/db/d1-http.js +61 -0
  24. package/dist/db/libsql.d.ts +13 -3
  25. package/dist/teams/models.js +51 -5
  26. package/docs/ai-manifest.json +23 -2
  27. package/docs/billing.md +5 -6
  28. package/docs/changelog.md +106 -21
  29. package/docs/database.md +5 -522
  30. package/docs/decorators.md +2 -6
  31. package/docs/errors.md +1 -1
  32. package/docs/hooks.md +1 -3
  33. package/docs/logger.md +2 -2
  34. package/docs/models.md +5 -2
  35. package/docs/orm.md +57 -0
  36. package/docs/packages.md +3 -3
  37. package/docs/providers.md +6 -6
  38. package/docs/query-builder.md +533 -0
  39. package/docs/request-response.md +2 -2
  40. package/docs/starter-kits.md +88 -0
  41. package/docs/testing.md +1 -2
  42. package/docs/validation.md +1 -2
  43. package/llms-full.txt +5301 -5126
  44. package/llms.txt +4 -1
  45. package/package.json +7 -2
  46. package/templates/api/.env.example +12 -0
  47. package/templates/api/app/Controllers/PostController.ts +37 -0
  48. package/templates/api/app/Http/Kernel.ts +13 -0
  49. package/templates/api/app/Http/Middleware/requestLogger.ts +8 -0
  50. package/templates/api/app/Models/Post.ts +12 -0
  51. package/templates/api/app/Providers/AppServiceProvider.ts +8 -0
  52. package/templates/api/app/Providers/DatabaseServiceProvider.ts +52 -0
  53. package/templates/api/bin/keel.ts +15 -0
  54. package/templates/api/bootstrap/app.ts +24 -0
  55. package/templates/api/bootstrap/providers.edge.ts +14 -0
  56. package/templates/api/bootstrap/providers.ts +7 -0
  57. package/templates/api/config/app.ts +10 -0
  58. package/templates/api/config/database.ts +42 -0
  59. package/templates/api/database/migrations/0001_create_posts.ts +20 -0
  60. package/templates/api/package.json +30 -0
  61. package/templates/api/routes/web.ts +12 -0
  62. package/templates/api/tests/posts.test.ts +30 -0
  63. package/templates/api/tsconfig.json +16 -0
  64. package/templates/api/worker.ts +33 -0
  65. package/templates/api/wrangler.jsonc +22 -0
  66. package/templates/app/.env.example +12 -0
  67. package/templates/app/app/Controllers/AuthController.ts +117 -0
  68. package/templates/app/app/Controllers/DashboardController.ts +37 -0
  69. package/templates/app/app/Controllers/HomeController.ts +10 -0
  70. package/templates/app/app/Http/Kernel.ts +21 -0
  71. package/templates/app/app/Http/Middleware/requestLogger.ts +8 -0
  72. package/templates/app/app/Models/User.ts +15 -0
  73. package/templates/app/app/Providers/AppServiceProvider.ts +12 -0
  74. package/templates/app/app/Providers/DatabaseServiceProvider.ts +52 -0
  75. package/templates/app/bin/keel.ts +15 -0
  76. package/templates/app/bootstrap/app.ts +24 -0
  77. package/templates/app/bootstrap/providers.edge.ts +15 -0
  78. package/templates/app/bootstrap/providers.ts +18 -0
  79. package/templates/app/config/app.ts +10 -0
  80. package/templates/app/config/database.ts +42 -0
  81. package/templates/app/database/migrations/0001_create_users.ts +21 -0
  82. package/templates/app/package.json +35 -0
  83. package/templates/app/public/.gitkeep +2 -0
  84. package/templates/app/resources/css/app.css +1 -0
  85. package/templates/app/resources/views/auth/forgot.tsx +30 -0
  86. package/templates/app/resources/views/auth/login.tsx +24 -0
  87. package/templates/app/resources/views/auth/register.tsx +24 -0
  88. package/templates/app/resources/views/auth/two-factor.tsx +22 -0
  89. package/templates/app/resources/views/dashboard.tsx +20 -0
  90. package/templates/app/resources/views/layout.tsx +15 -0
  91. package/templates/app/resources/views/welcome.tsx +29 -0
  92. package/templates/app/routes/web.ts +35 -0
  93. package/templates/app/tests/auth.test.ts +47 -0
  94. package/templates/app/tsconfig.json +31 -0
  95. package/templates/app/worker.ts +33 -0
  96. package/templates/app/wrangler.jsonc +25 -0
  97. package/templates/minimal/.env.example +8 -0
  98. package/templates/minimal/app/Controllers/HomeController.ts +14 -0
  99. package/templates/minimal/app/Http/Kernel.ts +22 -0
  100. package/templates/minimal/app/Http/Middleware/requestLogger.ts +8 -0
  101. package/templates/minimal/app/Providers/AppServiceProvider.ts +8 -0
  102. package/templates/minimal/bin/keel.ts +15 -0
  103. package/templates/minimal/bootstrap/app.ts +24 -0
  104. package/templates/minimal/bootstrap/providers.ts +6 -0
  105. package/templates/minimal/config/app.ts +10 -0
  106. package/templates/minimal/package.json +29 -0
  107. package/templates/minimal/public/.gitkeep +2 -0
  108. package/templates/minimal/resources/css/app.css +1 -0
  109. package/templates/minimal/resources/views/layout.tsx +15 -0
  110. package/templates/minimal/resources/views/welcome.tsx +15 -0
  111. package/templates/minimal/routes/web.ts +13 -0
  112. package/templates/minimal/tsconfig.json +18 -0
  113. package/templates/minimal/worker.ts +23 -0
  114. package/templates/minimal/wrangler.jsonc +10 -0
  115. package/templates/saas/.env.example +12 -0
  116. package/templates/saas/app/Controllers/AuthController.ts +125 -0
  117. package/templates/saas/app/Controllers/DashboardController.ts +37 -0
  118. package/templates/saas/app/Controllers/HomeController.ts +10 -0
  119. package/templates/saas/app/Controllers/TeamController.ts +88 -0
  120. package/templates/saas/app/Http/Kernel.ts +27 -0
  121. package/templates/saas/app/Http/Middleware/requestLogger.ts +8 -0
  122. package/templates/saas/app/Models/Project.ts +20 -0
  123. package/templates/saas/app/Models/User.ts +15 -0
  124. package/templates/saas/app/Providers/AppServiceProvider.ts +12 -0
  125. package/templates/saas/app/Providers/DatabaseServiceProvider.ts +52 -0
  126. package/templates/saas/bin/keel.ts +15 -0
  127. package/templates/saas/bootstrap/app.ts +24 -0
  128. package/templates/saas/bootstrap/providers.edge.ts +18 -0
  129. package/templates/saas/bootstrap/providers.ts +22 -0
  130. package/templates/saas/config/app.ts +10 -0
  131. package/templates/saas/config/database.ts +42 -0
  132. package/templates/saas/database/migrations/0001_create_users.ts +21 -0
  133. package/templates/saas/database/migrations/0002_create_projects.ts +20 -0
  134. package/templates/saas/package.json +35 -0
  135. package/templates/saas/public/.gitkeep +2 -0
  136. package/templates/saas/resources/css/app.css +1 -0
  137. package/templates/saas/resources/views/auth/forgot.tsx +30 -0
  138. package/templates/saas/resources/views/auth/login.tsx +24 -0
  139. package/templates/saas/resources/views/auth/register.tsx +24 -0
  140. package/templates/saas/resources/views/auth/two-factor.tsx +22 -0
  141. package/templates/saas/resources/views/dashboard.tsx +20 -0
  142. package/templates/saas/resources/views/layout.tsx +15 -0
  143. package/templates/saas/resources/views/teams/index.tsx +85 -0
  144. package/templates/saas/resources/views/welcome.tsx +29 -0
  145. package/templates/saas/routes/web.ts +44 -0
  146. package/templates/saas/tests/auth.test.ts +47 -0
  147. package/templates/saas/tests/teams.test.ts +27 -0
  148. package/templates/saas/tsconfig.json +31 -0
  149. package/templates/saas/worker.ts +33 -0
  150. package/templates/saas/wrangler.jsonc +25 -0
package/docs/changelog.md CHANGED
@@ -4,6 +4,92 @@ All notable changes to Keel are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project aims to
5
5
  adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.83.0] — 2026-07-12
8
+
9
+ ### Added
10
+
11
+ - **Starter kits, and a generator that can't fall behind.** Four curated
12
+ applications — `minimal`, `api`, `app` (views, sessions, register/login, password
13
+ reset, two-factor), and `saas` (teams, roles, invitations, billing,
14
+ multi-tenancy):
15
+
16
+ ```bash
17
+ npm create keeljs@latest my-app -- --preset saas
18
+ ```
19
+
20
+ The templates ship **inside this package**, so the version a kit is generated from
21
+ is, by construction, the version it was written for. The old standalone starter
22
+ drifted to five releases behind and nothing noticed; CI now generates all four kits
23
+ on every push and typechecks, migrates, boots, serves a request, bundles the
24
+ Worker, and runs their tests.
25
+
26
+ - **Teams (`@shaferllc/keel/teams`)** — multi-tenancy, membership, roles, and
27
+ invitations. `TenantModel` makes isolation the default rather than a habit: reads
28
+ are constrained by an inherited global scope (so even `find(id)` returns `null` for
29
+ another team's row — not a filter you can forget), and writes are stamped with the
30
+ current team, so a row can't be born ownerless.
31
+
32
+ Outside a team context a tenant query **throws**. A job, console command, or
33
+ webhook has no request and therefore no team; returning everything is how one
34
+ customer's data reaches another, and `team_id = NULL` means jobs quietly do
35
+ nothing. So work says which team it is for — `runForTeam(team, fn)` — or says out
36
+ loud that it spans all of them — `withoutTenant(fn)`. Both are greppable at audit
37
+ time.
38
+
39
+ - **Accounts (`@shaferllc/keel/accounts`)** — password reset, email verification, and
40
+ two-factor, mounted with one provider. A correct password on a 2FA account yields a
41
+ short-lived, single-purpose **challenge, not a session**, so there is no
42
+ half-authenticated state for a route to forget to check. TOTP is RFC 6238, verified
43
+ against the RFC's published vectors, WebCrypto-only, and therefore edge-safe. No
44
+ tokens table: reset links carry their own purpose and expiry and are bound to a
45
+ fingerprint of the current password hash, so spending one kills it.
46
+
47
+ - **D1 over HTTP (`@shaferllc/keel/db/d1-http`).** The D1 binding only exists inside a
48
+ Worker, so `keel migrate` had nowhere to point and you could not create your
49
+ tables. The same `Connection` over D1's HTTP API closes that: migrations run from a
50
+ laptop and from CI. It treats an error in the response *body* as an error even when
51
+ the status is 200 — which is how Cloudflare often reports them, and trusting the
52
+ status would let a failed migration look like it succeeded.
53
+
54
+ - **`Model.withoutGlobalScope(...)` / `withoutGlobalScopes()`** — escaping a scope
55
+ should be typed out and greppable, never something you arrive at by forgetting a
56
+ `where`.
57
+
58
+ ### Fixed
59
+
60
+ - **Global scopes and model hooks now inherit.** Both were keyed by the exact class,
61
+ so a scope or a `creating` hook declared on a *base* class was silently ignored by
62
+ every subclass. The models guide advertises global scopes as "the base for
63
+ multi-tenancy", and that was precisely the case that didn't work: the scope did
64
+ nothing, the query returned every tenant's rows, and nothing complained. It failed
65
+ **open**. Both now walk the prototype chain, and a scope is passed the model it is
66
+ scoping so a base class can read each subclass's own configuration.
67
+
68
+ - **The official `@libsql/client` no longer needs a cast.** Under
69
+ `strictFunctionTypes` the narrower parameter types made the real `Client`
70
+ unassignable to `LibSqlLike`, so wiring libSQL the obvious way required
71
+ `client as unknown as LibSqlLike` — a cast even Keel's own tests carried.
72
+
73
+ - **`createTeam()` could not give two people with the same name a team.** It slugged
74
+ the name into a `UNIQUE` column, so the second "Ada" to sign up got a 500. Looking
75
+ for a free slug first is check-then-act and loses the race anyway; the unique index
76
+ is the only real arbiter, so it now arbitrates and the insert retries.
77
+
78
+ ## [0.82.0] — 2026-07-12
79
+
80
+ ### Added
81
+
82
+ - **Query builder: the methods people actually reach for.** `join`/`leftJoin`,
83
+ `groupBy`/`having`/`distinct`, `whereColumn`/`whereRaw`/`orderByRaw`, `when`,
84
+ `increment`/`decrement`, `upsert`/`insertOrIgnore`, and `chunk`, with docs.
85
+
86
+ ## [0.81.2] — 2026-07-12
87
+
88
+ ### Documentation
89
+
90
+ - **The changelog is published to the docs site** (`docs/changelog.md`), so releases
91
+ are readable at keeljs.com rather than only in the repository.
92
+
7
93
  ## [0.81.1] — 2026-07-12
8
94
 
9
95
  ### Documentation
@@ -25,8 +111,8 @@ adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
25
111
 
26
112
  ### Added
27
113
 
28
- - **ORM: Eloquent-parity features.** The active-record `Model` grows most of the
29
- Eloquent surface people reach for, all backward-compatible and still on the
114
+ - **ORM: the features people reach for.** The active-record `Model` grows the
115
+ surface a real ORM needs, all backward-compatible and still on the
30
116
  driver-agnostic query builder (no JOINs, edge-safe):
31
117
 
32
118
  - **Lifecycle events & observers** — `creating`/`created`, `updating`/
@@ -91,8 +177,8 @@ adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
91
177
 
92
178
  ### Added
93
179
 
94
- - **Billing** — a new `@shaferllc/keel/billing` package: a Cashier-style
95
- subscription layer with **one gateway-neutral API over Stripe and Paddle**
180
+ - **Billing** — a new `@shaferllc/keel/billing` package: a subscription
181
+ layer with **one gateway-neutral API over Stripe and Paddle**
96
182
  (switching gateways is a config change). `class User extends Billable(Model)`
97
183
  gives a gateway customer, subscriptions (create/swap/quantity/trials/cancel/
98
184
  resume + status checks), single charges + refunds, invoices, hosted checkout,
@@ -286,7 +372,7 @@ adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
286
372
 
287
373
  ### Changed
288
374
 
289
- - **Removed the Remult references** from the API-resources guide and source
375
+ - **Removed comparisons to other frameworks** from the API-resources guide and source
290
376
  comments. Keel isn't that, and the docs shouldn't read as a comparison to another
291
377
  framework. The surrounding sentences are rewritten so they still say what the
292
378
  feature does rather than leaving a hole. No behavior change.
@@ -960,7 +1046,7 @@ adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
960
1046
  connection. They honor `X-Forwarded-Proto` / `X-Forwarded-Host` over the raw
961
1047
  URL, so an app behind a TLS-terminating proxy or load balancer sees the
962
1048
  client's real scheme and host — use `origin` to build absolute links and
963
- `secure` to gate insecure requests. (Koa-inspired.)
1049
+ `secure` to gate insecure requests.
964
1050
  - **`response.back(fallback?)` and `redirect("back")`.** Redirect to the
965
1051
  request's `Referer`, falling back to `fallback` (default `"/"`) when it's
966
1052
  absent — the "return where you came from" shortcut for post-form flows.
@@ -1189,7 +1275,7 @@ adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
1189
1275
  - **Route config / metadata.** Attach arbitrary data to a route or group with
1190
1276
  `.config({ … })` and read it in the handler or route middleware via
1191
1277
  `request.route.config` — per-route flags like an auth scope, rate tier, or
1192
- layout choice (Fastify's route `config`). Group config merges into every route,
1278
+ layout choice. Group config merges into every route,
1193
1279
  with a route's own keys winning. The matched-route context is now set *before*
1194
1280
  a route's middleware, so route/group middleware can branch on `request.route`.
1195
1281
  See [docs/routing.md](./docs/routing.md#route-config).
@@ -1236,7 +1322,7 @@ adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
1236
1322
  - **Response header helpers.** The `response` accessor gains `headers({...})` (set
1237
1323
  several at once), `getHeader(name)`, and `hasHeader(name)` — so middleware can
1238
1324
  inspect and conditionally set what a handler already put on the response (e.g.
1239
- a default `cache-control`). Brings `response` to parity with Fastify's Reply.
1325
+ a default `cache-control`).
1240
1326
  See [docs/request-response.md](./docs/request-response.md).
1241
1327
  - **Design principles** documented in
1242
1328
  [docs/architecture.md](./docs/architecture.md#design-principles) — edge-safe /
@@ -1253,9 +1339,9 @@ adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
1253
1339
  options at registration: `app.register(RateLimitProvider, { max: 100 })`, typed
1254
1340
  via `ServiceProvider<{ max: number }>` and read as `this.options`. The same
1255
1341
  provider class can register more than once with different options, so a provider
1256
- is now genuinely reusable (Fastify's `register(plugin, options)`). Backward
1257
- compatible — options default to `{}`. (Keel providers stay un-encapsulated by
1258
- design; per-request scoping is [middleware](./docs/middleware.md).) See
1342
+ is now genuinely reusable. Backward compatible options default to `{}`.
1343
+ (Keel providers stay un-encapsulated by design; per-request scoping is
1344
+ [middleware](./docs/middleware.md).) See
1259
1345
  [docs/providers.md](./docs/providers.md#providers-are-keels-plugin-system).
1260
1346
 
1261
1347
  [0.50.0]: https://github.com/shaferllc/keel/releases/tag/v0.50.0
@@ -1350,8 +1436,8 @@ adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
1350
1436
  a bad request with a `422` `ValidationException` (errors from every part
1351
1437
  aggregated, keyed `body.field` / `query.field` / `params.field`) so the handler
1352
1438
  only ever sees valid input. `validated(part)` returns the parsed, typed value.
1353
- The declarative counterpart to Fastify's route schemas, built on the same
1354
- schema-agnostic `validate()` engine (bring your own Zod-style schema). See
1439
+ Built on the same schema-agnostic `validate()` engine (bring your own
1440
+ Zod-style schema). See
1355
1441
  [docs/validation.md](./docs/validation.md#declarative-validation-before-the-handler).
1356
1442
 
1357
1443
  [0.44.0]: https://github.com/shaferllc/keel/releases/tag/v0.44.0
@@ -1362,7 +1448,7 @@ adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
1362
1448
 
1363
1449
  - **Per-request logging.** `requestLogger()` middleware binds a child logger with
1364
1450
  a generated `reqId` to each request, so every log line within a request
1365
- correlates (Fastify's `request.log`). It logs request start/completion
1451
+ correlates. It logs request start/completion
1366
1452
  (method, path, status, ms) by default; options for `genReqId`, reusing an
1367
1453
  incoming `idHeader` (distributed tracing), and disabling the auto lines.
1368
1454
  `requestLog()` reaches the current request's logger anywhere (falls back to the
@@ -1400,8 +1486,8 @@ adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
1400
1486
  through the default path (with `code` in the JSON body) and passes
1401
1487
  `instanceof HttpException`. The built-in exceptions now carry stable codes too
1402
1488
  (`E_NOT_FOUND`, `E_UNAUTHORIZED`, `E_FORBIDDEN`, `E_VALIDATION`), so `code`
1403
- surfaces without any work. Inspired by Fastify's `@fastify/error`. Also
1404
- documented: serving over **HTTP/2** needs no framework code — it's a transport
1489
+ surfaces without any work. Also documented: serving over **HTTP/2** needs no
1490
+ framework code — it's a transport
1405
1491
  concern handled by the edge platform, a reverse proxy, or a `@hono/node-server`
1406
1492
  `node:http2` option. See [docs/errors.md](./docs/errors.md#coded-errors-with-createerror)
1407
1493
  and [docs/hono.md](./docs/hono.md#serving-over-http2).
@@ -1435,8 +1521,8 @@ adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
1435
1521
  - **Raw request-body accessors.** `request.text()`, `request.arrayBuffer()`, and
1436
1522
  `request.blob()` read the body for content types `json()` / `all()` don't
1437
1523
  handle — XML, CSV, protobuf, msgpack, or any custom format — which you then
1438
- parse yourself. Keel keeps body parsing explicit (no Fastify-style content-type
1439
- parser registry): you call the accessor you want. See
1524
+ parse yourself. Keel keeps body parsing explicit (no content-type parser
1525
+ registry): you call the accessor you want. See
1440
1526
  [docs/request-response.md](./docs/request-response.md#other-content-types).
1441
1527
 
1442
1528
  [0.40.1]: https://github.com/shaferllc/keel/releases/tag/v0.40.1
@@ -1450,9 +1536,8 @@ adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
1450
1536
  life of the request. `decorateRequest(name, resolver)` registers a resolver
1451
1537
  (sync or async), `decorated(name)` reads it (computed once, then cached),
1452
1538
  `setRequestValue(name, value)` sets it imperatively (e.g. from middleware), and
1453
- `hasRequestDecorator(name)` checks. Inspired by Fastify's `decorateRequest`,
1454
- but without the null-placeholder/`onRequest`-hook dance the per-request memo
1455
- is keyed off the context via a WeakMap, so nothing leaks between requests.
1539
+ `hasRequestDecorator(name)` checks. The per-request memo is keyed off the
1540
+ context via a WeakMap, so nothing leaks between requests.
1456
1541
  (Decorating the *app* is already the container's job.) See
1457
1542
  [docs/decorators.md](./docs/decorators.md).
1458
1543