Skip to content

fix(devtools): rework timeline function wrapping to run after key injection - #1028

Open
cernymatej wants to merge 8 commits into
nuxt:mainfrom
cernymatej:fix/timeline-function-wrapping
Open

cernymatej wants to merge 8 commits into
nuxt:mainfrom
cernymatej:fix/timeline-function-wrapping

Conversation

@cernymatej

@cernymatej cernymatej commented Jul 20, 2026 •

Copy link
Copy Markdown
Member

🔗 Linked issue

fix #941

📚 Description

this aims to fix the issues with wrapping functions with __nuxtTimelineWrap after the changes in Nuxt that made function key injection stricter (and more accurate): nuxt/nuxt#33446

the main issue was that we wrapped functions before the key injection plugin transformed the code... as a result, the plugin only saw local variable declarations instead of imports and bailed out without injecting function keys

another issue, reported in nuxt/nuxt#34934, was that it attempted to wrap new compiler macros, even though they're not meant to be called that way

this implementation fixes both of these issues and improves on the original implementation by supporting explicitly imported functions too, where the old one only wrapped auto-imported functions

@cernymatej
cernymatej force-pushed the fix/timeline-function-wrapping branch from 095b589 to d10cb86 Compare July 20, 2026 19:43
@coderabbitai

coderabbitai Bot commented Jul 20, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: d00df10a-b1ea-422f-b6bf-b6645725cbe9

📥 Commits

Reviewing files that changed from the base of the PR and between 33fc50c and 808219d.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (8)
  • packages/devtools/src/integrations/timeline-wrap.ts
  • packages/devtools/src/integrations/timeline.ts
  • packages/devtools/test/timeline-wrap.test.ts
  • playgrounds/tab-timeline/composables/useApiData.ts
  • playgrounds/tab-timeline/pages/keyed.vue
  • playgrounds/tab-timeline/server/api/data.ts
  • pnpm-workspace.yaml
  • tests/e2e/specs/playground-tab-timeline.spec.ts

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The timeline integration now wraps eligible imports in a post-build unplugin transform after key injection. A new utility handles static import bindings, helper injection, and source maps. Vitest and Playwright coverage validate wrapping, filtering, keyed composables, and generated markers. The timeline playground adds a keyed page and enables timeline support. Package and workspace configuration adds dependencies and test scripts.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 80821

The change moves timeline wrapping into a post-transform and adds keyed-composable coverage. No concrete merge-blocking failure was established; mergeability remains subject to normal build and test checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 80821

The change remains development-only and preserves configured import filtering. Recording is initially disabled, but enabling it can now capture arguments from additional explicitly imported functions. No introduced security vulnerability was established; build-order guarantees and broader deployment exposure remain incompletely verified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The demonstrated exposure is additional eligible function calls within a development application's browser metrics state. The normal path does not extend instrumentation to production builds or server-side capture. External plugin consumers and downstream metrics-reader exposure remain incompletely established.

Trust Boundaries and Controls

  • observed — The internal integration supplies the helper dependency and eligible import inventory. Exact source/export matching limits rewriting, while filters exclude virtual modules, dependencies, macro queries, non-script Vue queries, and the DevTools runtime directory. Namespace imports remain unchanged.

Resilience and Maintainability Implications

  • observed — The unchanged enabled-recording helper appends an event before invoking the function. A synchronous throw leaves the event without an end timestamp, and promise tracking creates an uncaught derived rejection chain. Additional explicit-import callers can exercise these existing behaviors, but no material security or service-level failure-containment impact was established.

