Skip to content

deeplink2 typesafe path#1868

Merged
yujonglee merged 2 commits intomainfrom
yl-branch-49
Nov 25, 2025
Merged

deeplink2 typesafe path#1868
yujonglee merged 2 commits intomainfrom
yl-branch-49

Conversation

@yujonglee
Copy link
Contributor

@yujonglee yujonglee commented Nov 25, 2025

Summary

Refactors the deeplink2 plugin to provide type-safe path handling with compile-time validation that deep link paths match valid application routes.

Key changes:

  • Removed command exports (ping, get_available_deep_links) and extension trait (DeepLink2PluginExt)
  • Restructured types module from single file to multi-file structure with notification-focused variants
  • Added FromStr implementation for URL parsing with query parameter validation
  • Added new /notification route that redirects to /app/main
  • Added compile-time type assertion in __root.tsx to ensure DeepLink["to"] matches valid routes

Review & Testing Checklist for Human

  • CRITICAL: TypeScript bindings mismatch - The generated bindings.gen.ts shows OnboardingSearch/OnboardingStep types but Rust code defines NotificationSearch. Verify bindings are regenerated correctly (cargo test in plugins/deeplink2 should regenerate)
  • Verify the compile-time type assertion (0 as DeepLink["to"] extends NonNullable<LinkProps["to"]>...) actually catches invalid routes at build time
  • Test deep link parsing with hyprnote://notification?key=test - should parse correctly
  • Test deep link parsing without required key param - should return MissingQueryParam error
  • Confirm /notification route redirects to /app/main as expected

Recommended test plan:

  1. Run pnpm -F desktop tauri dev and verify app builds without type errors
  2. Trigger a deep link to /notification?key=test and verify redirect behavior
  3. Check that removing/changing the /notification route causes a compile error due to the type assertion

Notes

This is a breaking change that removes the DeepLink2PluginExt trait and associated commands. Any code depending on parse_deep_link() or get_available_deep_links() will need updates.

Link to Devin run: https://app.devin.ai/sessions/c57f639984e64d2a8d1dc55be07cc869
Requested by: yujonglee (@yujonglee)

@netlify
Copy link

netlify bot commented Nov 25, 2025

Deploy Preview for hyprnote-storybook ready!

Name Link
🔨 Latest commit 11dc37a
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote-storybook/deploys/69252f0a169a31000868c143
😎 Deploy Preview https://deploy-preview-1868--hyprnote-storybook.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Nov 25, 2025

Deploy Preview for hyprnote ready!

Name Link
🔨 Latest commit 11dc37a
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote/deploys/69252f0ad4d5340008c77631
😎 Deploy Preview https://deploy-preview-1868--hyprnote.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 25, 2025

📝 Walkthrough

Walkthrough

Removed old deeplink types, commands, and extension trait; introduced a new notification-focused DeepLink parsing surface (requiring a "key" query param) with an event wrapper; updated plugin exports/specta config; added desktop route /notification that redirects to /app/main; added a compile-time link compatibility assertion.

Changes

