Skip to content

Commit 4648a0c

Browse files
committed
feat(docs): document render caps and staticHandler option
- Add maxRenderIterations and maxOutputSize router options - Add the staticHandler advanced option for custom backends - Update template-limit responses from 500 to 400
1 parent a49af84 commit 4648a0c

6 files changed

Lines changed: 151 additions & 21 deletions

File tree

docs/getting-started/routes-configuration.md

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Configure the Deserve routes directory to match the project structure.
88

99
## Router Options
1010

11-
The `Router` constructor accepts configuration options. The common ones are `routesDir` for the route folder and `requestTimeoutMs` for a request timeout. Rendering, request limits, worker pools, and a custom error builder are all configurable too. Proxy trust through `trustProxy` and the worker pool live in [Client IP Resolution](/getting-started/server-configuration#client-ip-resolution) and [Worker Pool](/core-concepts/worker-pool).
11+
The `Router` constructor accepts one options object. The everyday pair is `routesDir` for the route folder and `requestTimeoutMs` for a request deadline. The sections below cover route loading, request size limits, template render limits, and the two advanced hooks `errorResponseBuilder` and `staticHandler`. Two related options live on their own pages, `trustProxy` under [Client IP Resolution](/getting-started/server-configuration#client-ip-resolution) and the `worker` pool under [Worker Pool](/core-concepts/worker-pool).
1212

1313
```typescript twoslash
1414
import { Router } from '@neabyte/deserve'
@@ -53,7 +53,7 @@ const router = new Router({
5353

5454
### `maxIterations`
5555

56-
Maximum iterations allowed per <code v-pre>{{#each}}</code> block in DVE templates. The cap prevents event loop starvation from unbounded rendering. The default is `100_000`, and exceeding it makes the engine throw so the server responds with **500 Internal Server Error**.
56+
Maximum iterations allowed per <code v-pre>{{#each}}</code> block in DVE templates. The cap prevents event loop starvation from one unbounded loop. The default is `100_000`, and exceeding it makes the engine throw so the server responds with **400 Bad Request**.
5757

5858
```typescript twoslash
5959
import { Router } from '@neabyte/deserve'
@@ -67,6 +67,34 @@ const router = new Router({
6767

6868
For datasets larger than the limit, use [`streamRender`](/rendering/streaming) instead, and see [Performance and Limits](/rendering/performance#iteration-limit) for how the cap behaves. For CPU-intensive rendering, consider offloading to a [worker pool](/core-concepts/worker-pool).
6969

70+
### `maxRenderIterations`
71+
72+
Maximum total <code v-pre>{{#each}}</code> body executions across one render, summed over every loop including nested ones. Where `maxIterations` guards a single loop, this guards the whole page. The default is `1_000_000`, and exceeding it responds with **400 Bad Request**.
73+
74+
```typescript twoslash
75+
import { Router } from '@neabyte/deserve'
76+
// ---cut---
77+
const router = new Router({
78+
routesDir: 'routes',
79+
viewsDir: './views',
80+
maxRenderIterations: 500_000
81+
})
82+
```
83+
84+
### `maxOutputSize`
85+
86+
Maximum total output characters produced by one render. The cap stops a small template from expanding into a huge response. The default is `5_000_000`, and exceeding it responds with **400 Bad Request**.
87+
88+
```typescript twoslash
89+
import { Router } from '@neabyte/deserve'
90+
// ---cut---
91+
const router = new Router({
92+
routesDir: 'routes',
93+
viewsDir: './views',
94+
maxOutputSize: 1_000_000
95+
})
96+
```
97+
7098
### `maxUrlLength`
7199

72100
Maximum length of the request URL in characters. A longer URL is rejected with **414 URI Too Long** before any route runs. The default is `8192`:
@@ -123,6 +151,27 @@ const router = new Router({
123151
})
124152
```
125153

154+
### `staticHandler`
155+
156+
Advanced option that replaces how static files are served. It receives the context, the [static options](/static-file/basic#static-file-options) for the matched route, and the URL path, then returns the `Response`. The default implementation already guards path traversal, so override it only for a custom backend such as object storage:
157+
158+
```typescript twoslash
159+
import type { Context, ServeOptions } from '@neabyte/deserve'
160+
import { Router } from '@neabyte/deserve'
161+
// ---cut---
162+
const router = new Router({
163+
routesDir: 'routes',
164+
staticHandler: {
165+
// Serve files from a custom backend
166+
async serve(ctx: Context, options: ServeOptions, urlPath: string) {
167+
return ctx.send.text(`requested ${urlPath}`)
168+
}
169+
}
170+
})
171+
```
172+
173+
Register the static route itself with [`router.static()`](/static-file/basic), which this handler then fulfills.
174+
126175
## Supported File Extensions
127176

128177
Deserve automatically detects and supports these file extensions:

docs/getting-started/server-configuration.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ Omit `requestTimeoutMs` for no timeout (default).
8484

8585
## Template Iteration Limit
8686

87-
The `maxIterations` option caps the iterations per <code v-pre>{{#each}}</code> block in DVE templates, which prevents event loop starvation from unbounded rendering. The default is `100_000`:
87+
The `maxIterations` option caps the iterations per <code v-pre>{{#each}}</code> block in DVE templates, which prevents event loop starvation from one unbounded loop. The default is `100_000`:
8888

8989
```typescript twoslash
9090
import { Router } from '@neabyte/deserve'
@@ -96,7 +96,7 @@ const router = new Router({
9696
await router.serve(8000)
9797
```
9898

99-
If a template exceeds the limit, the server responds with **500 Internal Server Error**. The full rendering behavior lives in [Performance and Limits](/rendering/performance#iteration-limit). For large datasets, use [`streamRender`](/rendering/streaming) instead. For CPU-intensive rendering, consider offloading to a [worker pool](/core-concepts/worker-pool).
99+
If a template exceeds the limit, the server responds with **400 Bad Request**. Two companion caps, `maxRenderIterations` for the whole-page loop budget and `maxOutputSize` for total output characters, behave the same way and are listed in [Routes Configuration](/getting-started/routes-configuration#configuration-options). The full rendering behavior lives in [Performance and Limits](/rendering/performance#iteration-limit). For large datasets, use [`streamRender`](/rendering/streaming) instead. For CPU-intensive rendering, consider offloading to a [worker pool](/core-concepts/worker-pool).
100100

101101
## Client IP Resolution
102102

docs/id/getting-started/routes-configuration.md

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Konfigurasi direktori routes Deserve agar cocok dengan struktur proyek.
88

99
## Opsi Router
1010

11-
Konstruktor `Router` menerima opsi konfigurasi. Yang umum adalah `routesDir` untuk folder rute dan `requestTimeoutMs` untuk timeout request. Rendering, batas request, worker pool, dan builder error khusus semuanya bisa dikonfigurasi juga. Kepercayaan proxy lewat `trustProxy` dan worker pool ada di [Resolusi IP Klien](/id/getting-started/server-configuration#resolusi-ip-klien) dan [Worker Pool](/id/core-concepts/worker-pool).
11+
Konstruktor `Router` menerima satu objek opsi. Pasangan sehari-hari adalah `routesDir` untuk folder rute dan `requestTimeoutMs` untuk tenggat request. Bagian di bawah membahas pemuatan rute, batas ukuran request, batas render template, dan dua kait lanjutan `errorResponseBuilder` serta `staticHandler`. Dua opsi terkait ada di halaman sendiri, `trustProxy` di [Resolusi IP Klien](/id/getting-started/server-configuration#resolusi-ip-klien) dan `worker` pool di [Worker Pool](/id/core-concepts/worker-pool).
1212

1313
```typescript twoslash
1414
import { Router } from '@neabyte/deserve'
@@ -53,7 +53,7 @@ const router = new Router({
5353

5454
### `maxIterations`
5555

56-
Iterasi maksimum yang diizinkan per blok <code v-pre>{{#each}}</code> di template DVE. Batas ini mencegah event loop kelaparan akibat rendering tak terbatas. Default-nya `100_000`, dan melampauinya membuat mesin melempar sehingga server membalas dengan **500 Internal Server Error**.
56+
Iterasi maksimum yang diizinkan per blok <code v-pre>{{#each}}</code> di template DVE. Batas ini mencegah event loop kelaparan akibat satu perulangan tak terbatas. Default-nya `100_000`, dan melampauinya membuat mesin melempar sehingga server membalas dengan **400 Bad Request**.
5757

5858
```typescript twoslash
5959
import { Router } from '@neabyte/deserve'
@@ -67,6 +67,34 @@ const router = new Router({
6767

6868
Untuk dataset lebih besar dari batas, gunakan [`streamRender`](/id/rendering/streaming), dan lihat [Performa dan Batas](/id/rendering/performance#batas-iterasi) untuk perilaku batasnya. Untuk rendering berat CPU, pertimbangkan mengalihkan ke [worker pool](/id/core-concepts/worker-pool).
6969

70+
### `maxRenderIterations`
71+
72+
Total maksimum eksekusi badan <code v-pre>{{#each}}</code> dalam satu render, dijumlahkan dari setiap perulangan termasuk yang bersarang. Jika `maxIterations` membatasi satu perulangan, opsi ini membatasi seluruh halaman. Default-nya `1_000_000`, dan melampauinya membalas dengan **400 Bad Request**.
73+
74+
```typescript twoslash
75+
import { Router } from '@neabyte/deserve'
76+
// ---cut---
77+
const router = new Router({
78+
routesDir: 'routes',
79+
viewsDir: './views',
80+
maxRenderIterations: 500_000
81+
})
82+
```
83+
84+
### `maxOutputSize`
85+
86+
Total maksimum karakter keluaran yang dihasilkan satu render. Batas ini mencegah template kecil membengkak menjadi response besar. Default-nya `5_000_000`, dan melampauinya membalas dengan **400 Bad Request**.
87+
88+
```typescript twoslash
89+
import { Router } from '@neabyte/deserve'
90+
// ---cut---
91+
const router = new Router({
92+
routesDir: 'routes',
93+
viewsDir: './views',
94+
maxOutputSize: 1_000_000
95+
})
96+
```
97+
7098
### `maxUrlLength`
7199

72100
Panjang maksimum URL request dalam karakter. URL lebih panjang ditolak dengan **414 URI Too Long** sebelum rute mana pun berjalan. Default-nya `8192`:
@@ -95,7 +123,7 @@ const router = new Router({
95123

96124
### `errorResponseBuilder`
97125

98-
Opsi lanjutan yang mengganti cara response error dibangun. Ia menerima context, status code, error, dan handler yang diatur dengan [`router.catch()`](/id/error-handling/object-details), lalu mengembalikan `Response` final. Kebanyakan aplikasi membentuk error lewat `router.catch()` saja, dibahas di [Penanganan Error](/id/error-handling/object-details):
126+
Opsi lanjutan yang mengganti cara response error dibangun. Opsi ini menerima context, status code, error, dan handler yang diatur dengan [`router.catch()`](/id/error-handling/object-details), lalu mengembalikan `Response` final. Kebanyakan aplikasi membentuk error lewat `router.catch()` saja, dibahas di [Penanganan Error](/id/error-handling/object-details):
99127

100128
```typescript twoslash
101129
import type { Context, ErrorMiddleware } from '@neabyte/deserve'
@@ -123,6 +151,27 @@ const router = new Router({
123151
})
124152
```
125153

154+
### `staticHandler`
155+
156+
Opsi lanjutan yang mengganti cara berkas statis dilayani. Opsi ini menerima context, [opsi statis](/id/static-file/basic#opsi-static-file) untuk rute yang cocok, dan path URL, lalu mengembalikan `Response`. Implementasi default sudah menjaga path traversal, jadi ganti hanya untuk backend khusus seperti object storage:
157+
158+
```typescript twoslash
159+
import type { Context, ServeOptions } from '@neabyte/deserve'
160+
import { Router } from '@neabyte/deserve'
161+
// ---cut---
162+
const router = new Router({
163+
routesDir: 'routes',
164+
staticHandler: {
165+
// Layani berkas dari backend khusus
166+
async serve(ctx: Context, options: ServeOptions, urlPath: string) {
167+
return ctx.send.text(`requested ${urlPath}`)
168+
}
169+
}
170+
})
171+
```
172+
173+
Daftarkan rute statisnya sendiri dengan [`router.static()`](/id/static-file/basic), yang lalu dipenuhi handler ini.
174+
126175
## Ekstensi Berkas yang Didukung
127176

128177
Deserve otomatis mendeteksi dan mendukung ekstensi berkas ini:

docs/id/getting-started/server-configuration.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ Hilangkan `requestTimeoutMs` untuk tanpa timeout (default).
8484

8585
## Batas Iterasi Template
8686

87-
Opsi `maxIterations` membatasi iterasi per blok <code v-pre>{{#each}}</code> di template DVE, yang mencegah event loop kelaparan akibat rendering tak terbatas. Default-nya `100_000`:
87+
Opsi `maxIterations` membatasi iterasi per blok <code v-pre>{{#each}}</code> di template DVE, yang mencegah event loop kelaparan akibat satu perulangan tak terbatas. Default-nya `100_000`:
8888

8989
```typescript twoslash
9090
import { Router } from '@neabyte/deserve'
@@ -96,7 +96,7 @@ const router = new Router({
9696
await router.serve(8000)
9797
```
9898

99-
Jika template melewati batas, server membalas dengan **500 Internal Server Error**. Perilaku rendering lengkap ada di [Performa dan Batas](/id/rendering/performance#batas-iterasi). Untuk dataset besar, gunakan [`streamRender`](/id/rendering/streaming). Untuk rendering berat CPU, pertimbangkan mengalihkan ke [worker pool](/id/core-concepts/worker-pool).
99+
Jika template melewati batas, server membalas dengan **400 Bad Request**. Dua batas pendamping, `maxRenderIterations` untuk anggaran perulangan seluruh halaman dan `maxOutputSize` untuk total karakter keluaran, berperilaku sama dan tercantum di [Konfigurasi Rute](/id/getting-started/routes-configuration#opsi-konfigurasi). Perilaku rendering lengkap ada di [Performa dan Batas](/id/rendering/performance#batas-iterasi). Untuk dataset besar, gunakan [`streamRender`](/id/rendering/streaming). Untuk rendering berat CPU, pertimbangkan mengalihkan ke [worker pool](/id/core-concepts/worker-pool).
100100

101101
## Resolusi IP Klien
102102

@@ -158,14 +158,14 @@ ac.abort()
158158

159159
### Penanganan Sinyal Proses
160160

161-
Tanpa `AbortSignal`, router mendengarkan `SIGINT` dan `SIGTERM` sendiri (hanya `SIGINT` di Windows) dan menguras dengan baik pada salah satunya. Tidak perlu perakitan sinyal manual:
161+
Tanpa `AbortSignal`, router mendengarkan `SIGINT` dan `SIGTERM` sendiri (hanya `SIGINT` di Windows) dan menyelesaikan request berjalan dengan rapi pada salah satunya. Tidak perlu menyiapkan sinyal secara manual:
162162

163163
```typescript twoslash
164164
import { Router } from '@neabyte/deserve'
165165

166166
const router = new Router()
167167

168-
// SIGINT dan SIGTERM menguras otomatis
168+
// SIGINT dan SIGTERM menuntaskan request otomatis
169169
await router.serve(8000, '127.0.0.1')
170170
```
171171

@@ -211,10 +211,10 @@ Lihat [Pelaporan Error](/id/middleware/observability/errors) untuk pola lengkapn
211211
Tujuannya adalah ketersediaan. Satu jalur kode yang cacat atau jahat semestinya tidak bisa membatalkan seluruh proses dan menolak layanan ke setiap rute dan service yang ditampungnya.
212212

213213
- **Penyalahgunaan rantai pasok** - dependensi transitif yang memanggil `process.exit()` atau `Deno.exit()`, baik karena kecelakaan maupun sebagai serangan, tidak bisa lagi membuat server crash. Ini selaras dengan [OWASP A03:2025 Software Supply Chain Failures](https://owasp.org/Top10/2025/A03_2025-Software_Supply_Chain_Failures/) dan [CWE-1395](https://cwe.mitre.org/data/definitions/1395.html).
214-
- **Denial of service** - memblokir terminasi-diri menghapus saklar mati ketersediaan yang mudah, terkait [CWE-400](https://cwe.mitre.org/data/definitions/400.html) dan [CWE-730](https://cwe.mitre.org/data/definitions/730.html).
215-
- **Kesalahan tak tertangkap** - menjebak unhandled rejection dan uncaught error menjaga satu request buruk dari mengakhiri proses, terkait [CWE-248](https://cwe.mitre.org/data/definitions/248.html).
214+
- **Denial of service** - memblokir terminasi-diri menghapus tombol mematikan ketersediaan yang mudah, terkait [CWE-400](https://cwe.mitre.org/data/definitions/400.html) dan [CWE-730](https://cwe.mitre.org/data/definitions/730.html).
215+
- **Kesalahan tak tertangkap** - menjebak unhandled rejection dan uncaught error mencegah satu request buruk mengakhiri proses, terkait [CWE-248](https://cwe.mitre.org/data/definitions/248.html).
216216

217-
Ini pertahanan upaya-terbaik, bukan sandbox. Ia menyisip ke titik masuk terminasi yang diketahui ketimbang mengisolasi kode tak tepercaya, jadi ia mengurangi blast radius tanpa mengklaim menghentikan setiap penyalahgunaan yang mungkin. Pasangkan dengan flag izin Deno dan tinjauan dependensi untuk jaminan lebih kuat. Pendekatan berlapis terhadap kesalahan dibahas di [Pertahanan Berlapis](/id/error-handling/defense-in-depth).
217+
Ini pertahanan upaya-terbaik, bukan sandbox. Pertahanan ini menyisip ke titik masuk terminasi yang diketahui ketimbang mengisolasi kode tak tepercaya, jadi ia memperkecil dampak kesalahan tanpa mengklaim menghentikan setiap penyalahgunaan yang mungkin. Pasangkan dengan flag izin Deno dan tinjauan dependensi untuk jaminan lebih kuat. Pendekatan berlapis terhadap kesalahan dibahas di [Pertahanan Berlapis](/id/error-handling/defense-in-depth).
218218

219219
## Pengujian Konfigurasi
220220

docs/id/rendering/performance.md

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description: "Karakteristik performa dan perilaku caching mesin template Deserve
44

55
# Performa dan Batas
66

7-
Mesin DVE meng-cache template terkompilasi dan menjaga rendering dengan dua batas, jadi halaman besar tetap cepat dan template liar gagal dengan jelas alih-alih menggantung server.
7+
Mesin DVE meng-cache template terkompilasi dan mengawal rendering dengan dua batas, jadi halaman besar tetap cepat dan template yang tak terkendali gagal dengan jelas alih-alih menggantung server.
88

99
## Caching
1010

@@ -27,7 +27,7 @@ Cache hanya mencakup kompilasi template, bukan data atau logika backend. Perubah
2727

2828
## Batas Iterasi
2929

30-
Setiap blok <code v-pre>{{#each}}</code> dibatasi `100_000` iterasi secara default, yang mencegah event loop kelaparan akibat perulangan tak terbatas. Setel dengan `maxIterations`:
30+
Setiap blok <code v-pre>{{#each}}</code> dibatasi `100_000` iterasi secara default, yang mencegah event loop kelaparan akibat satu perulangan tak terbatas. Setel dengan `maxIterations`:
3131

3232
```typescript twoslash
3333
import { Router } from '@neabyte/deserve'
@@ -38,8 +38,24 @@ const router = new Router({
3838
})
3939
```
4040

41-
Ketika perulangan melewati batas, mesin melempar dan server membalas **500**. Untuk dataset sangat besar, gunakan [streaming rendering](/id/rendering/streaming). Untuk rendering berat CPU, alihkan ke [worker pool](/id/core-concepts/worker-pool).
41+
Ketika perulangan melewati batas, mesin melempar dan server membalas **400 Bad Request**. Untuk dataset sangat besar, gunakan [streaming rendering](/id/rendering/streaming). Untuk rendering berat CPU, alihkan ke [worker pool](/id/core-concepts/worker-pool).
42+
43+
## Batas Anggaran Render
44+
45+
Dua batas lain mengawal seluruh render, bukan hanya satu perulangan. `maxRenderIterations` menjumlahkan setiap eksekusi badan <code v-pre>{{#each}}</code> di seluruh halaman, termasuk perulangan bersarang, dan default-nya `1_000_000`. `maxOutputSize` membatasi total karakter yang boleh dihasilkan satu render dan default-nya `5_000_000`:
46+
47+
```typescript twoslash
48+
import { Router } from '@neabyte/deserve'
49+
// ---cut---
50+
const router = new Router({
51+
viewsDir: './views',
52+
maxRenderIterations: 500_000,
53+
maxOutputSize: 2_000_000
54+
})
55+
```
56+
57+
Melewati salah satu batas membalas dengan **400 Bad Request**, status yang sama dengan batas per perulangan. Ketiganya diatur di [opsi Router](/id/getting-started/routes-configuration#opsi-konfigurasi).
4258

4359
## Batas Kedalaman Include
4460

45-
Include template dibatasi 64 tingkat bersarang, jadi rantai include melingkar atau liar melempar error alih-alih berputar selamanya. Menjaga partial tetap dangkal aman jauh di dalam batas ini.
61+
Include template dibatasi 64 tingkat bersarang, jadi rantai include melingkar atau tak terkendali melempar error alih-alih berputar selamanya. Melewati batas ini membalas dengan **400 Bad Request**. Selama struktur partial tidak terlalu dalam, render tetap jauh di dalam batas ini.

0 commit comments

Comments
 (0)