You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit edd7549
Browse filesBrowse the repository at this point in the historyBrowse files
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
Copy file name to clipboardExpand all lines: CHANGELOG.md
+74-73Lines changed: 74 additions & 73 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,104 +10,105 @@ Format inspired by [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
10
10
11
11
### Added
12
12
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
14
14
- Hot reload for routes and templates via file system watchers (`Routing.Watcher`, `Rendering.Watcher`)
15
15
-`Helper` utility class consolidating `headersToRecord` from `Redirect` and `Response` into a shared `Helper.toRecord` method
16
16
-`Engine.viewsDir` getter, `Engine.invalidateFile()` and `Engine.refreshPaths()` for cache invalidation during hot reload
17
17
-`Handler.reloadRoute()` and `Handler.removeRoute()` for runtime route replacement
18
18
-`Handler.getViewEngine()` accessor
19
19
-`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
31
20
-`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
36
21
- Method overload signatures for `Context.cookie()`, `Context.header()`, `Context.query()`
37
22
- Documentation pages for hot reload feature (EN and ID)
38
23
- Indonesian landing page feature cards for middleware, template engine, and hot reload
-`SecurityHeaderKey`, `SecurityHeaderValue` type aliases
43
+
-`SocketCallback`, `SocketEventCallback<E>` type aliases in WebSocket interface
44
+
-`ErrorInfo` named interface replacing inline error object in `ErrorMiddleware`
43
45
44
46
### Changed
45
47
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)
-`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
90
58
-`Engine.renderNodes()` now delegates to `renderNodeToChunk()` instead of duplicating node type handling
91
59
-`Error.escapeHtml()` now escapes `"` and `'` in addition to `&`, `<`, `>`
92
60
-`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
93
69
-`Scanner.discoverRoutes()` uses extracted `registerHandlers()` instead of inline registration
94
70
-`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 `
97
92
- Indonesian landing page tagline updated
98
93
99
94
### Removed
100
95
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`
104
99
105
100
### Fixed
106
101
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
107
108
-`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
109
110
- 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
111
112
112
113
---
113
114
@@ -128,7 +129,7 @@ Format inspired by [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
128
129
### Changed
129
130
130
131
- 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`
132
133
- Remove `optional` flag from `ExprNode` member access (expression AST simplification)
133
134
- Simplify `Router` constructor using object destructuring instead of manual property copying
134
135
- 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/).
142
143
143
144
### Fixed
144
145
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
146
147
- Filename extraction in `Response.file()` now splits on both `/` and `\` for cross-platform compatibility
147
148
- Cross-platform test path resolution using `fileURLToPath` instead of raw URL strings
148
149
- TypeScript parameter compatibility in `streamRender()` method
0 commit comments