Cohort / File(s) Summary
Desktop route changes
apps/desktop/src/routes/__root.tsx, apps/desktop/src/routes/notification.tsx
Added type imports and a compile-time assertion ensuring DeepLink["to"] is compatible with LinkProps["to"]; added a new /notification file route that immediately redirects to /app/main in beforeLoad.
Deeplink plugin command removal
plugins/deeplink2/build.rs, plugins/deeplink2/src/commands.rs
Removed Tauri command implementations (ping, get_available_deep_links) and cleared the COMMANDS array so no commands are registered.
Deeplink plugin trait removal
plugins/deeplink2/src/ext.rs
Deleted DeepLink2PluginExt trait and its impl (methods parse_deep_link and get_available_deep_links), removing the manager extension API.
Deeplink plugin types refactor
plugins/deeplink2/src/types.rs (removed), plugins/deeplink2/src/types/mod.rs, plugins/deeplink2/src/types/notification.rs
Replaced prior DeepLink/Auth/DeepLinkInfo definitions with a new DeepLink enum focused on Notification(NotificationSearch), added NotificationSearch struct, implemented FromStr parsing that requires key for notification path, and added DeepLinkEvent wrapper.
Deeplink plugin errors
plugins/deeplink2/src/error.rs
Added MissingQueryParam(String) error variant for missing required query parameters.
Deeplink plugin top-level exports
plugins/deeplink2/src/lib.rs
Removed commands, ext, and old types module declarations and re-exports; removed the prior events wrapper; updated specta builder to have no commands, to use new types::DeepLinkEvent, and to register types::DeepLink.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant DesktopApp as Desktop App
    participant DeeplinkPlugin as Deeplink Plugin

    Client->>DesktopApp: Navigate to /notification
    DesktopApp->>DesktopApp: beforeLoad hook runs
    DesktopApp->>DesktopApp: Redirect to /app/main

    Note over DeeplinkPlugin: DeepLink parsing flow (new)
    Client->>DeeplinkPlugin: Send deeplink URL (e.g., hypr://notification?key=...)
    DeeplinkPlugin->>DeeplinkPlugin: Parse URL (FromStr)
    alt Path == "notification"
        DeeplinkPlugin->>DeeplinkPlugin: Require "key" query param
        alt "key" present
            DeeplinkPlugin->>DeeplinkPlugin: Emit DeepLinkEvent( Notification(NotificationSearch) )
        else "key" missing
            DeeplinkPlugin-->>Client: Return MissingQueryParam error
        end
    else unknown path
        DeeplinkPlugin-->>Client: Return UnknownPath error
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • Module/export changes in plugins/deeplink2/src/lib.rs and Specta builder adjustments.
    • Correctness of the new FromStr URL parsing and query-param validation in types/mod.rs.
    • Impact of removing DeepLink2PluginExt and Tauri commands on downstream consumers and call sites.
    • Ensure new error variant MissingQueryParam is handled where parsing is invoked.

Possibly related PRs

Suggested reviewers

  • yujonglee

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to evaluate whether the description relates to the changeset. Add a pull request description explaining the motivation, approach, and impact of the typesafe path refactoring for the deeplink2 plugin.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'deeplink2 typesafe path' clearly summarizes the main objective: refactoring the deeplink2 plugin to use typesafe path handling, evidenced by removal of legacy type structures and commands, and introduction of a new type-safe DeepLink enum focused on notification paths.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch yl-branch-49

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 568487a and 11dc37a.

📒 Files selected for processing (2)
  • plugins/deeplink2/src/types/mod.rs (1 hunks)
  • plugins/deeplink2/src/types/notification.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • plugins/deeplink2/src/types/notification.rs
  • plugins/deeplink2/src/types/mod.rs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote
  • GitHub Check: Redirect rules - hyprnote-storybook
  • GitHub Check: Header rules - hyprnote-storybook
  • GitHub Check: Pages changed - hyprnote-storybook
  • GitHub Check: fmt
  • GitHub Check: ci (linux, depot-ubuntu-24.04-8)
  • GitHub Check: ci (macos, macos-15)
  • GitHub Check: ci (linux, depot-ubuntu-22.04-8)

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fa8ab98 and 568487a.

⛔ Files ignored due to path filters (2)
  • apps/desktop/src/routeTree.gen.ts is excluded by !**/*.gen.ts
  • plugins/deeplink2/js/bindings.gen.ts is excluded by !**/*.gen.ts
📒 Files selected for processing (10)
  • apps/desktop/src/routes/__root.tsx (1 hunks)
  • apps/desktop/src/routes/notification.tsx (1 hunks)
  • plugins/deeplink2/build.rs (1 hunks)
  • plugins/deeplink2/src/commands.rs (0 hunks)
  • plugins/deeplink2/src/error.rs (1 hunks)
  • plugins/deeplink2/src/ext.rs (0 hunks)
  • plugins/deeplink2/src/lib.rs (1 hunks)
  • plugins/deeplink2/src/types.rs (0 hunks)
  • plugins/deeplink2/src/types/mod.rs (1 hunks)
  • plugins/deeplink2/src/types/notification.rs (1 hunks)
💤 Files with no reviewable changes (3)
  • plugins/deeplink2/src/commands.rs
  • plugins/deeplink2/src/ext.rs
  • plugins/deeplink2/src/types.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/desktop/src/routes/__root.tsx
  • apps/desktop/src/routes/notification.tsx
🧠 Learnings (3)
📚 Learning: 2025-11-24T16:32:19.706Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T16:32:19.706Z
Learning: Applies to **/*.{ts,tsx} : Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props. Just inline them.

Applied to files:

  • apps/desktop/src/routes/__root.tsx
📚 Learning: 2025-11-24T16:32:23.036Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T16:32:23.036Z
Learning: Applies to **/*.{ts,tsx} : Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.

Applied to files:

  • apps/desktop/src/routes/__root.tsx
📚 Learning: 2025-11-24T16:32:19.706Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T16:32:19.706Z
Learning: Applies to **/*.{ts,tsx} : Use `motion/react` instead of `framer-motion`.

Applied to files:

  • apps/desktop/src/routes/__root.tsx
🧬 Code graph analysis (2)
plugins/deeplink2/src/types/mod.rs (1)
plugins/deeplink2/src/lib.rs (1)
  • tauri_specta (9-13)
apps/desktop/src/routes/notification.tsx (1)
apps/desktop/src/routes/__root.tsx (1)
  • Route (22-26)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote
  • GitHub Check: fmt
  • GitHub Check: ci (linux, depot-ubuntu-22.04-8)
  • GitHub Check: ci (linux, depot-ubuntu-24.04-8)
  • GitHub Check: ci (macos, macos-15)
🔇 Additional comments (7)
plugins/deeplink2/build.rs (1)

1-4: LGTM!

The empty COMMANDS slice aligns with the removal of the commands module from the plugin.

apps/desktop/src/routes/notification.tsx (1)

3-7: Query parameters are lost during redirect.

The /notification deep link expects a key query parameter (as defined in NotificationSearch), but the redirect discards all search params. If the notification key needs to be preserved, pass it through:

 export const Route = createFileRoute("/notification")({
-  beforeLoad: async () => {
-    throw redirect({ to: "/app/main" });
+  beforeLoad: async ({ search }) => {
+    throw redirect({ to: "/app/main", search });
   },
 });

If the key is intentionally discarded (e.g., processed elsewhere before this redirect), please disregard.

apps/desktop/src/routes/__root.tsx (1)

18-20: LGTM!

Clever compile-time assertion ensuring DeepLink["to"] always maps to a valid router path. This will catch mismatches at build time.

plugins/deeplink2/src/error.rs (1)

13-14: LGTM!

The new MissingQueryParam variant follows the existing error pattern and integrates well with the FromStr implementation for DeepLink.

plugins/deeplink2/src/types/mod.rs (2)

1-19: LGTM!

The module structure is clean. The DeepLinkEvent wrapper integrates properly with tauri_specta, and the serde tagging produces a clear JSON structure.


21-49: LGTM with minor note.

The FromStr implementation correctly parses deep link URLs. Note that using HashMap for query params means duplicate keys will keep the last value only—this is typically acceptable behavior.

One minor suggestion: consider adding #[inline] to this implementation if it's called frequently in hot paths, though this is optional.

plugins/deeplink2/src/lib.rs (1)

11-13: Code changes verified — all type definitions properly configured.

Verification confirms all types referenced in the specta builder are correctly defined with the necessary derives for tauri-specta integration:

  • DeepLinkEvent (line 12 of types/mod.rs): Struct with tauri_specta::Event derive ✓
  • DeepLink (lines 15-19 of types/mod.rs): Enum with Serialize, Deserialize, Type derives ✓
  • NotificationSearch (notification.rs): Properly defined and exported ✓

The builder configuration at lines 11-13 is correct: empty commands list, proper event binding, and type export via .typ::<>().

…2 types

Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
@yujonglee yujonglee merged commit f0d80d9 into main Nov 25, 2025
12 of 13 checks passed
@yujonglee yujonglee deleted the yl-branch-49 branch November 25, 2025 04:40
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.

1 participant