@bakery-framework/cli 2.2.2 → 2.4.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/package.json +3 -3
- package/src/pipeline.ts +29 -1
- package/src/worker.ts +16 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bakery-framework/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Bakery entry point and process supervision: mode dispatch, dev watcher, cluster.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bakery",
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
"bun": ">=1.4.0"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@bakery-framework/core": "^2.
|
|
42
|
+
"@bakery-framework/core": "^2.4.0"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"@bakery-framework/orm": "^2.
|
|
45
|
+
"@bakery-framework/orm": "^2.4.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependenciesMeta": {
|
|
48
48
|
"@bakery-framework/orm": {
|
package/src/pipeline.ts
CHANGED
|
@@ -35,12 +35,40 @@ export type RateLimitConfig = Exclude<ProcessedAppConfig['rateLimit'], false>
|
|
|
35
35
|
* 400 is included: `>= 400`, not `> 400`. A handler returning a bare
|
|
36
36
|
* `new Response(…, {status: 404})` must still reach `TSXErrorHandler` and get
|
|
37
37
|
* the app's error page rather than an empty body.
|
|
38
|
+
*
|
|
39
|
+
* A Response whose body the app wrote as a document is not an error to dress,
|
|
40
|
+
* whatever its status: `hasOwnDocument`. The registry keeps only the status
|
|
41
|
+
* of what it replaces, so through 2.2.2 an `onRequest` answering with its own
|
|
42
|
+
* 404 page came back as the app's `error-404.html` (or Bakery's own page),
|
|
43
|
+
* and an API route's `Response.json({ error }, { status: 404 })` as an
|
|
44
|
+
* envelope whose message read `404`.
|
|
38
45
|
*/
|
|
39
46
|
export function isErrorResult(res: unknown): boolean {
|
|
40
|
-
if (res instanceof Response) return res.status >= 400
|
|
47
|
+
if (res instanceof Response) return res.status >= 400 && !hasOwnDocument(res)
|
|
41
48
|
return is.object(res) && 'errorCode' in res
|
|
42
49
|
}
|
|
43
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Whether an error Response carries a document of its own: a body typed as
|
|
53
|
+
* anything but plain text (an HTML page, a JSON document, an uploaded site's
|
|
54
|
+
* `404.html`).
|
|
55
|
+
*
|
|
56
|
+
* What asks to be dressed is untyped or plain text: `response.error()`, a
|
|
57
|
+
* null body, and a string body such as the router's own
|
|
58
|
+
* `new Response('Not Found', { status: 404 })`, which Bun leaves untyped until
|
|
59
|
+
* it is sent. Those still get the app's error page, or the JSON envelope under
|
|
60
|
+
* `/api`.
|
|
61
|
+
*
|
|
62
|
+
* The header alone, never `res.body`: on Bun 1.4.2 reading the body of a Blob
|
|
63
|
+
* or file Response first makes its `content-type` read `null`, so
|
|
64
|
+
* `new Response(Bun.file('404.html'), { status: 404 })` would be judged
|
|
65
|
+
* untyped and dressed.
|
|
66
|
+
*/
|
|
67
|
+
export function hasOwnDocument(res: Response): boolean {
|
|
68
|
+
const type = res.headers.get('content-type')
|
|
69
|
+
return Boolean(type) && !/^text\/plain\b/i.test(type!)
|
|
70
|
+
}
|
|
71
|
+
|
|
44
72
|
/**
|
|
45
73
|
* One bucket a request can be charged to: the top level, or a route rule,
|
|
46
74
|
* checked and ready to match.
|
package/src/worker.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
hostStore,
|
|
5
5
|
} from '@bakery-framework/core/core/bakery'
|
|
6
6
|
import {
|
|
7
|
+
checkHostMounts,
|
|
7
8
|
initConfig,
|
|
8
9
|
resolveHostConfig,
|
|
9
10
|
} from '@bakery-framework/core/core/config'
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
handleRequest,
|
|
16
17
|
handleRequestError,
|
|
17
18
|
processResponse,
|
|
19
|
+
routeOf,
|
|
18
20
|
serveWebSocket,
|
|
19
21
|
} from '@bakery-framework/core/router'
|
|
20
22
|
import { Session } from '@bakery-framework/core/session'
|
|
@@ -247,8 +249,10 @@ function settleRequest(
|
|
|
247
249
|
try {
|
|
248
250
|
// Before anything binds: a rate limit that cannot mean anything (a refill
|
|
249
251
|
// of 0, a prefix without its slash) stops the boot here, naming the field,
|
|
250
|
-
// in the catch below.
|
|
252
|
+
// in the catch below. So does a host mount (a path without its slash, a
|
|
253
|
+
// pattern with '*' inside a label).
|
|
251
254
|
checkRateLimits(Bakery.config)
|
|
255
|
+
checkHostMounts(Bakery.config)
|
|
252
256
|
|
|
253
257
|
Bakery.server = Bun.serve({
|
|
254
258
|
port: PORT,
|
|
@@ -266,6 +270,9 @@ try {
|
|
|
266
270
|
const hostConfig = resolveHostConfig(hostname)
|
|
267
271
|
|
|
268
272
|
return hostStore.run({ config: hostConfig, hostname, req }, async () => {
|
|
273
|
+
// The limiter keys on the path as sent. An error page is looked up
|
|
274
|
+
// from `routeOf(req).path` instead, the path routing resolved, so a
|
|
275
|
+
// page request on a mounted host finds the subtree's error page.
|
|
269
276
|
const path = url.pathname
|
|
270
277
|
req.startNs = Bun.nanoseconds()
|
|
271
278
|
req.__hostname = hostname
|
|
@@ -281,7 +288,7 @@ try {
|
|
|
281
288
|
Bakery.sharedPool.incrementCounter(COUNTER_SLOTS.TOTAL_ERRORS, 1)
|
|
282
289
|
serveLog.SESSION_LOAD_ERR({ error: errorMsg(loadError) })
|
|
283
290
|
return await processResponse(
|
|
284
|
-
await handleRequestError(path, req, loadError),
|
|
291
|
+
await handleRequestError(routeOf(req).path, req, loadError),
|
|
285
292
|
req,
|
|
286
293
|
)
|
|
287
294
|
}
|
|
@@ -303,7 +310,12 @@ try {
|
|
|
303
310
|
|
|
304
311
|
if (isErrorResult(res)) {
|
|
305
312
|
Bakery.sharedPool.incrementCounter(COUNTER_SLOTS.TOTAL_ERRORS, 1)
|
|
306
|
-
return await handleRequestError(path, req, res)
|
|
313
|
+
return await handleRequestError(routeOf(req).path, req, res)
|
|
314
|
+
}
|
|
315
|
+
// The app's own error page (see `hasOwnDocument`): sent as written,
|
|
316
|
+
// and still an error answer for the counter.
|
|
317
|
+
if (res instanceof Response && res.status >= 400) {
|
|
318
|
+
Bakery.sharedPool.incrementCounter(COUNTER_SLOTS.TOTAL_ERRORS, 1)
|
|
307
319
|
}
|
|
308
320
|
return res
|
|
309
321
|
},
|
|
@@ -311,7 +323,7 @@ try {
|
|
|
311
323
|
async function errorHandler(error) {
|
|
312
324
|
Bakery.sharedPool.incrementCounter(COUNTER_SLOTS.TOTAL_ERRORS, 1)
|
|
313
325
|
serveLog.UNHANDLED_ERR({ error: errorMsg(error) })
|
|
314
|
-
return await handleRequestError(path, req, error)
|
|
326
|
+
return await handleRequestError(routeOf(req).path, req, error)
|
|
315
327
|
},
|
|
316
328
|
)
|
|
317
329
|
|