Fix PR #23 review findings and CI typecheck regression#24
Fix PR #23 review findings and CI typecheck regression#24riceharvest wants to merge 25 commits intomainfrom
Conversation
- Upgraded multiple packages to modern standards (Next.js, Next-auth, PWA, SEO). - Added new utility packages: critters, next-circuit-breaker, next-csrf, next-images, next-json-ld. - Integrated Changesets for versioning. - Updated CI/CD workflows and linting configurations. - Fixed numerous linting and type-checking issues across the monorepo.
Review Summary by QodoFix PR #23 review findings: middleware chaining, API validation, test compatibility, and configuration cleanup
WalkthroughsDescription• **Fix middleware chaining and error handling**: Rewrote next-connect router exec() method with fail-fast behavior, detecting multiple next() calls and calls after last middleware • **Harden API helpers with validation**: Added guarded JSON parsing, generic error messages, validated token parsing with header injection prevention, and authorization header only when token is valid • **Restore test suite compatibility**: Migrated tests from Vitest to Node.js native test runner, updated assertions, fixed mock patterns, and addressed stale hook test expectations • **Improve type safety**: Relaxed type constraints in compatibility layers, added type assertions for crypto operations, and updated session method implementations with proper context binding • **Refactor ESLint configuration**: Tightened globals scoping so browser/Jest globals only apply to relevant files, added Vitest globals support • **Simplify build and test configurations**: Removed unnecessary sourcemaps, minification, and esbuild banners; simplified Vitest patterns; updated TypeScript entry points • **Add comprehensive test coverage**: New tests for API helpers, middleware error handling, plugin composition, image optimization, and react-virtualized components • **Implement new packages and features**: Added Critters CSS extraction, next-optimized-images webpack plugin, next-compose-plugins composition utilities, and react-query-auth example with MSW mocking • **Update dependencies and types**: Changed cookie serialization types from CookieSerializeOptions to SerializeOptions, updated session record types for flexibility Diagramflowchart LR
A["Middleware Chaining<br/>next-connect"] -->|fail-fast detection| B["Error Handling<br/>Multiple next() calls"]
C["API Helpers<br/>react-query-auth"] -->|guarded parsing| D["Validation<br/>Token & JSON"]
E["Test Migration<br/>Vitest → Node.js"] -->|compatibility fixes| F["Mock Updates<br/>Assertions & Patterns"]
G["Type Safety<br/>Relaxed Constraints"] -->|context binding| H["Session Methods<br/>Fixed Implementation"]
I["ESLint Config<br/>Scoped Globals"] -->|file-specific| J["Browser/Jest<br/>Globals Isolation"]
K["Build Config<br/>Simplified"] -->|removed bloat| L["Sourcemaps<br/>Minification Cleanup"]
M["New Packages<br/>Critters, Compose"] -->|comprehensive tests| N["Plugin System<br/>Image Optimization"]
File Changes1. packages/next-iron-session/src/index.test.ts
|
Code Review by Qodo
1.
|
|
|
||
| const fn = fns[position]; | ||
| if (!fn) { | ||
| throw new Error("next() called with no middleware remaining"); |
There was a problem hiding this comment.
WARNING: The exec method throws an error when next() is called with no middleware remaining. This could break existing code that expects next() to simply return without error when there are no more handlers. Consider changing this to return Promise.resolve() instead of throwing an error.
| } | ||
|
|
||
| export function loginWithEmailAndPassword(data: unknown): Promise<AuthResponse> { | ||
| return fetch("/auth/login", { |
There was a problem hiding this comment.
CRITICAL: Missing Content-Type header in login and register requests. This should be set to application/json when sending JSON data in the request body.
| ...globals.node, | ||
| ...globals.jest, | ||
| ...globals.es2021, | ||
| vi: "readonly", |
There was a problem hiding this comment.
SUGGESTION: The vi and spyOn globals are defined in the test files configuration. However, these are Vitest globals, not Jest globals. Consider adding a separate Vitest configuration or updating the comments to reflect this.
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (1 file)
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 27e5058365
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "devDependencies": { | ||
| "@types/node": "^22.0.0", | ||
| "tsup": "^8.0.0", | ||
| "typescript": "^5.0.0", | ||
| "vitest": "^4.0.18" | ||
| }, | ||
| "peerDependencies": { | ||
| "webpack": "^4.0.0 || ^5.0.0" | ||
| }, | ||
| "peerDependenciesMeta": { | ||
| "webpack": { | ||
| "optional": true | ||
| } | ||
| }, | ||
| "engines": { | ||
| "node": ">=18.0.0" | ||
| "eslint-plugin-jest": "^29.15.0", | ||
| "tsup": "^8.5.1", | ||
| "typescript": "^5.9.3", | ||
| "vitest": "^2.1.9" |
There was a problem hiding this comment.
Restore runtime loader dependencies for next-images
The plugin still resolves url-loader and file-loader at runtime in withImages (packages/next-images/src/index.ts), but this manifest no longer declares either package as a runtime dependency. In a normal consumer install, those loaders will not be present, so Next.js builds using @opensourceframework/next-images will fail with module resolution errors when webpack evaluates the rule. Please add these loader packages back under dependencies (not only dev-only tooling).
Useful? React with 👍 / 👎.
| const users: Record<string, DBUser> = JSON.parse( | ||
| window.localStorage.getItem('db_users') || '{}' | ||
| ); | ||
|
|
||
| export function setUser(data: DBUser) { | ||
| if (data?.email) { | ||
| users[data.email] = data; | ||
| window.localStorage.setItem('db_users', JSON.stringify(users)); | ||
| return data; |
There was a problem hiding this comment.
1. db_users json.parse may throw 📘 Rule violation ⛯ Reliability
The mock DB initializes users via JSON.parse() on localStorage without guarding against malformed JSON. A corrupted db_users value would throw at import time and crash the example instead of degrading gracefully.
Agent Prompt
## Issue description
`JSON.parse()` is called on `localStorage.getItem('db_users')` without guarding against malformed JSON, which can throw during module initialization and crash the app.
## Issue Context
This is reading external/untrusted persisted data (browser storage). The compliance checklist requires explicit handling for malformed/empty inputs and avoiding unhandled exceptions.
## Fix Focus Areas
- packages/react-query-auth/examples/vite/src/mocks/db.ts[7-15]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| export type DBUser = { | ||
| email: string; | ||
| name: string; | ||
| password: string; | ||
| }; | ||
|
|
||
| const users: Record<string, DBUser> = JSON.parse( | ||
| window.localStorage.getItem('db_users') || '{}' | ||
| ); | ||
|
|
||
| export function setUser(data: DBUser) { | ||
| if (data?.email) { | ||
| users[data.email] = data; | ||
| window.localStorage.setItem('db_users', JSON.stringify(users)); | ||
| return data; | ||
| } else { | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| export function getUser(email: string | null) { |
There was a problem hiding this comment.
2. Password stored in localstorage 📘 Rule violation ⛨ Security
The mock DB persists DBUser.password in browser localStorage, which is inappropriate handling of sensitive credentials. This increases the risk of credential exposure via XSS, shared devices, or browser extensions.
Agent Prompt
## Issue description
The mock DB persists plaintext passwords in browser `localStorage` via `db_users`, which is unsafe handling of sensitive credential data.
## Issue Context
Even in examples/mocks, storing passwords client-side in plaintext normalizes insecure patterns and creates real exposure risks if reused.
## Fix Focus Areas
- packages/react-query-auth/examples/vite/src/mocks/db.ts[1-21]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| if (user && user.password === parsedBody.password) { | ||
| return HttpResponse.json({ | ||
| jwt: user.email, | ||
| user, | ||
| }) | ||
| } | ||
|
|
||
| return HttpResponse.json( | ||
| { message: 'Unauthorized' }, | ||
| { status: 401 } | ||
| ) | ||
| }), | ||
|
|
||
| http.post('/auth/register', async ({ request }) => { | ||
| const parsedBody = (await request.json()) as DBUser | ||
| const user = getUser(parsedBody?.email) | ||
|
|
||
| await delay(1000); | ||
|
|
||
| if (!user && parsedBody) { | ||
| const newUser = setUser(parsedBody) | ||
| if (newUser) { | ||
| return HttpResponse.json({ | ||
| jwt: newUser.email, | ||
| user: getUser(newUser.email), | ||
| }) | ||
| } | ||
|
|
||
| return HttpResponse.json( | ||
| { message: 'Registration failed!' }, | ||
| { status: 403 } | ||
| ) |
There was a problem hiding this comment.
3. Password included in responses 📘 Rule violation ⛨ Security
The mock auth endpoints return user objects sourced from DBUser, which includes the password field. Returning passwords to the client is a sensitive-data exposure risk and violates secure data handling expectations.
Agent Prompt
## Issue description
The mock API returns user objects that include a `password` field, exposing credentials in API responses.
## Issue Context
Responses should never include plaintext passwords. Even for mocks, return a safe user shape that omits sensitive fields.
## Fix Focus Areas
- packages/react-query-auth/examples/vite/src/mocks/api-server.ts[21-31]
- packages/react-query-auth/examples/vite/src/mocks/api-server.ts[49-52]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| http.post('/auth/logout', async () => { | ||
| storage.clearToken() | ||
|
|
||
| await delay(1000); |
There was a problem hiding this comment.
4. Registration reveals user existence 📘 Rule violation ⛨ Security
The registration endpoint returns an explicit message stating a user already exists. This leaks account existence information to clients and enables user enumeration.
Agent Prompt
## Issue description
The registration handler returns an error message that confirms whether a user account exists, which enables account enumeration.
## Issue Context
Per secure error handling guidelines, user-facing errors must be generic and not reveal sensitive internal state (including account existence).
## Fix Focus Areas
- packages/react-query-auth/examples/vite/src/mocks/api-server.ts[61-64]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
packages/critters/src/runtime.js
Outdated
| // Sanitize href to prevent script breakout | ||
| const safeHref = sanitizeAttributeValue(href); | ||
| if (safeHref !== href) { | ||
| link.setAttribute('href', safeHref); | ||
| } | ||
|
|
||
| let noscriptFallback = false; | ||
| let updateLinkToPreload = false; | ||
| const noscriptLink = link.cloneNode(false); | ||
|
|
||
| // Also remove dangerous attributes from noscriptLink and sanitize its href | ||
| dangerousAttrs.forEach(attr => noscriptLink.removeAttribute(attr)); | ||
| noscriptLink.setAttribute('href', safeHref); | ||
|
|
||
| if (preloadMode === 'body') { | ||
| document.body.appendChild(link); | ||
| } else { | ||
| if (preloadMode === 'js' || preloadMode === 'js-lazy') { | ||
| const script = document.createElement('script'); | ||
| script.setAttribute('data-href', safeHref); | ||
| script.setAttribute('data-media', sanitizeAttributeValue(media || 'all')); | ||
| const js = `${cssLoaderPreamble}$loadcss(document.currentScript.dataset.href,document.currentScript.dataset.media)`; | ||
| script.textContent = js; |
There was a problem hiding this comment.
5. Critters href scheme unsanitized 🐞 Bug ⛨ Security
Critters defines a dedicated URL-scheme sanitizer (DANGEROUS_URL_PATTERN/_sanitizeUrl) but does not apply it when building safeHref, so javascript: / data:text/html schemes are not blocked despite the presence of explicit blocking logic. This weakens the intended security hardening for href/data-href values that Critters rewrites and re-emits into the output HTML.
Agent Prompt
## Issue description
`packages/critters/src/runtime.js` defines `_sanitizeUrl()` / `DANGEROUS_URL_PATTERN` to block dangerous URL schemes, but the href rewriting path only applies `sanitizeAttributeValue()` (script-tag stripping) and never applies scheme blocking.
## Issue Context
`safeHref` is used to set `href` on rewritten `<link>` tags and as `data-href` on injected `<script>` tags for JS-based preload strategies. The intended scheme-based protection is currently ineffective.
## Fix Focus Areas
- packages/critters/src/runtime.js[31-75]
- packages/critters/src/runtime.js[451-489]
## Suggested fix
- Construct `safeHref` using both sanitizers, e.g.:
- `const safeHref = _sanitizeUrl(sanitizeAttributeValue(href))`
- If `safeHref` becomes an empty string (blocked), avoid emitting/injecting preload/js-loader markup for that stylesheet link (or fall back to a non-mutating behavior), and consider logging a warning at `warn` level.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 98bb31904e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "dependencies": { | ||
| "chalk": "^4.1.0", | ||
| "css-select": "^5.1.0", | ||
| "css-what": "^6.1.0", | ||
| "dom-serializer": "^2.0.0", | ||
| "domhandler": "^5.0.3", | ||
| "htmlparser2": "^8.0.2", | ||
| "postcss": "^8.4.38", | ||
| "postcss-media-query-parser": "^0.2.3" | ||
| }, |
There was a problem hiding this comment.
Restore critters runtime dependencies in package manifest
@opensourceframework/critters still imports external runtime modules (css-select, htmlparser2, css-what, domhandler, dom-serializer, postcss) from src/dom.js and src/css.js, but this manifest now declares only postcss-media-query-parser under dependencies. In a normal package install, those imports are unresolved and the package build/runtime path fails with module resolution errors, so these runtime deps need to be declared again (or the package must be fully bundled and verified).
Useful? React with 👍 / 👎.
| "files": [ | ||
| "dist", | ||
| "src/global.d.ts", | ||
| "README.md", | ||
| "CHANGELOG.md", | ||
| "LICENSE", | ||
| "llms.txt" | ||
| "LICENSE" |
There was a problem hiding this comment.
Ship next-images ambient image type declarations
This change drops global.d.ts from published files/exports, so consumers no longer receive the declare module '*.png'/'*.svg' ambient typings that were previously distributed with this package. Since the generated entry declarations don’t include those ambient modules, TypeScript users importing image files can regress to Cannot find module './image.png' unless they add their own declarations; keep global.d.ts shipped and exposed (or explicitly referenced from emitted types).
Useful? React with 👍 / 👎.
| } | ||
|
|
||
| // Sanitize href to prevent script breakout and dangerous URL schemes. | ||
| const safeHref = _sanitizeUrl(sanitizeAttributeValue(href)); |
There was a problem hiding this comment.
CRITICAL: The _sanitizeUrl function only checks for basic dangerous URL schemes. It should be improved to handle more edge cases like data URLs with different content types and javascript URLs with varying casing.
| if (validatePassword(email, password) && email) { | ||
| const user = getUser(email) | ||
| return HttpResponse.json({ | ||
| jwt: email, |
There was a problem hiding this comment.
WARNING: Returning email as JWT token is not secure. Use a proper JWT library to generate and verify tokens with expiration times and secret keys.
|
|
||
| const safeUser: PublicUser = { id: email, email, name }; | ||
| users[email] = safeUser; | ||
| credentials[email] = password; |
There was a problem hiding this comment.
CRITICAL: Storing plain text passwords is a severe security vulnerability. Use a secure password hashing library like bcrypt to store hashed passwords.
| ], | ||
| languageOptions: { | ||
| globals: { | ||
| vi: "readonly", |
There was a problem hiding this comment.
SUGGESTION: The vi and spyOn globals are defined but not clearly documented. Ensure these are actually used in tests before keeping them.
| "type": "github", | ||
| "url": "https://github.com/sponsors/opensourceframework" | ||
| "dependencies": { | ||
| "file-loader": "^6.2.0", |
There was a problem hiding this comment.
SUGGESTION: The file-loader and url-loader dependencies are added but not clearly used. Verify they are needed before keeping them as dependencies.
|
|
||
| const fn = fns[position]; | ||
| if (!fn) { | ||
| throw new Error("next() called with no middleware remaining"); |
There was a problem hiding this comment.
CRITICAL: The exec method throws an error when next() is called and there are no more middleware functions left. This could be improved to handle the case where next() is called without any remaining middleware, perhaps by returning undefined instead of throwing an error.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0a5e30a5bb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
eslint.config.js
Outdated
| "jest/no-try-expect": "off", | ||
| "jest/no-conditional-expect": "off", |
There was a problem hiding this comment.
Scope Jest rules to files that load the Jest plugin
The jest plugin is only registered inside the test-file scoped config block, but these jest/* rules are declared in the global rules block, so non-test files can be linted with jest/no-* configured while no jest plugin is loaded for that file. In flat config this leads to rule-resolution errors (Definition for rule 'jest/…' was not found) and can break eslint runs in packages that inherit the root config.
Useful? React with 👍 / 👎.
packages/next-images/package.json
Outdated
| "name": "@opensourceframework/next-images", | ||
| "version": "1.9.1", | ||
| "description": "Import images (jpg, jpeg, png, svg, gif, ico, webp, jp2, avif) in Next.js applications. Fork of next-images with TypeScript support.", | ||
| "version": "0.0.0", |
There was a problem hiding this comment.
Keep published package version monotonic
Resetting this package version from 1.9.1 to 0.0.0 is a release regression: consumers pinned to ^1.x will never pick up new publishes from this branch, and publishing can fail outright if 0.0.0 already exists on the registry. The fix should keep the version at or above the previously released line so upgrades remain reachable.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dfc3c7b8a1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
packages/next-csrf/package.json
Outdated
| "name": "@opensourceframework/next-csrf", | ||
| "version": "0.3.1", | ||
| "description": "CSRF protection for Next.js applications - Maintained fork of next-csrf", | ||
| "version": "0.0.0", |
There was a problem hiding this comment.
Keep next-csrf package version monotonic
This commit downgrades @opensourceframework/next-csrf from 0.3.1 to 0.0.0, which creates a release regression for consumers: projects pinned to ^0.3.x will never receive new publishes from this line, and publishing can also fail if 0.0.0 is already on the registry. Please keep the version at or above the previously released version (the same monotonic-version issue appears in several other package manifests in this commit).
Useful? React with 👍 / 👎.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
| @@ -10,19 +10,39 @@ export function hash(sess: SessionData) { | |||
|
|
|||
| export function parseTime(time: number | string): number { | |||
There was a problem hiding this comment.
WARNING: parseTime function lacks input validation for invalid time strings. If given invalid input like "abc" or "123x", it returns NaN without any error, which could cause unexpected behavior.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
|
||
| const fn = fns[position]; | ||
| if (!fn) { | ||
| throw new Error("next() called with no middleware remaining"); |
There was a problem hiding this comment.
CRITICAL: The exec method throws an error when next() is called with no middleware remaining. This breaks compatibility with existing code that might rely on calling next() at the end of the middleware chain without checking if there are more handlers left. It should instead return undefined or Promise.resolve() when there are no more middleware functions to execute.
| } | ||
|
|
||
| // Sanitize href to prevent script breakout and dangerous URL schemes. | ||
| const safeHref = _sanitizeUrl(sanitizeAttributeValue(href)); |
There was a problem hiding this comment.
CRITICAL: The _sanitizeUrl function only checks for basic dangerous URL schemes. It should also validate and sanitize URLs to prevent other XSS attacks, such as URL schemes with whitespace or other obfuscation techniques. Consider using a more robust URL validation library or implementing additional checks.
| ], | ||
| languageOptions: { | ||
| globals: { | ||
| vi: "readonly", |
There was a problem hiding this comment.
SUGGESTION: The vi and spyOn globals are defined in the test file configuration, but the vitest plugin is not installed or configured. This could cause eslint to flag usage of these globals in test files. Consider adding the eslint-plugin-vitest plugin to the eslint configuration.
|
|
||
| const fn = fns[position]; | ||
| if (!fn) { | ||
| throw new Error("next() called with no middleware remaining"); |
There was a problem hiding this comment.
CRITICAL: The exec method throws an error when next() is called with no middleware remaining. This breaks compatibility with existing code that might rely on calling next() at the end of the middleware chain without checking if there are more handlers left. It should instead return undefined or Promise.resolve() when there are no more middleware functions to execute.
| } | ||
|
|
||
| // Sanitize href to prevent script breakout and dangerous URL schemes. | ||
| const safeHref = _sanitizeUrl(sanitizeAttributeValue(href)); |
There was a problem hiding this comment.
CRITICAL: The _sanitizeUrl function only checks for basic dangerous URL schemes. It should also validate and sanitize URLs to prevent other XSS attacks, such as URL schemes with whitespace or other obfuscation techniques. Consider using a more robust URL validation library or implementing additional checks.
| ], | ||
| languageOptions: { | ||
| globals: { | ||
| vi: "readonly", |
There was a problem hiding this comment.
SUGGESTION: The vi and spyOn globals are defined in the test file configuration, but the vitest plugin is not installed or configured. This could cause eslint to flag usage of these globals in test files. Consider adding the eslint-plugin-vitest plugin to the eslint configuration.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Summary
@opensourceframework/critterstypecheck failure by separating runtime entry (runtime.js/.d.ts) from TS export entrynext-connect(next()past chain and double-next()now throw) and add regression testsreact-query-authexample API helpers:localStoragenext-sessiontest suite compatibility issues from previous migration (Vitest imports, stable assertions, callback-to-promise updates)react-query-authhook test expectations for current mutation signatureValidation
pnpm typecheck✅pnpm --filter @opensourceframework/critters typecheck✅pnpm --filter @opensourceframework/next-connect test✅pnpm --filter @opensourceframework/next-session test✅pnpm --filter @opensourceframework/react-query-auth test✅Notes
pnpm teststill surfaces pre-existing failures in unrelated packages (react-virtualized/next-iron-session) that are outside this PR scope and reproduce independently of these changes.