Skip to content

fix: Bounty: Deeplinks support + Raycast Extension#1721

Open
gugli4ifenix-design wants to merge 4 commits intoCapSoftware:mainfrom
gugli4ifenix-design:bounty-fix-1775753185266
Open

fix: Bounty: Deeplinks support + Raycast Extension#1721
gugli4ifenix-design wants to merge 4 commits intoCapSoftware:mainfrom
gugli4ifenix-design:bounty-fix-1775753185266

Conversation

@gugli4ifenix-design
Copy link
Copy Markdown

@gugli4ifenix-design gugli4ifenix-design commented Apr 9, 2026

🔍 Fix: Bounty: Deeplinks support + Raycast Extension

This PR addresses the issue with the following changes:

  • packages/utils/src/deeplinks.ts
  • packages/utils/src/__tests__/deeplinks.test.ts
  • packages/web-api-contract-effect/deeplink-handler.ts
  • packages/web-api-contract-effect/__tests__/deeplink-handler.test.ts

Solved with AI assistance (JARVIS Bounty Solver)

💎 TON Wallet: UQD-1AZ0R84E2B_PW-Aozdj8pikmV79dr5VZKka51PvOxtcS

Greptile Summary

This PR introduces a TypeScript deeplink utility layer (parseDeeplink, createDeeplink, DeeplinkBuilder, DeeplinkActions) in @cap/utils and a DeeplinkHandler dispatcher class in packages/web-api-contract-effect, along with tests for both. There are three blocking issues: the new deeplinks.ts module is not re-exported from packages/utils/src/index.ts (making the @cap/utils imports in deeplink-handler.ts unresolvable), parseDeeplink checks startsWith before trimming so whitespace-padded URLs always return null, and the deeplink-handler test file is truncated mid-string and will not parse. Additionally, the new cap://record/cap://stop URL scheme is incompatible with the existing Rust Tauri handler which expects cap://action?value=<json> — the desktop app will not dispatch any of these new deeplinks.

Confidence Score: 3/5

Not safe to merge — multiple P1 defects prevent build resolution and test execution, and the deeplink scheme is incompatible with the existing Tauri handler.

Three P1 issues: missing package export (causes import failure at build time), trim-before-startsWith bug (breaks the stated trim behavior and its test), and a truncated test file (parse error). Additionally, the introduced URL scheme conflicts with the existing Rust deeplink handler, meaning no new deeplink would actually reach the desktop app. These collectively block the feature from working end-to-end.

packages/utils/src/index.ts (missing export), packages/utils/src/deeplinks.ts (trim bug + comments), packages/web-api-contract-effect/tests/deeplink-handler.test.ts (truncated file), apps/desktop/src-tauri/src/deeplink_actions.rs (incompatible scheme)

Vulnerabilities

No security concerns identified. Deeplink parsing is purely structural (string splitting, known-action allowlist validation), no credentials or sensitive data flow through these utilities.

Important Files Changed

