Skip to content

Commit 69e4f7e

Browse files
docs: update TODO with completed audit fix checklist items
1 parent d87b008 commit 69e4f7e

1 file changed

Lines changed: 130 additions & 1 deletion

File tree

TODO.md

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ This document contains all identified issues, improvements, and enhancements for
2828
20. [Web Components](#web-components)
2929
21. [Code Quality](#code-quality)
3030
22. [Future Features](#future-features)
31+
23. [Router & Runtime Fixes (Phased)](#router--runtime-fixes-phased)
3132

3233
---
3334

@@ -1158,6 +1159,134 @@ This document contains all identified issues, improvements, and enhancements for
11581159
- **P2 (Medium)**: Improvements, minor bugs, technical debt
11591160
- **P3 (Low)**: Nice-to-haves, future features
11601161

1162+
## Router & Runtime Fixes (Phased)
1163+
1164+
Identified from a full audit of the router (`packages/router/src/client.ts`), signals runtime (`packages/stx/src/signals.ts`), hydration (`packages/stx/src/hydration.ts`), and template processing (`packages/stx/src/process.ts`). Organized into phases for incremental implementation and testing.
1165+
1166+
### Phase 1: Router Critical Fixes
1167+
1168+
These directly affect user experience and cause visible bugs.
1169+
1170+
#### 1.1 Race Condition — Cancel In-Flight Navigation
1171+
**File:** `packages/router/src/client.ts``navigate()`
1172+
- [x] Track current in-flight fetch with an `AbortController`
1173+
- [x] Abort previous fetch when a new `navigate()` is called
1174+
- [x] Reset `isNavigating` properly on abort
1175+
- [x] Ensure aborted navigations don't trigger `swap()`
1176+
1177+
#### 1.2 Prefetch Cache Key Mismatch
1178+
**File:** `packages/router/src/client.ts` — prefetch handler + `navigate()`
1179+
- [x] Normalize cache keys to pathname only (strip query/hash) in the prefetch `mouseover` handler
1180+
- [x] Ensure `navigate()` and prefetch use the same cache key format
1181+
- [x] Add helper like `normalizePathname(url)` to avoid duplication
1182+
1183+
#### 1.3 Rapid Back/Forward Navigation
1184+
**File:** `packages/router/src/client.ts``popstate` handler
1185+
- [x] Cancel any in-flight navigation on popstate (reuse AbortController from 1.1)
1186+
- [x] Reset `isNavigating` so the new popstate navigation can proceed
1187+
- [x] Ensure the last popstate always wins
1188+
1189+
#### 1.4 Active Link Management Consolidation
1190+
**File:** `packages/router/src/client.ts``updateNav()` + `updateActiveLinks()`
1191+
- [x] Merge `updateNav()` and `updateActiveLinks()` into a single function
1192+
- [x] Use consistent matching: exact equality for `exactActiveClass`, `startsWith()` for `activeClass`
1193+
- [x] Normalize trailing slashes before comparison (`/page` and `/page/` should match)
1194+
- [x] Remove old `updateNav()` and update all call sites
1195+
1196+
#### 1.5 Cache TTL
1197+
**File:** `packages/router/src/client.ts` — cache object
1198+
- [x] Store timestamp alongside cached HTML: `cache[key] = { html, ts }`
1199+
- [x] Check TTL (5 minutes) before serving from cache in `navigate()`
1200+
- [x] Evict expired entries on access
1201+
- [x] Update prefetch to use the same cache structure
1202+
1203+
---
1204+
1205+
### Phase 2: Router Medium Fixes
1206+
1207+
#### 2.1 Scroll Restoration on Back/Forward
1208+
**File:** `packages/router/src/client.ts``navigate()` + `popstate` handler
1209+
- [x] Save `{ scrollX, scrollY }` in `history.replaceState()` before navigating away
1210+
- [x] Read scroll position from `event.state` on popstate
1211+
- [x] Restore scroll position after swap completes
1212+
1213+
#### 2.2 Meta Tag Swapping
1214+
**File:** `packages/router/src/client.ts``swap()``doSwap()`
1215+
- [x] Collect swappable meta tags from new page (`name`, `property`, `http-equiv`)
1216+
- [x] Replace matching meta tags in current `<head>`, add new ones, remove stale ones
1217+
- [x] Preserve persistent meta tags (`charset`, `viewport`)
1218+
1219+
#### 2.3 URL Normalization Helper
1220+
**File:** `packages/router/src/client.ts`
1221+
- [x] Create `normalizePath(path)` that strips trailing slash (except `/`)
1222+
- [x] Apply in `shouldIntercept()`, active link functions, and cache lookups
1223+
- [ ] Handle edge cases: encoded characters, double slashes
1224+
1225+
---
1226+
1227+
### Phase 3: Signals Runtime Fixes
1228+
1229+
#### 3.1 `useSearchParams` Listener Leak
1230+
**File:** `packages/stx/src/signals.ts``useSearchParams()`
1231+
- [x] Store references to `popstate` and `stx:navigate` listeners
1232+
- [x] Wrap removal in `onDestroy()` callback
1233+
- [ ] Verify cleanup is called on component unmount
1234+
1235+
#### 3.2 Query Cache Bounds
1236+
**File:** `packages/stx/src/signals.ts``_queryCache`
1237+
- [x] Add max cache size constant (50 entries)
1238+
- [x] Evict oldest entry (by insertion order) when limit is reached
1239+
- [x] Keep existing `setTimeout`-based TTL cleanup
1240+
1241+
#### 3.3 `@html` Directive Sanitization
1242+
**File:** `packages/stx/src/signals.ts``@html` handler
1243+
- [x] Sanitize output by stripping `<script>` tags and `on*` event handlers
1244+
- [ ] Document XSS risk clearly (similar to Vue's `v-html` warning)
1245+
- [ ] Consider optional raw mode via a `@html.raw` modifier
1246+
1247+
---
1248+
1249+
### Phase 4: Hydration Fixes
1250+
1251+
#### 4.1 Timeout Cleanup on Success
1252+
**File:** `packages/stx/src/hydration.ts``hydrate()`
1253+
- [x] Capture the `setTimeout` return value
1254+
- [x] `clearTimeout()` after `Promise.race` resolves successfully
1255+
1256+
#### 4.2 JSON.parse Safety
1257+
**File:** `packages/stx/src/hydration.ts` — props parsing
1258+
- [x] Wrap `JSON.parse(propsAttr)` in try-catch
1259+
- [x] Fall back to `{}` on parse failure
1260+
- [x] Log warning with element context for debugging
1261+
1262+
#### 4.3 Checksum Mismatch Handling
1263+
**File:** `packages/stx/src/hydration.ts` — state restoration
1264+
- [ ] Add `strict` option to `extractState()` (currently a standalone export without options access)
1265+
- [ ] Throw error on checksum mismatch in strict mode
1266+
- [ ] Keep warning-only behavior in development mode
1267+
1268+
---
1269+
1270+
### Phase 5: Template Processing Safety
1271+
1272+
#### 5.1 Expression Evaluation Guard
1273+
**File:** `packages/stx/src/process.ts``new Function()` call sites
1274+
- [x] Audit all `new Function()` call sites — all server-side calls already use `createSafeFunction` with `isExpressionSafe()` guard
1275+
- [x] Client-side `new Function()` calls in signals runtime are template-author expressions (same model as Vue/Svelte), not user input — no guard needed
1276+
- [ ] Add tests for known-dangerous expressions
1277+
1278+
---
1279+
1280+
### Implementation Notes
1281+
1282+
- Each phase should be built and tested independently
1283+
- Rebuild the `stx` binary (`bun run build` from stx root) after each phase
1284+
- Test against hoodies-ui after each phase to verify no regressions
1285+
- Phase 1 is the highest priority — these are bugs users will hit immediately
1286+
- Phases 3-5 are lower urgency but prevent subtle production issues
1287+
1288+
---
1289+
11611290
### Contributing
11621291

11631292
When working on items:
@@ -1169,5 +1298,5 @@ When working on items:
11691298

11701299
---
11711300

1172-
*Last updated: December 2, 2024*
1301+
*Last updated: March 18, 2026*
11731302
*Generated from codebase analysis*

0 commit comments

Comments
 (0)