Hardening Proposals

  • proposed — Consider making expanded explicit-import capture visible to users who already have recording enabled, and offering argument redaction or narrower capture selection for sensitive development workflows. This is a privacy-hardening proposal, not an observed disclosure finding.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 7 files. (2 skipped: 2… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: reworking timeline function wrapping so it runs after key injection.
Description check ✅ Passed The description directly explains the key-injection failure, macro-wrapping issue, and support for explicitly imported functions. It matches the changeset and references relevant issues.
Linked Issues check ✅ Passed Issue #941 requires timeline wrapping to avoid breaking function key injection. The PR replaces the earlier import rewrite with a post-transform TimelineWrapPlugin. The wrapper preserves import spec…
Out of Scope Changes check ✅ Passed The changed source, dependency, unit-test, playground, and end-to-end files support the timeline wrapping fix in issue #941. Macro exclusions and explicit-import handling protect the same wrapping beh…
Full details: Docstring Coverage

Explanation

Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 7 files. (2 skipped: 2 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/devtools/vitest.config.ts (1)

1-7: 🧹 Nitpick | 🔵 Trivial

Wire the new test suite into CI before merge.

Per the PR summary, tests were added but aren't yet configured in CI. This config also runs the 60s-timeout e2e suite (timeline.e2e.test.ts, which boots a real dev server) together with fast unit tests under the same vitest run invocation — consider splitting them (e.g. separate include globs/projects or a dedicated test:e2e script) once CI is wired up, so unit-test feedback isn't gated on booting a Nuxt dev server.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools/vitest.config.ts` around lines 1 - 7, Wire the new Vitest
suite into the repository’s CI workflow so it runs before merges. In the test
configuration around defineConfig, separate the fast unit tests from
timeline.e2e.test.ts into distinct globs, projects, or scripts, and ensure CI
invokes the unit suite independently so it is not gated by the real dev-server
e2e test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/devtools/src/integrations/timeline.ts`:
- Around line 87-104: Update the resolveId handling in the bySource processing
block to invoke ctx.resolveId(from) inside a promise-safe boundary so
synchronous throws become rejected resolutions handled as undefined. Ensure
resolvedFrom caches that safely handled promise and Promise.all cannot reject
from a resolver throw, preserving normal mapping behavior for successful
resolutions.

---

Nitpick comments:
In `@packages/devtools/vitest.config.ts`:
- Around line 1-7: Wire the new Vitest suite into the repository’s CI workflow
so it runs before merges. In the test configuration around defineConfig,
separate the fast unit tests from timeline.e2e.test.ts into distinct globs,
projects, or scripts, and ensure CI invokes the unit suite independently so it
is not gated by the real dev-server e2e test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 04b140c7-e9b9-4e43-a2cc-3271b7c282b2

📥 Commits

Reviewing files that changed from the base of the PR and between 0c94730 and d10cb86.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (11)
  • packages/devtools/package.json
  • packages/devtools/src/integrations/timeline-wrap.ts
  • packages/devtools/src/integrations/timeline.ts
  • packages/devtools/test/fixtures/timeline/app.vue
  • packages/devtools/test/fixtures/timeline/nuxt.config.ts
  • packages/devtools/test/timeline-wrap.test.ts
  • packages/devtools/test/timeline.e2e.test.ts
  • packages/devtools/vitest.config.ts
  • playgrounds/tab-timeline/components/GlobalNav.vue
  • playgrounds/tab-timeline/pages/keyed.vue
  • pnpm-workspace.yaml

Comment thread packages/devtools/src/integrations/timeline.ts Outdated
cernymatej and others added 7 commits July 20, 2026 22:10
…acros

Drop the stale `keyedComposableFactories` cast (the schema types now
declare it) and the unimport `resolveId` re-keying, which Nuxt never
configures. Move the plugin definition next to the wrapper so its id and
code filters are exercised through unplugin in unit tests instead of a
re-implementation, and keep a single re-wrap guard.

Remove the per-package vitest config, script and devDependency: the root
config already runs `packages/**/test`.

Playground: add a `createUseFetch` composable (nuxt/nuxt#34934) and assert
it stays unwrapped while every keyed call still receives a key. Fix the
`/api/data` route, which relied on Nitro auto-imports that no longer apply.
Removing the per-import-list cache and the `exclude` option leaves
behaviour unchanged under the unit and e2e suites: recomputing the
wrappable set is a filter over the import list, and the only exclusion
ever passed was our own runtime dir, which the plugin now owns.

Kept after ablation because the suites fail without them: registering
from `modules:done`, `enforce: 'post'`, and the keyed-function-factory
exclusion. Kept although unobservable here: the runtime-dir exclude
(otherwise DevTools' own `useState`/`useRouter` calls land in the user's
timeline) and the id/code filters mirroring Nuxt's compiler plugins.
…-wrapping

# Conflicts:
#	pnpm-lock.yaml
#	pnpm-workspace.yaml

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: Devtools timeline breaks function key injection

2 participants