Skip to content

Commit edd7549

Browse files
committed
docs(changelog): restructure unreleased section with subgroups
- Add maxIterations and security fix entries - Group Added into Dependencies and Types subsections - Group Changed into Error Handling, Template Engine, Routing, Interface, and Code Style - Replace em dash with hyphen
1 parent d508911 commit edd7549

1 file changed

Lines changed: 74 additions & 73 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 74 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -10,104 +10,105 @@ Format inspired by [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
1010

1111
### Added
1212

13-
- `@neabyte/superwatcher@^0.1.1` dependency for file watching with debounce, event dedup, and atomic write detection
13+
- `maxIterations` option on `EngineOptions` and `HandlerOptions` - limits `#each` loop iterations to prevent event loop starvation from unbounded template rendering (default 100,000). Exceeding the limit throws `Deno.errors.InvalidData` with a 500 response
1414
- Hot reload for routes and templates via file system watchers (`Routing.Watcher`, `Rendering.Watcher`)
1515
- `Helper` utility class consolidating `headersToRecord` from `Redirect` and `Response` into a shared `Helper.toRecord` method
1616
- `Engine.viewsDir` getter, `Engine.invalidateFile()` and `Engine.refreshPaths()` for cache invalidation during hot reload
1717
- `Handler.reloadRoute()` and `Handler.removeRoute()` for runtime route replacement
1818
- `Handler.getViewEngine()` accessor
1919
- `Scanner.registerHandlers()` static method extracted from inline scanning logic
20-
- `MaybeAsync<T>` type alias in `Error` and `Middleware` interfaces
21-
- `AstBlockKind`, `UnaryOp`, `SecurityHeaderKey`, `SecurityHeaderValue` type aliases for stricter typing
22-
- `SocketCallback` and `SocketEventCallback<E>` type aliases in WebSocket interface
23-
- `ErrorInfo` named interface replacing inline error object in `ErrorMiddleware`
24-
- `Utility.ts` module with shared `MaybeAsync<T>` and `DataRecord` type aliases
25-
- `HttpMethod` literal union type constraining HTTP method strings
26-
- `RouteFileExtension` literal union type for allowed route file extensions
27-
- `RouteChangeEntry` interface for hot-reload pending route changes
28-
- `RedirectStatus`, `RedirectBuilder`, `RedirectInit` types for redirect response typing
29-
- `BodyParsedFormat` exported type alias for body parser format tracking
30-
- `NextFn`, `MiddlewareResult`, `AsyncMiddlewareResult` exported type aliases for middleware chain typing
3120
- `WatchableEngine` interface for template cache invalidation
32-
- `AstBlockNode` extracted type for block-level AST nodes containing children
33-
- `AstNodeType`, `ExprNodeType`, `ExprTokenKind` discriminant type aliases
34-
- `ArithmeticSign` shared type for `+`/`-` operators used in unary and binary expressions
35-
- `BinaryOp`, `StructuralOp`, `TokenOp` operator literal types for expression tokens
3621
- Method overload signatures for `Context.cookie()`, `Context.header()`, `Context.query()`
3722
- Documentation pages for hot reload feature (EN and ID)
3823
- Indonesian landing page feature cards for middleware, template engine, and hot reload
39-
- VitePress custom theme directory (`docs/.vitepress/theme/`)
40-
- `docs/en/index.md` standalone English landing page
41-
- `@neabyte/utils-core@^0.2.0` dependency for `Async` debounce and `createSequential` utilities
42-
- Test coverage for `Engine` (empty data, null values, `viewsDir` getter, `streamRender` missing template), `Handler` (default constructor, `maxRouteParamLength` zero, error `statusCode` propagation, `getViewEngine`, `removeRoute`), `Router` (empty options, no options, `HandlerOptions` propagation), `Scanner` (`registerHandlers`, empty module, no extension, empty string), `Error` (`escapeHtml` quote escaping, `buildResponse` edge cases), `Redirect` (extra headers merging), `Response` (stream and custom response)
24+
- VitePress custom theme directory (`docs/.vitepress/theme/`) and `docs/en/index.md` standalone English landing page
25+
- Test coverage for `Engine`, `Handler`, `Router`, `Scanner`, `Error`, `Redirect`, `Response`
26+
27+
#### Dependencies
28+
29+
- `@neabyte/stackz@^0.1.0` for formatted stack traces in route reload and scan error logging
30+
- `@neabyte/superwatcher@^0.1.1` for file watching with debounce, event dedup, and atomic write detection
31+
- `@neabyte/utils-core@^0.2.0` for `Async` debounce and `createSequential` utilities
32+
33+
#### Types
34+
35+
- `MaybeAsync<T>`, `DataRecord` type aliases in shared `Utility.ts` module
36+
- `HttpMethod` literal union, `RouteFileExtension` literal union
37+
- `RouteChangeEntry` interface for hot-reload pending route changes
38+
- `RedirectStatus`, `RedirectBuilder`, `RedirectInit` types for redirect response typing
39+
- `BodyParsedFormat`, `NextFn`, `MiddlewareResult`, `AsyncMiddlewareResult` exported type aliases
40+
- `AstBlockNode`, `AstNodeType`, `ExprNodeType`, `ExprTokenKind` discriminant type aliases
41+
- `AstBlockKind`, `UnaryOp`, `ArithmeticSign`, `BinaryOp`, `StructuralOp`, `TokenOp` operator literal types
42+
- `SecurityHeaderKey`, `SecurityHeaderValue` type aliases
43+
- `SocketCallback`, `SocketEventCallback<E>` type aliases in WebSocket interface
44+
- `ErrorInfo` named interface replacing inline error object in `ErrorMiddleware`
4345

4446
### Changed
4547

46-
- `TemplateData` renamed to `DataRecord` and moved to shared `Utility.ts` module
47-
- `MaybeAsync<T>` moved from file-local types in `Error.ts` and `Middleware.ts` to shared `Utility.ts`
48-
- All interface properties across `Auth`, `BodyLimit`, `Cors`, `Handler`, `Middleware`, `Render`, `Session`, `Serve`, `WebSocket` made `readonly`
49-
- `Constant.allowedExtensions` typed as `readonly RouteFileExtension[]` (was `string[]`)
50-
- `Constant.httpMethods` typed as `readonly HttpMethod[]` (was `string[]`)
51-
- `Context.redirect()` param `status` typed as `RedirectStatus` (was `number`), `init` typed as `RedirectInit`
52-
- `Response.create()` param `buildRedirect` typed as `RedirectBuilder` (was inline function type)
53-
- `Redirect.buildResponse()` param `status` typed as `RedirectStatus` (was `number`)
54-
- `Worker` message data typed as `WorkerMessageData` (was inline object type)
55-
- `CorsOptions.methods` typed as `readonly HttpMethod[]` (was `string[]`)
56-
- `ErrorInfo.error` changed from optional to required
57-
- `StatusError` changed from `type` to `interface extends Error`
58-
- `SendHelpers` changed from `type` to `interface`
59-
- `StaticFileHandler` changed from `type` to `interface`
60-
- `AstNode` union members made `readonly` on immutable fields
61-
- `ExprNode` literal value narrowed from `unknown` to `string | number`
62-
- `ExprToken` op value typed as `TokenOp` (was `string`)
63-
- `DveStackFrame.node` typed as `AstBlockNode` (was `AstNode`)
64-
- `AstBlockKind` derived from `AstBlockNode` (was `Extract` on full `AstNode`)
65-
- `Expression.matchOp()` param typed as `TokenOp` (was `string`)
66-
- `BasicAuth` and `Session` middleware return typed as `AsyncMiddlewareResult`
67-
- `Handler.executeMiddlewares()` return typed as `AsyncMiddlewareResult`
68-
- `SendHelpers.redirect` signature updated with `RedirectInit` param
69-
- `Handler.createHandler()` now strips response body for HEAD requests, returning headers-only response
70-
- `Handler.createHandler()` falls back to GET handler when HEAD method has no registered route
71-
- CORS middleware returns `ctx.send.custom(null, { status: 204 })` for preflight instead of `ctx.handleError(204, ...)`
72-
- CORS middleware returns `ctx.send.custom(null, { status: 403 })` for forbidden origin instead of `ctx.handleError(403, ...)`
73-
- JSDoc briefs and descriptions standardized to 6/9 word rule with periods
74-
- Alphabetical sort order applied to all module-level declarations and class members
75-
- `WebSocket.onDisconnect` callback now receives `CloseEvent` as second argument, exposing `event.code`, `event.reason`, and `event.wasClean`
76-
- `WebSocket.onDisconnect` type changed from `SocketCallback` to `SocketEventCallback<CloseEvent>`
77-
- File watchers migrated from internal `WatchFs` to `@neabyte/superwatcher` with ignore-based extension filtering, Map-based event dedup, and atomic write detection
78-
- `Routing.Watcher.watch()` and `Rendering.Watcher.watch()` changed from `async` to synchronous
79-
- `Router.startWatchers()` no longer wraps watcher calls in `.catch()` since watchers are now synchronous
80-
- Error types upgraded to `Deno.errors` for semantic error handling:
81-
- `Context.render()`/`streamRender()`: `Error` → `Deno.errors.NotSupported` (view engine not configured)
82-
- `Context.ensureBodyNotConsumed()`: `Error` → `Deno.errors.BadResource` (body already consumed)
83-
- `Engine.resolveTemplate()`: `Error` → `Deno.errors.NotFound` (template not found)
84-
- `Scanner.validateModule()`: `Error` → `Deno.errors.InvalidData` (no HTTP method) and `TypeError` (export not a function)
85-
- `BasicAuth.create()`: `Error` → `Deno.errors.InvalidData` (empty users array)
86-
- `Session.create()`: `Error` → `Deno.errors.InvalidData` (empty cookieSecret)
87-
- `RouterOptions` now extends `HandlerOptions` instead of duplicating fields
88-
- `SecurityHeadersOptions` refactored to `Partial<Record<SecurityHeaderKey, SecurityHeaderValue>>`
89-
- `Engine.render()` and `Engine.streamRender()` refactored to share `resolveTemplate()` private method, removing duplicated path resolution logic
48+
#### Error Handling
49+
50+
- Error types upgraded to `Deno.errors` for semantic error handling across `Context`, `Engine`, `Scanner`, `BasicAuth`, `Session`, `Worker`, `Response`, `Handler`, `Static`, `Tokenizer`
51+
- `Error.buildResponse()` now sanitizes 5xx error messages using a static `serverErrorMessages` map instead of exposing raw internal error details
52+
- `Handler.reloadRoute()` and `Scanner.discoverRoutes()` error logging now uses `Stackz.format()` for detailed formatted stack traces
53+
54+
#### Template Engine
55+
56+
- `Utils.lookup()` blocks prototype chain access via `Object.hasOwn` check - only own properties are accessible in templates
57+
- `Engine.render()` and `Engine.streamRender()` refactored to share `resolveTemplate()` private method
9058
- `Engine.renderNodes()` now delegates to `renderNodeToChunk()` instead of duplicating node type handling
9159
- `Error.escapeHtml()` now escapes `"` and `'` in addition to `&`, `<`, `>`
9260
- `Utils.escape()` in rendering engine now delegates to `Core.Error.escapeHtml()` instead of duplicating logic
61+
62+
#### Routing and Middleware
63+
64+
- `Handler.createHandler()` now strips response body for HEAD requests and falls back to GET handler when no HEAD route is registered
65+
- CORS middleware returns `ctx.send.custom(null, ...)` for preflight (204) and forbidden origin (403) instead of `ctx.handleError()`
66+
- `Context.parseCookies()` uses `for...of` and keeps only the first occurrence per RFC 6265
67+
- `Context.parseHeaders()` simplified to `Object.fromEntries(this.req.headers)`
68+
- `Router` constructor simplified - passes options directly to `Handler` instead of destructuring
9369
- `Scanner.discoverRoutes()` uses extracted `registerHandlers()` instead of inline registration
9470
- `Router.serve()` starts route and template watchers automatically after scanning
95-
- VitePress config: removed duplicate `root` locale sidebar, changed root locale key from `root` to `en`, removed inline CSS styles, added viewport meta tag
96-
- Documentation code blocks changed from `` ```dve `` to `` ```html `` for better syntax highlighting
71+
- File watchers migrated from internal `WatchFs` to `@neabyte/superwatcher`
72+
73+
#### Interface and Type Refinements
74+
75+
- `TemplateData` renamed to `DataRecord` and moved to shared `Utility.ts` module
76+
- `MaybeAsync<T>` moved from file-local types to shared `Utility.ts`
77+
- All interface properties made `readonly`
78+
- `RouterOptions` now extends `HandlerOptions` instead of duplicating fields
79+
- `StatusError` changed from `type` to `interface extends Error`
80+
- `SendHelpers` and `StaticFileHandler` changed from `type` to `interface`
81+
- `WebSocket.onDisconnect` type changed to `SocketEventCallback<CloseEvent>`, now receives `CloseEvent` as second argument
82+
- `SecurityHeadersOptions` refactored to `Partial<Record<SecurityHeaderKey, SecurityHeaderValue>>`
83+
84+
#### Code Style
85+
86+
- JSDoc briefs and descriptions standardized to 6/9 word rule with periods
87+
- Alphabetical sort order applied to all module-level declarations and class members
88+
- `Handler.ts`, `Middleware.ts`, `Static.ts` interfaces refactored from barrel imports to direct named imports
89+
- `Static.serve()` ETag hash generation simplified to a single `Array.from` with map callback
90+
- `Watcher` uses `pendingChanges.values()` instead of destructured iteration
91+
- VitePress config cleanup and documentation code blocks changed from ` ```dve ` to ` ```html `
9792
- Indonesian landing page tagline updated
9893

9994
### Removed
10095

101-
- `WatchFs` class from `src/core/WatchFs.ts` — replaced by `@neabyte/superwatcher`
102-
- `WatchedEvent` and `WatchFsOptions` types from `src/interfaces/Watcher.ts` — no longer needed
103-
- `TemplateData` type alias — replaced by `DataRecord` in `Utility.ts`
96+
- `WatchFs` class from `src/core/WatchFs.ts` - replaced by `@neabyte/superwatcher`
97+
- `WatchedEvent` and `WatchFsOptions` types from `src/interfaces/Watcher.ts`
98+
- `TemplateData` type alias - replaced by `DataRecord` in `Utility.ts`
10499

105100
### Fixed
106101

102+
- **Security**: Recursive `{{> include}}` crashes server process - self-referencing templates or circular include chains (A→B→A) caused infinite recursion, exhausting V8 heap memory (~4GB) and killing the Deno process. `Engine.render()` now tracks include depth and throws `Deno.errors.InvalidData` when exceeding 64 levels
103+
- **Security**: Prototype chain leakage in DVE template `Utils.lookup()` - accessing `{{constructor}}`, `{{__proto__}}`, or `{{toString}}` in templates leaked native function references from the JavaScript prototype chain. Now blocked by `Object.hasOwn` check that only allows own properties, naturally preventing prototype chain traversal
104+
- **Security**: Cookie last-write-wins allowed session fixation - duplicate cookie names (e.g. injected via subdomain or XSS) would overwrite the legitimate value. `parseCookies()` now keeps only the first occurrence per RFC 6265
105+
- **Security**: Internal error details leaked to clients on 5xx responses - raw parser messages, template engine errors, and file system paths were exposed in JSON and HTML error responses. Server errors now return generic status text from a static lookup map
106+
- DVE template `Utils.lookup()` no longer blocks own properties named `constructor`, `toString`, etc. - the previous `unsafeProperties` blocklist prevented rendering user data with these key names even when explicitly set. Replaced with `Object.hasOwn` which allows own properties while still blocking prototype-inherited ones
107+
- DVE expression tokenizer now supports scientific notation (`1e2`, `2.5e3`, `1e-3`, `5E2`) - previously `{{1e308 + 1e308}}` crashed with 500 because the tokenizer parsed `1` as number and `e308` as identifier
107108
- `Redirect` and `Response` no longer carry duplicate `headersToRecord` implementations
108-
- HEAD requests no longer hang — response body is stripped and Content-Length omitted so clients don't wait for bytes that never arrive
109+
- HEAD requests no longer hang - response body is stripped and Content-Length omitted so clients don't wait for bytes that never arrive
109110
- HEAD requests now fall back to GET handler when no HEAD route is registered, matching standard HTTP semantics
110-
- CORS preflight no longer returns 500 — `ctx.handleError(204, ...)` created a JSON body on null-body status 204, causing Deno to throw. Now uses `ctx.send.custom(null, { status: 204 })` directly
111+
- CORS preflight no longer returns 500 - `ctx.handleError(204, ...)` created a JSON body on null-body status 204, causing Deno to throw. Now uses `ctx.send.custom(null, { status: 204 })` directly
111112

112113
---
113114

@@ -128,7 +129,7 @@ Format inspired by [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
128129
### Changed
129130

130131
- DVE template parsing is now strict: unmatched `{{else}}`, `{{/if}}`, `{{/each}}` and unclosed `{{#if}}`/`{{#each}}` blocks throw errors instead of being silently ignored
131-
- Remove `options` parameter from `ViewEngine.render()` — the engine always uses its configured `viewsDir`
132+
- Remove `options` parameter from `ViewEngine.render()` - the engine always uses its configured `viewsDir`
132133
- Remove `optional` flag from `ExprNode` member access (expression AST simplification)
133134
- Simplify `Router` constructor using object destructuring instead of manual property copying
134135
- Simplify `Worker.createPool` using `Array.from` instead of manual loop
@@ -142,7 +143,7 @@ Format inspired by [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
142143

143144
### Fixed
144145

145-
- Windows path handling in static file serving — backslash separators are now normalized correctly
146+
- Windows path handling in static file serving - backslash separators are now normalized correctly
146147
- Filename extraction in `Response.file()` now splits on both `/` and `\` for cross-platform compatibility
147148
- Cross-platform test path resolution using `fileURLToPath` instead of raw URL strings
148149
- TypeScript parameter compatibility in `streamRender()` method

0 commit comments

Comments
 (0)