Filename Overview
packages/utils/src/deeplinks.ts New deeplink parsing/creation utilities; contains a whitespace-trim bug (startsWith check precedes trim), prohibited code comments, and is not exported from the package index.
packages/utils/src/tests/deeplinks.test.ts Comprehensive unit tests for deeplinks utilities; the trim whitespace test will fail due to the bug in the implementation.
packages/web-api-contract-effect/deeplink-handler.ts New DeeplinkHandler class dispatching parsed deeplinks to callbacks; uses an incompatible URL scheme vs. the existing Rust Tauri handler and imports from @cap/utils that aren't yet exported.
packages/web-api-contract-effect/tests/deeplink-handler.test.ts Deeplink handler tests; file is truncated mid-string (ends at 'cap:// with no closing syntax), which will cause a parse error at test time.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["cap:// URL (e.g. cap://record)"] --> B["parseDeeplink (packages/utils)"]
    B --> C{Valid prefix & action?}
    C -- No --> D["return null"]
    C -- Yes --> E["DeeplinkParams { action, ...queryParams }"]
    E --> F["DeeplinkHandler.handle (web-api-contract-effect)"]
    F --> G{action}
    G -- record --> H["onStartRecording()"]
    G -- stop --> I["onStopRecording()"]
    G -- pause --> J["onPauseRecording()"]
    G -- resume --> K["onResumeRecording()"]
    G -- switch-microphone --> L["onSwitchMicrophone(deviceId)"]
    G -- switch-camera --> M["onSwitchCamera(deviceId)"]
    G -- unknown --> N["return false"]
    subgraph existing ["Existing Tauri Handler (incompatible scheme)"]
        O["cap://action?value={json}"] --> P["deeplink_actions::handle (Rust)"]
        P --> Q["DeepLinkAction::execute"]
    end
Loading

Comments Outside Diff (1)

  1. packages/utils/src/index.ts, line 5 (link)

    P1 New deeplinks module not re-exported from the package

    packages/utils/src/deeplinks.ts is never added to packages/utils/src/index.ts, so nothing that imports @cap/utils can reach parseDeeplink, createDeeplink, DeeplinkActions, etc. In particular, packages/web-api-contract-effect/deeplink-handler.ts imports parseDeeplink from @cap/utils and will fail to resolve it at build time.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/utils/src/index.ts
    Line: 5
    
    Comment:
    **New `deeplinks` module not re-exported from the package**
    
    `packages/utils/src/deeplinks.ts` is never added to `packages/utils/src/index.ts`, so nothing that imports `@cap/utils` can reach `parseDeeplink`, `createDeeplink`, `DeeplinkActions`, etc. In particular, `packages/web-api-contract-effect/deeplink-handler.ts` imports `parseDeeplink` from `@cap/utils` and will fail to resolve it at build time.
    
    
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: packages/utils/src/deeplinks.ts
Line: 36-40

Comment:
**Trim applied after prefix check, breaking whitespace test**

The URL is checked with `startsWith` before it is trimmed. `'  cap://pause  '.startsWith('cap://')` evaluates to `false`, so the function returns `null`. The `trim()` on `urlPart` only runs on the slice that follows the prefix — it never gets there for padded inputs. The accompanying test `'should trim whitespace from URL'` will always fail.

```suggestion
    const trimmedUrl = url.trim();
    if (!trimmedUrl.startsWith(DEEPLINK_PREFIX)) {
      return null;
    }

    const urlPart = trimmedUrl.slice(DEEPLINK_PREFIX.length);
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/utils/src/index.ts
Line: 5

Comment:
**New `deeplinks` module not re-exported from the package**

`packages/utils/src/deeplinks.ts` is never added to `packages/utils/src/index.ts`, so nothing that imports `@cap/utils` can reach `parseDeeplink`, `createDeeplink`, `DeeplinkActions`, etc. In particular, `packages/web-api-contract-effect/deeplink-handler.ts` imports `parseDeeplink` from `@cap/utils` and will fail to resolve it at build time.

```suggestion
export * from "./constants/plans.ts";
export * from "./helpers.ts";
export * from "./lib/dub.ts";
export * from "./lib/stripe/stripe.ts";
export * from "./types/database.ts";
export * from "./deeplinks.ts";
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/web-api-contract-effect/__tests__/deeplink-handler.test.ts
Line: 40-41

Comment:
**Test file is truncated — file ends mid-string**

The file ends abruptly at `handler.handle('cap://` with no closing quote, parenthesis, or block. The test for the `pause` action and any tests that follow are completely missing. The file also lacks a final newline. This will cause a parse error when the test runner loads it.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/utils/src/deeplinks.ts
Line: 17

Comment:
**Code comments violate project conventions**

The project prohibits all code comments (see CLAUDE.md: "CRITICAL: NO CODE COMMENTS"). This file contains three inline comments (`// Validate action against known types`, `// Filter out undefined/empty values`, `// Builder pattern for fluent API`) as well as a `// Invalid query string, continue with parsed params` comment inside a catch block. All of these must be removed; the code should be self-explanatory through naming alone.

**Context Used:** CLAUDE.md ([source](https://app.greptile.com/review/custom-context?memory=9a906542-f1fe-42c1-89a2-9f252d96d9f0))

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/web-api-contract-effect/deeplink-handler.ts
Line: 1-10

Comment:
**Incompatible deeplink scheme with the existing Tauri handler**

The existing Rust handler in `apps/desktop/src-tauri/src/deeplink_actions.rs` uses a `cap://action?value=<json>` scheme (e.g. `cap://action?value={"stop_recording":null}`), resolving actions via the URL *domain* `"action"`. This PR introduces a parallel scheme where the action is the domain itself (`cap://record`, `cap://stop`, etc.), producing a different URL format that the Rust handler will reject as `ActionParseFromUrlError::NotAction`. The two schemas are mutually incompatible; without updating the Rust handler (or replacing it), none of the new deeplinks will be dispatched to the desktop app.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix: Bounty: Deeplinks support + Raycast..." | Re-trigger Greptile

Greptile also left 4 inline comments on this PR.

(2/5) Greptile learns from your feedback when you react with thumbs up/down!

Context used:

  • Context used - CLAUDE.md (source)

Comment on lines +36 to +40
if (!url.startsWith(DEEPLINK_PREFIX)) {
return null;
}

const urlPart = url.slice(DEEPLINK_PREFIX.length).trim();
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.

P1 Trim applied after prefix check, breaking whitespace test

The URL is checked with startsWith before it is trimmed. ' cap://pause '.startsWith('cap://') evaluates to false, so the function returns null. The trim() on urlPart only runs on the slice that follows the prefix — it never gets there for padded inputs. The accompanying test 'should trim whitespace from URL' will always fail.

Suggested change
if (!url.startsWith(DEEPLINK_PREFIX)) {
return null;
}
const urlPart = url.slice(DEEPLINK_PREFIX.length).trim();
const trimmedUrl = url.trim();
if (!trimmedUrl.startsWith(DEEPLINK_PREFIX)) {
return null;
}
const urlPart = trimmedUrl.slice(DEEPLINK_PREFIX.length);
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/utils/src/deeplinks.ts
Line: 36-40

Comment:
**Trim applied after prefix check, breaking whitespace test**

The URL is checked with `startsWith` before it is trimmed. `'  cap://pause  '.startsWith('cap://')` evaluates to `false`, so the function returns `null`. The `trim()` on `urlPart` only runs on the slice that follows the prefix — it never gets there for padded inputs. The accompanying test `'should trim whitespace from URL'` will always fail.

```suggestion
    const trimmedUrl = url.trim();
    if (!trimmedUrl.startsWith(DEEPLINK_PREFIX)) {
      return null;
    }

    const urlPart = trimmedUrl.slice(DEEPLINK_PREFIX.length);
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +40 to +41

const result = await handler.handle('cap:// No newline at end of file
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.

P1 Test file is truncated — file ends mid-string

The file ends abruptly at handler.handle('cap:// with no closing quote, parenthesis, or block. The test for the pause action and any tests that follow are completely missing. The file also lacks a final newline. This will cause a parse error when the test runner loads it.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/web-api-contract-effect/__tests__/deeplink-handler.test.ts
Line: 40-41

Comment:
**Test file is truncated — file ends mid-string**

The file ends abruptly at `handler.handle('cap://` with no closing quote, parenthesis, or block. The test for the `pause` action and any tests that follow are completely missing. The file also lacks a final newline. This will cause a parse error when the test runner loads it.

How can I resolve this? If you propose a fix, please make it concise.


export const DEEPLINK_PREFIX = 'cap://';

// Validate action against known types
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.

P2 Code comments violate project conventions

The project prohibits all code comments (see CLAUDE.md: "CRITICAL: NO CODE COMMENTS"). This file contains three inline comments (// Validate action against known types, // Filter out undefined/empty values, // Builder pattern for fluent API) as well as a // Invalid query string, continue with parsed params comment inside a catch block. All of these must be removed; the code should be self-explanatory through naming alone.

Context Used: CLAUDE.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/utils/src/deeplinks.ts
Line: 17

Comment:
**Code comments violate project conventions**

The project prohibits all code comments (see CLAUDE.md: "CRITICAL: NO CODE COMMENTS"). This file contains three inline comments (`// Validate action against known types`, `// Filter out undefined/empty values`, `// Builder pattern for fluent API`) as well as a `// Invalid query string, continue with parsed params` comment inside a catch block. All of these must be removed; the code should be self-explanatory through naming alone.

**Context Used:** CLAUDE.md ([source](https://app.greptile.com/review/custom-context?memory=9a906542-f1fe-42c1-89a2-9f252d96d9f0))

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +1 to +10
import { DeeplinkParams, parseDeeplink, DeeplinkAction } from '@cap/utils';

export interface DeeplinkHandlerContext {
onStartRecording?: () => void | Promise<void>;
onStopRecording?: () => void | Promise<void>;
onPauseRecording?: () => void | Promise<void>;
onResumeRecording?: () => void | Promise<void>;
onSwitchMicrophone?: (deviceId: string) => void | Promise<void>;
onSwitchCamera?: (deviceId: string) => void | Promise<void>;
onError?: (error: Error) => void;
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.

P2 Incompatible deeplink scheme with the existing Tauri handler

The existing Rust handler in apps/desktop/src-tauri/src/deeplink_actions.rs uses a cap://action?value=<json> scheme (e.g. cap://action?value={"stop_recording":null}), resolving actions via the URL domain "action". This PR introduces a parallel scheme where the action is the domain itself (cap://record, cap://stop, etc.), producing a different URL format that the Rust handler will reject as ActionParseFromUrlError::NotAction. The two schemas are mutually incompatible; without updating the Rust handler (or replacing it), none of the new deeplinks will be dispatched to the desktop app.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/web-api-contract-effect/deeplink-handler.ts
Line: 1-10

Comment:
**Incompatible deeplink scheme with the existing Tauri handler**

The existing Rust handler in `apps/desktop/src-tauri/src/deeplink_actions.rs` uses a `cap://action?value=<json>` scheme (e.g. `cap://action?value={"stop_recording":null}`), resolving actions via the URL *domain* `"action"`. This PR introduces a parallel scheme where the action is the domain itself (`cap://record`, `cap://stop`, etc.), producing a different URL format that the Rust handler will reject as `ActionParseFromUrlError::NotAction`. The two schemas are mutually incompatible; without updating the Rust handler (or replacing it), none of the new deeplinks will be dispatched to the desktop app.

How can I resolve this? If you propose a fix, please make it concise.

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