@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.
- package/README.md +2 -2
- package/dist/billing/billable.d.ts +2 -3
- package/dist/billing/billable.js +2 -3
- package/dist/billing/billing.config.stub +1 -1
- package/dist/billing/builder.d.ts +2 -2
- package/dist/billing/builder.js +2 -2
- package/dist/billing/provider.d.ts +1 -1
- package/dist/billing/provider.js +2 -2
- package/dist/billing/subscription-item.d.ts +0 -1
- package/dist/billing/subscription-item.js +0 -1
- package/dist/core/decorators.d.ts +5 -7
- package/dist/core/decorators.js +5 -7
- package/dist/core/exceptions.d.ts +2 -3
- package/dist/core/exceptions.js +2 -3
- package/dist/core/model-query.d.ts +1 -1
- package/dist/core/model-query.js +1 -1
- package/dist/core/request-logger.d.ts +2 -2
- package/dist/core/request-logger.js +2 -2
- package/dist/core/request.js +1 -1
- package/dist/core/testing.d.ts +3 -3
- package/dist/core/testing.js +3 -3
- package/dist/db/d1-http.d.ts +34 -0
- package/dist/db/d1-http.js +61 -0
- package/dist/db/libsql.d.ts +13 -3
- package/dist/teams/models.js +51 -5
- package/docs/ai-manifest.json +23 -2
- package/docs/billing.md +5 -6
- package/docs/changelog.md +106 -21
- package/docs/database.md +5 -522
- package/docs/decorators.md +2 -6
- package/docs/errors.md +1 -1
- package/docs/hooks.md +1 -3
- package/docs/logger.md +2 -2
- package/docs/models.md +5 -2
- package/docs/orm.md +57 -0
- package/docs/packages.md +3 -3
- package/docs/providers.md +6 -6
- package/docs/query-builder.md +533 -0
- package/docs/request-response.md +2 -2
- package/docs/starter-kits.md +88 -0
- package/docs/testing.md +1 -2
- package/docs/validation.md +1 -2
- package/llms-full.txt +5301 -5126
- package/llms.txt +4 -1
- package/package.json +7 -2
- package/templates/api/.env.example +12 -0
- package/templates/api/app/Controllers/PostController.ts +37 -0
- package/templates/api/app/Http/Kernel.ts +13 -0
- package/templates/api/app/Http/Middleware/requestLogger.ts +8 -0
- package/templates/api/app/Models/Post.ts +12 -0
- package/templates/api/app/Providers/AppServiceProvider.ts +8 -0
- package/templates/api/app/Providers/DatabaseServiceProvider.ts +52 -0
- package/templates/api/bin/keel.ts +15 -0
- package/templates/api/bootstrap/app.ts +24 -0
- package/templates/api/bootstrap/providers.edge.ts +14 -0
- package/templates/api/bootstrap/providers.ts +7 -0
- package/templates/api/config/app.ts +10 -0
- package/templates/api/config/database.ts +42 -0
- package/templates/api/database/migrations/0001_create_posts.ts +20 -0
- package/templates/api/package.json +30 -0
- package/templates/api/routes/web.ts +12 -0
- package/templates/api/tests/posts.test.ts +30 -0
- package/templates/api/tsconfig.json +16 -0
- package/templates/api/worker.ts +33 -0
- package/templates/api/wrangler.jsonc +22 -0
- package/templates/app/.env.example +12 -0
- package/templates/app/app/Controllers/AuthController.ts +117 -0
- package/templates/app/app/Controllers/DashboardController.ts +37 -0
- package/templates/app/app/Controllers/HomeController.ts +10 -0
- package/templates/app/app/Http/Kernel.ts +21 -0
- package/templates/app/app/Http/Middleware/requestLogger.ts +8 -0
- package/templates/app/app/Models/User.ts +15 -0
- package/templates/app/app/Providers/AppServiceProvider.ts +12 -0
- package/templates/app/app/Providers/DatabaseServiceProvider.ts +52 -0
- package/templates/app/bin/keel.ts +15 -0
- package/templates/app/bootstrap/app.ts +24 -0
- package/templates/app/bootstrap/providers.edge.ts +15 -0
- package/templates/app/bootstrap/providers.ts +18 -0
- package/templates/app/config/app.ts +10 -0
- package/templates/app/config/database.ts +42 -0
- package/templates/app/database/migrations/0001_create_users.ts +21 -0
- package/templates/app/package.json +35 -0
- package/templates/app/public/.gitkeep +2 -0
- package/templates/app/resources/css/app.css +1 -0
- package/templates/app/resources/views/auth/forgot.tsx +30 -0
- package/templates/app/resources/views/auth/login.tsx +24 -0
- package/templates/app/resources/views/auth/register.tsx +24 -0
- package/templates/app/resources/views/auth/two-factor.tsx +22 -0
- package/templates/app/resources/views/dashboard.tsx +20 -0
- package/templates/app/resources/views/layout.tsx +15 -0
- package/templates/app/resources/views/welcome.tsx +29 -0
- package/templates/app/routes/web.ts +35 -0
- package/templates/app/tests/auth.test.ts +47 -0
- package/templates/app/tsconfig.json +31 -0
- package/templates/app/worker.ts +33 -0
- package/templates/app/wrangler.jsonc +25 -0
- package/templates/minimal/.env.example +8 -0
- package/templates/minimal/app/Controllers/HomeController.ts +14 -0
- package/templates/minimal/app/Http/Kernel.ts +22 -0
- package/templates/minimal/app/Http/Middleware/requestLogger.ts +8 -0
- package/templates/minimal/app/Providers/AppServiceProvider.ts +8 -0
- package/templates/minimal/bin/keel.ts +15 -0
- package/templates/minimal/bootstrap/app.ts +24 -0
- package/templates/minimal/bootstrap/providers.ts +6 -0
- package/templates/minimal/config/app.ts +10 -0
- package/templates/minimal/package.json +29 -0
- package/templates/minimal/public/.gitkeep +2 -0
- package/templates/minimal/resources/css/app.css +1 -0
- package/templates/minimal/resources/views/layout.tsx +15 -0
- package/templates/minimal/resources/views/welcome.tsx +15 -0
- package/templates/minimal/routes/web.ts +13 -0
- package/templates/minimal/tsconfig.json +18 -0
- package/templates/minimal/worker.ts +23 -0
- package/templates/minimal/wrangler.jsonc +10 -0
- package/templates/saas/.env.example +12 -0
- package/templates/saas/app/Controllers/AuthController.ts +125 -0
- package/templates/saas/app/Controllers/DashboardController.ts +37 -0
- package/templates/saas/app/Controllers/HomeController.ts +10 -0
- package/templates/saas/app/Controllers/TeamController.ts +88 -0
- package/templates/saas/app/Http/Kernel.ts +27 -0
- package/templates/saas/app/Http/Middleware/requestLogger.ts +8 -0
- package/templates/saas/app/Models/Project.ts +20 -0
- package/templates/saas/app/Models/User.ts +15 -0
- package/templates/saas/app/Providers/AppServiceProvider.ts +12 -0
- package/templates/saas/app/Providers/DatabaseServiceProvider.ts +52 -0
- package/templates/saas/bin/keel.ts +15 -0
- package/templates/saas/bootstrap/app.ts +24 -0
- package/templates/saas/bootstrap/providers.edge.ts +18 -0
- package/templates/saas/bootstrap/providers.ts +22 -0
- package/templates/saas/config/app.ts +10 -0
- package/templates/saas/config/database.ts +42 -0
- package/templates/saas/database/migrations/0001_create_users.ts +21 -0
- package/templates/saas/database/migrations/0002_create_projects.ts +20 -0
- package/templates/saas/package.json +35 -0
- package/templates/saas/public/.gitkeep +2 -0
- package/templates/saas/resources/css/app.css +1 -0
- package/templates/saas/resources/views/auth/forgot.tsx +30 -0
- package/templates/saas/resources/views/auth/login.tsx +24 -0
- package/templates/saas/resources/views/auth/register.tsx +24 -0
- package/templates/saas/resources/views/auth/two-factor.tsx +22 -0
- package/templates/saas/resources/views/dashboard.tsx +20 -0
- package/templates/saas/resources/views/layout.tsx +15 -0
- package/templates/saas/resources/views/teams/index.tsx +85 -0
- package/templates/saas/resources/views/welcome.tsx +29 -0
- package/templates/saas/routes/web.ts +44 -0
- package/templates/saas/tests/auth.test.ts +47 -0
- package/templates/saas/tests/teams.test.ts +27 -0
- package/templates/saas/tsconfig.json +31 -0
- package/templates/saas/worker.ts +33 -0
- 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:
|
|
29
|
-
|
|
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
|
|
95
|
-
|
|
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
|
|
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.
|
|
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
|
|
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`).
|
|
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
|
|
1257
|
-
|
|
1258
|
-
|
|
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
|
-
|
|
1354
|
-
|
|
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
|
|
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.
|
|
1404
|
-
|
|
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
|
|
1439
|
-
|
|
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.
|
|
1454
|
-
|
|
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
|
|