Skip to content

fix: replace try-catch with @praha/byethrow Result type for functional error handling - #221

Merged
ryoppippi merged 8 commits into
mainfrom
byethrow
Jul 2, 2025
Merged

fix: replace try-catch with @praha/byethrow Result type for functional error handling#221
ryoppippi merged 8 commits into
mainfrom
byethrow

Conversation

@ryoppippi

@ryoppippi ryoppippi commented Jun 29, 2025

Copy link
Copy Markdown
Member

Summary

Add @praha/byethrow dependency and replace traditional try-catch blocks with Result.try() for functional error handling across 11 locations in 5 files.

Key Changes

  • Add @praha/byethrow@^0.3.0 dependency
  • Convert JSON parsing try-catch to Result.try() patterns
  • Replace network operation error handling with functional approach
  • Use Result.isFailure() for better readability
  • Document error handling guidelines in CLAUDE.md

Files Modified

  • src/debug.ts - JSON parsing error handling
  • src/data-loader.ts - Data loading error handling (3 locations)
  • src/pricing-fetcher.ts - Network operations (2 locations)
  • src/_live-monitor.ts - Live monitoring (2 locations)
  • src/commands/_blocks.live.ts - Monitoring loop (1 location)

Benefits

  • More functional and composable error handling
  • Better type safety with explicit Success/Failure types
  • Clearer error handling patterns
  • All 210 tests pass with no behavioral changes

Test Status

✅ TypeScript compilation succeeds
✅ All tests pass (210/210)
✅ ESLint formatting applied

Summary by CodeRabbit

  • New Features

    • Added support for the @praha/byethrow package and related configuration for enhanced error handling.
  • Refactor

    • Replaced traditional try-catch blocks with a structured Result pattern for error handling across multiple modules, improving code clarity and reliability.
    • Updated statistics tracking logic for improved consistency and structure.
  • Documentation

    • Added new guidelines on functional error handling using the Result type to the code style notes.
  • Chores

    • Updated development dependencies to include @praha/byethrow and @praha/byethrow-mcp.

Add @praha/byethrow as devDependency for functional error handling to replace traditional try-catch patterns
…throw Result type

Replace JSON.parse try-catch blocks with Result.try() for better functional error handling in main data processing loops
…type

Replace monitoring loop try-catch with Result.try() for better functional error handling in live monitoring command
…ethrow Result type

Replace JSON.parse try-catch with Result.try() for better functional error handling while keeping Promise.catch() for async file operations
Properly use Result.try() with async file reading by creating a wrapped function and calling it, rather than reverting to Promise.catch()
- Replace \!Result.isSuccess with more readable Result.isFailure

- Fix Result.try function call pattern by creating wrapper function and calling it

- Use early return pattern (continue) instead of ternary operators for better readability

- All tests pass and TypeScript compilation succeeds
Document the preference for using @praha/byethrow Result type over traditional try-catch patterns with best practices and usage guidelines
@coderabbitai

coderabbitai Bot commented Jun 29, 2025

Copy link
Copy Markdown

Walkthrough

This update comprehensively refactors error handling throughout the codebase to use the Result utility from @praha/byethrow in place of traditional try-catch blocks. It updates multiple modules, adds relevant dependencies and configuration, and documents new error handling conventions in the code style guide.

Changes

File(s) Change Summary
.mcp.json, package.json Added @praha/byethrow and @praha/byethrow-mcp as dev dependencies; configured MCP server for byethrow-mcp.
CLAUDE.md Documented new error handling conventions, recommending the use of Result over try-catch for functional style.
src/_live-monitor.ts, src/data-loader.ts,
src/debug.ts, src/commands/_blocks.live.ts,
src/pricing-fetcher.ts
Refactored all error handling for file reading, JSON parsing, and async operations to use Result.try and related utilities, replacing try-catch and catch handlers. Improved logging and statistics initialization in some modules.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant FileSystem/Network
    participant Result
    Note over Caller,Result: Example: JSON parsing with Result.try

    Caller->>Result: Result.try(() => JSON.parse(line))
    alt Success
        Result-->>Caller: Success(parsedObject)
        Caller->>Caller: Continue processing
    else Failure
        Result-->>Caller: Failure(error)
        Caller->>Caller: Skip line or log error
    end
Loading
sequenceDiagram
    participant LiveMonitor
    participant FileSystem
    participant Result
    Note over LiveMonitor,Result: Reading and parsing files

    loop For each file
        LiveMonitor->>Result: Result.try(async () => readFile(file))
        alt File read succeeds
            Result-->>LiveMonitor: Success(fileContent)
            LiveMonitor->>Result: Result.try(() => JSON.parse(line))
            alt Parse succeeds
                Result-->>LiveMonitor: Success(parsedLine)
                LiveMonitor->>LiveMonitor: Validate and accumulate
            else Parse fails
                Result-->>LiveMonitor: Failure(error)
                LiveMonitor->>LiveMonitor: Skip line
            end
        else File read fails
            Result-->>LiveMonitor: Failure(error)
            LiveMonitor->>LiveMonitor: Skip file
        end
    end
Loading

Possibly related PRs

  • ryoppippi/ccusage#169: Also modifies src/pricing-fetcher.ts to improve error handling around pricing data fetches using the Result utility.
  • ryoppippi/ccusage#168: Refactors startLiveMonitoring (same function as in this PR) to add cache clearing, affecting the same code region with a different focus.

Poem

In the warren where errors once hopped,
Now Result bounces, exceptions all stopped.
No more try-catch, just hop and check—
If failure is found, we scurry, not wreck.
Byethrow’s the burrow, our code’s cozy den,
Rabbits rejoice: clean error handling again! 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@ryoppippi ryoppippi changed the title refactor: replace try-catch with @praha/byethrow Result type for functional error handling fix: replace try-catch with @praha/byethrow Result type for functional error handling Jun 29, 2025
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
ccusage-guide 67e2952 Jun 29 2025, 08:50 PM

@gemini-code-assist gemini-code-assist 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.

Summary of Changes

Hello @ryoppippi, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant shift in our error handling strategy by adopting a functional approach using the @praha/byethrow library's Result type. The change aims to enhance code clarity, type safety, and composability by replacing imperative try-catch blocks with explicit success/failure representations. All existing tests continue to pass, ensuring no behavioral regressions.

Highlights

  • Dependency Addition: I've added the @praha/byethrow library, along with its MCP counterpart, to the project dependencies (package.json, bun.lock, and .mcp.json). This library provides a Result type for functional error handling.
  • Error Handling Refactor: I've refactored traditional try-catch blocks across 11 locations in 5 files (src/debug.ts, src/data-loader.ts, src/pricing-fetcher.ts, src/_live-monitor.ts, src/commands/_blocks.live.ts) to utilize the new Result.try() pattern. This includes error handling for JSON parsing, file reading, and network operations.
  • Code Readability and Type Safety: The refactor leverages Result.isFailure() for clearer error checking and improves type safety by explicitly representing success and failure states, aligning with functional programming principles.
  • Documentation Update: I've updated CLAUDE.md to include new guidelines for error handling, recommending the @praha/byethrow Result type over traditional try-catch for most scenarios.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@pkg-pr-new

pkg-pr-new Bot commented Jun 29, 2025

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/ryoppippi/ccusage@221

commit: 67e2952

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request refactors error handling using @praha/byethrow, improving code clarity and type safety. The review suggests preserving original errors for better debuggability and updating CLAUDE.md to reflect this.

Comment thread CLAUDE.md
Comment thread src/_live-monitor.ts
});
const fileReader = Result.try({
try: async () => readFile(file, 'utf-8'),
catch: () => new Error('File read failed'),

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.

medium

To improve debuggability, it's better to preserve the original error from readFile instead of creating a new generic Error. This provides more context on why the file read failed (e.g., 'file not found', 'permission denied'). You can achieve this by passing the error through in the catch block.

Suggested change
catch: () => new Error('File read failed'),
catch: error => error,

Comment thread src/_live-monitor.ts
catch {
const parseParser = Result.try({
try: () => JSON.parse(line) as unknown,
catch: () => new Error('Invalid JSON'),

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.

medium

To aid in debugging, it's better to preserve the original error from JSON.parse. The default error provides specific details about what went wrong with the parsing (e.g., position of the syntax error), which is more useful than a generic 'Invalid JSON' message.

Suggested change
catch: () => new Error('Invalid JSON'),
catch: error => error,

Comment thread src/data-loader.ts
markAsProcessed(uniqueHash, processedHashes);
const parseParser = Result.try({
try: () => JSON.parse(line) as unknown,
catch: () => new Error('Invalid JSON'),

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.

medium

For consistency and better debugging, let's preserve the original error from JSON.parse here. This aligns with the implementation in loadSessionBlockData within this same file and provides more detailed error information if a line is malformed.

catch: () => error,

Comment thread src/data-loader.ts
const data = result.data;
const parseParser = Result.try({
try: () => JSON.parse(line) as unknown,
catch: () => new Error('Invalid JSON'),

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.

medium

To maintain consistency and improve debuggability, please preserve the original error from JSON.parse instead of creating a new generic one. This will provide more specific error details.

catch: () => error,

Comment thread src/debug.ts
const result = usageDataSchema.safeParse(parsed);
const parseParser = Result.try({
try: () => JSON.parse(line) as unknown,
catch: () => new Error('Invalid JSON'),

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.

medium

For better debugging and consistency with other parts of the codebase, it's best to preserve the original error from JSON.parse rather than creating a new, generic error.

catch: () => error,

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​praha/​byethrow-mcp@​0.0.7731009993100
Addedvitepress-plugin-mermaid@​2.0.171001008178100
Added@​praha/​byethrow@​0.3.0781009392100
Addedvitepress@​1.6.3981008397100
Addedvitepress-plugin-llms@​1.6.08510010096100
Addedvitepress-plugin-group-icons@​1.6.09010010092100
Addedwrangler@​4.22.0981009496100

View full report

@ryoppippi
ryoppippi requested a review from Copilot June 29, 2025 20:52

Copilot AI 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.

Pull Request Overview

A refactor to adopt functional error handling using @praha/byethrow across the codebase.

  • Added @praha/byethrow dependencies.
  • Replaced traditional try-catch blocks with Result.try() wrappers and Result.isFailure() checks.
  • Documented new error-handling guidelines in CLAUDE.md.

Reviewed Changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/pricing-fetcher.ts Wrapped fetch and fallback logic in Result.try() patterns
src/debug.ts Migrated JSON parsing to Result.try() and dropped try-catch
src/data-loader.ts Updated JSON parsing loops to use Result.try() for invalid lines
src/commands/_blocks.live.ts Wrapped monitoring loop in Result.try() and handle aborts
src/_live-monitor.ts Converted file reads and JSON parsing to use Result.try()
package.json Added @praha/byethrow and @praha/byethrow-mcp dependencies
CLAUDE.md Added functional error-handling guidelines
.mcp.json Configured MCP settings for @praha/byethrow
Comments suppressed due to low confidence (2)

src/pricing-fetcher.ts:68

  • [nitpick] fallbackWrapper is ambiguous. Consider renaming to something like loadOfflinePricingResultWrapper or offlinePricingOperation to clarify its purpose.
		const fallbackWrapper = Result.try({

src/debug.ts:114

  • [nitpick] parseParser is unclear—rename to parseJsonWrapper or safeJsonParse to better reflect that it's a JSON parsing helper.
			const parseParser = Result.try({

while (!abortController.signal.aborted) {
const now = Date.now();
const timeSinceLastRender = now - lastRenderTime;
const monitoringResult = await Result.try({

Copilot AI Jun 29, 2025

Copy link

Choose a reason for hiding this comment

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

Non-Abort errors in the monitoring loop are swallowed after checking for AbortError. Add logging or rethrow unexpected errors to avoid silent failures.

Copilot uses AI. Check for mistakes.
Comment thread src/debug.ts
Comment on lines +114 to +119
const parseParser = Result.try({
try: () => JSON.parse(line) as unknown,
catch: () => new Error('Invalid JSON'),
});

if (!result.success) {
continue;
}
const parseResult = parseParser();

Copilot AI Jun 29, 2025

Copy link

Choose a reason for hiding this comment

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

The JSON.parse wrapper pattern is repeated across several modules. Consider extracting a reusable safeJsonParse helper to reduce duplication and improve readability.

Copilot uses AI. Check for mistakes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (1)
CLAUDE.md (1)

112-121: Excellent documentation of the new error handling conventions with minor grammatical improvements needed.

The error handling guidelines are comprehensive and clearly document the new functional approach being adopted. This will help maintain consistency across the codebase.

Consider these minor grammatical improvements:

-Keep traditional try-catch only for: file I/O with complex error handling, legacy code that's hard to refactor
+Keep traditional try-catch only for file I/O with complex error handling or legacy code that's difficult to refactor
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 926bdac and 67e2952.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • .mcp.json (1 hunks)
  • CLAUDE.md (1 hunks)
  • package.json (1 hunks)
  • src/_live-monitor.ts (2 hunks)
  • src/commands/_blocks.live.ts (2 hunks)
  • src/data-loader.ts (4 hunks)
  • src/debug.ts (2 hunks)
  • src/pricing-fetcher.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
`**/*.ts`: Use .ts extensions for local file imports (e.g., import { foo } from ...

**/*.ts: Use .ts extensions for local file imports (e.g., import { foo } from './utils.ts').
No console.log allowed except where explicitly disabled with eslint-disable; use logger.ts instead.
Tests are written directly in the same files as the source code, not in separate test files. Use in-source testing pattern with if (import.meta.vitest != null) blocks.
Dynamic imports using await import() should only be used within test blocks to avoid tree-shaking issues.
Only export constants, functions, and types that are actually used by other modules. Internal/private constants that are only used within the same file should NOT be exported.
Variables should start with lowercase (camelCase), types with uppercase (PascalCase), and constants can use UPPER_SNAKE_CASE.
After making any code changes, always run bun run format, bun typecheck, and bun run test to ensure code quality.
File paths must always use Node.js path utilities for cross-platform compatibility.
Dependencies should always be added as devDependencies unless explicitly requested otherwise.

📄 Source: CodeRabbit Inference Engine (CLAUDE.md)

List of files the instruction was applied to:

  • src/commands/_blocks.live.ts
  • src/_live-monitor.ts
  • src/pricing-fetcher.ts
  • src/debug.ts
  • src/data-loader.ts
`**/_*.ts`: Internal files should use underscore prefix (e.g., _types.ts, _utils.ts, _consts.ts).

**/_*.ts: Internal files should use underscore prefix (e.g., _types.ts, _utils.ts, _consts.ts).

📄 Source: CodeRabbit Inference Engine (CLAUDE.md)

List of files the instruction was applied to:

  • src/commands/_blocks.live.ts
  • src/_live-monitor.ts
🧠 Learnings (7)
package.json (1)
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T13:20:08.531Z
Learning: Linting and formatting are enforced via ESLint with tab indentation and double quotes; TypeScript strict mode and bundler module resolution are required.
.mcp.json (1)
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T13:20:08.531Z
Learning: Linting and formatting are enforced via ESLint with tab indentation and double quotes; TypeScript strict mode and bundler module resolution are required.
CLAUDE.md (1)
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T13:20:08.531Z
Learning: Linting and formatting are enforced via ESLint with tab indentation and double quotes; TypeScript strict mode and bundler module resolution are required.
src/_live-monitor.ts (3)
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T13:20:08.531Z
Learning: The CLI tool architecture separates concerns: data loading (data-loader.ts), token aggregation (calculate-cost.ts), command execution (commands/), and CLI entry (index.ts).
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T13:20:08.531Z
Learning: Linting and formatting are enforced via ESLint with tab indentation and double quotes; TypeScript strict mode and bundler module resolution are required.
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T13:20:08.531Z
Learning: Raw usage data is parsed from JSONL files with timestamp, token counts, and pre-calculated costs, and is aggregated into daily, monthly, session, or 5-hour billing block summaries.
src/pricing-fetcher.ts (3)
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T13:20:08.531Z
Learning: Cost calculations depend on LiteLLM's pricing database; model names must exactly match LiteLLM's entries.
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T13:20:08.531Z
Learning: Tests depend on real pricing data from LiteLLM; always verify new model names exist in LiteLLM before adding tests.
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T13:20:08.531Z
Learning: The application cannot calculate costs for models not supported by LiteLLM; test failures may indicate model name mismatches or missing pricing data.
src/debug.ts (5)
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T13:20:08.531Z
Learning: Mock data for tests is created using fs-fixture with createFixture() to simulate Claude data directories.
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T13:20:08.531Z
Learning: Linting and formatting are enforced via ESLint with tab indentation and double quotes; TypeScript strict mode and bundler module resolution are required.
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T13:20:08.531Z
Learning: Raw usage data is parsed from JSONL files with timestamp, token counts, and pre-calculated costs, and is aggregated into daily, monthly, session, or 5-hour billing block summaries.
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T13:20:08.531Z
Learning: Error handling for JSONL parsing should silently skip malformed lines to ensure robustness.
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T13:20:08.531Z
Learning: The CLI tool architecture separates concerns: data loading (data-loader.ts), token aggregation (calculate-cost.ts), command execution (commands/), and CLI entry (index.ts).
src/data-loader.ts (4)

<retrieved_learning>
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T13:20:08.531Z
Learning: Raw usage data is parsed from JSONL files with timestamp, token counts, and pre-calculated costs, and is aggregated into daily, monthly, session, or 5-hour billing block summaries.
</retrieved_learning>

<retrieved_learning>
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T13:20:08.531Z
Learning: The CLI tool architecture separates concerns: data loading (data-loader.ts), token aggregation (calculate-cost.ts), command execution (commands/), and CLI entry (index.ts).
</retrieved_learning>

<retrieved_learning>
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T13:20:08.531Z
Learning: Error handling for JSONL parsing should silently skip malformed lines to ensure robustness.
</retrieved_learning>

<retrieved_learning>
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T13:20:08.531Z
Learning: Mock data for tests is created using fs-fixture with createFixture() to simulate Claude data directories.
</retrieved_learning>

🧬 Code Graph Analysis (5)
src/commands/_blocks.live.ts (2)
src/_consts.ts (1)
  • MIN_RENDER_INTERVAL_MS (110-110)
src/_live-rendering.ts (3)
  • delayWithAbort (36-38)
  • renderWaitingState (44-54)
  • renderActiveBlock (60-68)
src/_live-monitor.ts (1)
src/data-loader.ts (3)
  • usageDataSchema (125-140)
  • createUniqueHash (471-481)
  • calculateCostForEntry (563-595)
src/pricing-fetcher.ts (3)
src/logger.ts (1)
  • logger (18-18)
src/_consts.ts (1)
  • LITELLM_PRICING_URL (7-8)
src/_types.ts (2)
  • ModelPricing (122-122)
  • modelPricingSchema (112-117)
src/debug.ts (2)
src/data-loader.ts (1)
  • usageDataSchema (125-140)
src/_consts.ts (1)
  • DEBUG_MATCH_THRESHOLD_PERCENT (38-38)
src/data-loader.ts (1)
src/logger.ts (1)
  • logger (18-18)
🪛 LanguageTool
CLAUDE.md

[typographical] ~119-~119: Do not use a colon (:) before a series that is introduced by a preposition (‘for’). Remove the colon or add a noun or a noun phrase after the preposition.
Context: ...ll it - Keep traditional try-catch only for: file I/O with complex error handling, l...

(RP_COLON)


[style] ~119-~119: To elevate your writing, try using a synonym here.
Context: ...plex error handling, legacy code that's hard to refactor - Always use `Result.isFail...

(HARD_TO)

🪛 ESLint
src/commands/_blocks.live.ts

[error] 56-86: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 56-86: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 56-56: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 56-56: Unsafe member access .try on an error typed value.

(ts/no-unsafe-member-access)


[error] 58-58: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 58-58: Unsafe member access .signal on an error typed value.

(ts/no-unsafe-member-access)


[error] 64-64: Unsafe member access .signal on an error typed value.

(ts/no-unsafe-member-access)


[error] 73-73: Unsafe member access .signal on an error typed value.

(ts/no-unsafe-member-access)


[error] 82-82: Unsafe member access .signal on an error typed value.

(ts/no-unsafe-member-access)


[error] 85-85: Unsafe return of a value of type any.

(ts/no-unsafe-return)


[error] 88-88: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 88-88: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 88-88: Unsafe member access .isFailure on an error typed value.

(ts/no-unsafe-member-access)


[error] 89-89: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 89-89: Unsafe member access .error on an error typed value.

(ts/no-unsafe-member-access)

src/_live-monitor.ts

[error] 95-98: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 95-95: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 95-95: Unsafe member access .try on an error typed value.

(ts/no-unsafe-member-access)


[error] 96-96: Unsafe return of a value of type error.

(ts/no-unsafe-return)


[error] 96-96: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 100-100: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 100-100: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 101-101: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 101-101: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 101-101: Unsafe member access .isFailure on an error typed value.

(ts/no-unsafe-member-access)


[error] 106-106: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 106-106: Unsafe member access .value on an error typed value.

(ts/no-unsafe-member-access)


[error] 107-110: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 107-110: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 107-109: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 107-108: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 108-108: Unsafe member access .trim on an error typed value.

(ts/no-unsafe-member-access)


[error] 109-109: Unsafe member access .split on an error typed value.

(ts/no-unsafe-member-access)


[error] 110-110: Unsafe member access .filter on an error typed value.

(ts/no-unsafe-member-access)


[error] 110-110: Unsafe member access .length on an any value.

(ts/no-unsafe-member-access)


[error] 113-116: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 113-113: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 113-113: Unsafe member access .try on an error typed value.

(ts/no-unsafe-member-access)


[error] 114-114: Unsafe argument of type error typed assigned to a parameter of type string.

(ts/no-unsafe-argument)


[error] 118-118: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 118-118: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 119-119: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 119-119: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 119-119: Unsafe member access .isFailure on an error typed value.

(ts/no-unsafe-member-access)


[error] 124-124: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 124-124: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 124-124: Unsafe member access .safeParse on an error typed value.

(ts/no-unsafe-member-access)


[error] 124-124: Unsafe member access .value on an error typed value.

(ts/no-unsafe-member-access)


[error] 125-125: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 125-125: Unsafe member access .success on an error typed value.

(ts/no-unsafe-member-access)


[error] 128-128: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 128-128: Unsafe member access .data on an error typed value.

(ts/no-unsafe-member-access)


[error] 140-146: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 141-141: Unsafe member access .costUSD on an error typed value.

(ts/no-unsafe-member-access)


[error] 150-150: Unsafe argument of type error typed assigned to a parameter of type string | number | Date.

(ts/no-unsafe-argument)


[error] 150-150: Unsafe member access .timestamp on an error typed value.

(ts/no-unsafe-member-access)


[error] 152-152: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 152-152: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 153-153: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 153-153: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 154-154: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 154-154: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 155-155: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 155-155: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 158-158: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 158-158: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 159-159: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 159-159: Unsafe member access .version on an error typed value.

(ts/no-unsafe-member-access)

src/pricing-fetcher.ts

[error] 68-71: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 68-68: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 68-68: Unsafe member access .try on an error typed value.

(ts/no-unsafe-member-access)


[error] 70-70: Unsafe return of a value of type any.

(ts/no-unsafe-return)


[error] 73-73: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 73-73: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 74-74: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 74-74: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 74-74: Unsafe member access .isFailure on an error typed value.

(ts/no-unsafe-member-access)


[error] 75-75: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 75-75: Unsafe member access .error on an error typed value.

(ts/no-unsafe-member-access)


[error] 75-75: Unsafe member access .error on an error typed value.

(ts/no-unsafe-member-access)


[error] 76-76: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 76-76: Unsafe member access .error on an error typed value.

(ts/no-unsafe-member-access)


[error] 80-80: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 80-80: Unsafe member access .value on an error typed value.

(ts/no-unsafe-member-access)


[error] 81-81: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 81-81: Unsafe member access .info on an error typed value.

(ts/no-unsafe-member-access)


[error] 81-81: Unsafe member access .size on an error typed value.

(ts/no-unsafe-member-access)


[error] 82-82: Unsafe return of a value of type error.

(ts/no-unsafe-return)


[error] 100-128: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 100-100: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 100-100: Unsafe member access .try on an error typed value.

(ts/no-unsafe-member-access)


[error] 102-102: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 102-102: Unsafe member access .warn on an error typed value.

(ts/no-unsafe-member-access)


[error] 103-103: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 103-103: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 104-104: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 104-104: Unsafe member access .ok on an error typed value.

(ts/no-unsafe-member-access)


[error] 105-105: Unsafe member access .statusText on an error typed value.

(ts/no-unsafe-member-access)


[error] 108-108: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 108-108: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 108-108: Unsafe member access .json on an error typed value.

(ts/no-unsafe-member-access)


[error] 115-115: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 115-115: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 115-115: Unsafe member access .safeParse on an error typed value.

(ts/no-unsafe-member-access)


[error] 116-116: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 116-116: Unsafe member access .success on an error typed value.

(ts/no-unsafe-member-access)


[error] 117-117: Unsafe member access .data on an error typed value.

(ts/no-unsafe-member-access)


[error] 124-124: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 124-124: Unsafe member access .info on an error typed value.

(ts/no-unsafe-member-access)


[error] 127-127: Unsafe return of a value of type any.

(ts/no-unsafe-return)


[error] 130-130: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 130-130: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 131-131: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 131-131: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 131-131: Unsafe member access .isFailure on an error typed value.

(ts/no-unsafe-member-access)


[error] 132-132: Unsafe member access .error on an error typed value.

(ts/no-unsafe-member-access)


[error] 135-135: Unsafe return of a value of type error.

(ts/no-unsafe-return)


[error] 135-135: Unsafe member access .value on an error typed value.

(ts/no-unsafe-member-access)

src/debug.ts

[error] 114-117: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 114-114: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 114-114: Unsafe member access .try on an error typed value.

(ts/no-unsafe-member-access)


[error] 115-115: Unsafe argument of type error typed assigned to a parameter of type string.

(ts/no-unsafe-argument)


[error] 119-119: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 119-119: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 120-120: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 120-120: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 120-120: Unsafe member access .isFailure on an error typed value.

(ts/no-unsafe-member-access)


[error] 124-124: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 124-124: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 124-124: Unsafe member access .safeParse on an error typed value.

(ts/no-unsafe-member-access)


[error] 124-124: Unsafe member access .value on an error typed value.

(ts/no-unsafe-member-access)


[error] 126-126: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 126-126: Unsafe member access .success on an error typed value.

(ts/no-unsafe-member-access)


[error] 130-130: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 130-130: Unsafe member access .data on an error typed value.

(ts/no-unsafe-member-access)


[error] 135-135: Unsafe member access .costUSD on an error typed value.

(ts/no-unsafe-member-access)


[error] 136-136: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 137-137: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 141-141: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 141-141: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 143-143: Unsafe argument of type error typed assigned to a parameter of type { input_tokens: number; output_tokens: number; cache_creation_input_tokens?: number | undefined; cache_read_input_tokens?: number | undefined; }.

(ts/no-unsafe-argument)


[error] 143-143: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 144-144: Unsafe argument of type error typed assigned to a parameter of type string.

(ts/no-unsafe-argument)


[error] 148-148: Unsafe member access .costUSD on an error typed value.

(ts/no-unsafe-member-access)


[error] 150-150: Unsafe member access .costUSD on an error typed value.

(ts/no-unsafe-member-access)


[error] 150-150: Unsafe member access .costUSD on an error typed value.

(ts/no-unsafe-member-access)


[error] 153-153: Unsafe argument of type error typed assigned to a parameter of type string.

(ts/no-unsafe-argument)


[error] 162-162: Unsafe member access .version on an error typed value.

(ts/no-unsafe-member-access)


[error] 163-163: Unsafe argument of type error typed assigned to a parameter of type string.

(ts/no-unsafe-argument)


[error] 163-163: Unsafe member access .version on an error typed value.

(ts/no-unsafe-member-access)


[error] 184-184: Unsafe argument of type error typed assigned to a parameter of type string.

(ts/no-unsafe-argument)


[error] 184-184: Unsafe member access .version on an error typed value.

(ts/no-unsafe-member-access)


[error] 196-196: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 196-196: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 196-196: Unsafe member access .basename on an error typed value.

(ts/no-unsafe-member-access)


[error] 197-197: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 197-197: Unsafe member access .timestamp on an error typed value.

(ts/no-unsafe-member-access)


[error] 198-198: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 199-199: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 199-199: Unsafe member access .costUSD on an error typed value.

(ts/no-unsafe-member-access)


[error] 203-203: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 203-203: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 211-211: Unsafe argument of type error typed assigned to a parameter of type string.

(ts/no-unsafe-argument)

src/data-loader.ts

[error] 666-669: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 666-666: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 666-666: Unsafe member access .try on an error typed value.

(ts/no-unsafe-member-access)


[error] 667-667: Unsafe argument of type error typed assigned to a parameter of type string.

(ts/no-unsafe-argument)


[error] 671-671: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 671-671: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 672-672: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 672-672: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 672-672: Unsafe member access .isFailure on an error typed value.

(ts/no-unsafe-member-access)


[error] 677-677: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 677-677: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 677-677: Unsafe member access .safeParse on an error typed value.

(ts/no-unsafe-member-access)


[error] 677-677: Unsafe member access .value on an error typed value.

(ts/no-unsafe-member-access)


[error] 678-678: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 678-678: Unsafe member access .success on an error typed value.

(ts/no-unsafe-member-access)


[error] 681-681: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 681-681: Unsafe member access .data on an error typed value.

(ts/no-unsafe-member-access)


[error] 693-693: Unsafe argument of type error typed assigned to a parameter of type string.

(ts/no-unsafe-argument)


[error] 693-693: Unsafe member access .timestamp on an error typed value.

(ts/no-unsafe-member-access)


[error] 696-698: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 698-698: Unsafe member access .costUSD on an error typed value.

(ts/no-unsafe-member-access)


[error] 700-700: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 700-700: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 700-700: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 700-700: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 830-833: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 830-830: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 830-830: Unsafe member access .try on an error typed value.

(ts/no-unsafe-member-access)


[error] 831-831: Unsafe argument of type error typed assigned to a parameter of type string.

(ts/no-unsafe-argument)


[error] 835-835: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 835-835: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 836-836: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 836-836: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 836-836: Unsafe member access .isFailure on an error typed value.

(ts/no-unsafe-member-access)


[error] 841-841: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 841-841: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 841-841: Unsafe member access .safeParse on an error typed value.

(ts/no-unsafe-member-access)


[error] 841-841: Unsafe member access .value on an error typed value.

(ts/no-unsafe-member-access)


[error] 842-842: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 842-842: Unsafe member access .success on an error typed value.

(ts/no-unsafe-member-access)


[error] 845-845: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 845-845: Unsafe member access .data on an error typed value.

(ts/no-unsafe-member-access)


[error] 858-860: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 860-860: Unsafe member access .costUSD on an error typed value.

(ts/no-unsafe-member-access)


[error] 863-863: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 865-865: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 866-866: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 867-867: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 868-868: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 868-868: Unsafe member access .timestamp on an error typed value.

(ts/no-unsafe-member-access)


[error] 869-869: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 869-869: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 1060-1063: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 1060-1060: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1060-1060: Unsafe member access .try on an error typed value.

(ts/no-unsafe-member-access)


[error] 1061-1061: Unsafe argument of type error typed assigned to a parameter of type string.

(ts/no-unsafe-argument)


[error] 1062-1062: Unsafe return of a value of type any.

(ts/no-unsafe-return)


[error] 1065-1065: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 1065-1065: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1066-1066: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 1066-1066: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1066-1066: Unsafe member access .isFailure on an error typed value.

(ts/no-unsafe-member-access)


[error] 1068-1068: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1068-1068: Unsafe member access .debug on an error typed value.

(ts/no-unsafe-member-access)


[error] 1068-1068: Unsafe member access .error on an error typed value.

(ts/no-unsafe-member-access)


[error] 1068-1068: Unsafe member access .error on an error typed value.

(ts/no-unsafe-member-access)


[error] 1068-1068: Unsafe member access .error on an error typed value.

(ts/no-unsafe-member-access)


[error] 1072-1072: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 1072-1072: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1072-1072: Unsafe member access .safeParse on an error typed value.

(ts/no-unsafe-member-access)


[error] 1072-1072: Unsafe member access .value on an error typed value.

(ts/no-unsafe-member-access)


[error] 1073-1073: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 1073-1073: Unsafe member access .success on an error typed value.

(ts/no-unsafe-member-access)


[error] 1076-1076: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 1076-1076: Unsafe member access .data on an error typed value.

(ts/no-unsafe-member-access)


[error] 1088-1090: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 1090-1090: Unsafe member access .costUSD on an error typed value.

(ts/no-unsafe-member-access)


[error] 1093-1093: Unsafe argument of type error typed assigned to a parameter of type string | number | Date.

(ts/no-unsafe-argument)


[error] 1093-1093: Unsafe member access .timestamp on an error typed value.

(ts/no-unsafe-member-access)


[error] 1095-1095: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 1095-1095: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 1096-1096: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 1096-1096: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 1097-1097: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 1097-1097: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 1098-1098: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 1098-1098: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 1100-1100: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 1101-1101: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 1101-1101: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 1102-1102: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 1102-1102: Unsafe member access .version on an error typed value.

(ts/no-unsafe-member-access)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Socket Security: Pull Request Alerts
  • GitHub Check: Workers Builds: ccusage-guide
🔇 Additional comments (11)
package.json (1)

65-66: LGTM! Dependencies added correctly for functional error handling.

The addition of @praha/byethrow and @praha/byethrow-mcp as devDependencies aligns with the PR objective of adopting functional error handling. The version specifications look appropriate for the refactoring being implemented.

.mcp.json (1)

26-33: LGTM! MCP server configuration added correctly.

The new @praha/byethrow MCP server configuration follows the established pattern and properly integrates the byethrow functionality into the development environment.

src/commands/_blocks.live.ts (2)

11-11: Import added correctly for functional error handling.

The Result import from @praha/byethrow supports the new functional error handling pattern being adopted.


56-102: No TypeScript issues with Result.try() usage

The @praha/byethrow package ships complete .d.ts files (e.g., dist/cjs/index.d.ts and functions/try.d.ts), and a full tsc --noEmit run on the repo reports zero errors. The Result.try() API is fully typed and safe—so the original type-safety concern does not apply.

Likely an incorrect or invalid review comment.

src/_live-monitor.ts (1)

15-15: Import added correctly for functional error handling.

The Result import supports the functional error handling refactor being implemented across the codebase.

src/data-loader.ts (2)

24-24: LGTM: Clean import addition

The import of Result from @praha/byethrow is properly added and follows the established import pattern.


57-57: Good: Logger import is properly used

The logger import is correctly used in the loadSessionBlockData function for debug logging, which aligns with the coding guidelines that prohibit console.log usage.

src/debug.ts (4)

12-12: LGTM: Import added for functional error handling

The import of Result from @praha/byethrow aligns with the PR objective to standardize functional error handling across the codebase.


124-130: LGTM: Schema validation correctly uses parsed result

The schema validation properly uses parseResult.value to access the successfully parsed JSON and maintains the pattern of skipping invalid entries.


153-185: LGTM: Statistics tracking improvements

The refactoring to explicitly initialize counters and update statistics improves code clarity and maintainability. The logic correctly handles both model and version statistics with proper null coalescing.


114-122: ✅ Result.try() usage in src/debug.ts is correct

The Result.try() invocation matches the established pattern across the codebase (see src/data-loader.ts, src/_live-monitor.ts, src/pricing-fetcher.ts) and aligns with the @praha/byethrow usage documented in CLAUDE.md. No TypeScript type errors were found for version ^0.3.0, so no fixes are required.

Comment thread src/_live-monitor.ts
Comment on lines +95 to 161
const fileReader = Result.try({
try: async () => readFile(file, 'utf-8'),
catch: () => new Error('File read failed'),
});

const fileResult = await fileReader();
if (Result.isFailure(fileResult)) {
// Skip files that can't be read
continue;
}

const content = fileResult.value;
const lines = content
.trim()
.split('\n')
.filter(line => line.length > 0);

for (const line of lines) {
try {
const parsed = JSON.parse(line) as unknown;
const result = usageDataSchema.safeParse(parsed);
if (!result.success) {
continue;
}
const data = result.data;

// Check for duplicates
const uniqueHash = createUniqueHash(data);
if (uniqueHash != null && this.processedHashes.has(uniqueHash)) {
continue;
}
if (uniqueHash != null) {
this.processedHashes.add(uniqueHash);
}

// Calculate cost if needed
const costUSD: number = await (this.config.mode === 'display'
? Promise.resolve(data.costUSD ?? 0)
: calculateCostForEntry(
data,
this.config.mode,
this.fetcher!,
));

// Add entry
this.allEntries.push({
timestamp: new Date(data.timestamp),
usage: {
inputTokens: data.message.usage.input_tokens ?? 0,
outputTokens: data.message.usage.output_tokens ?? 0,
cacheCreationInputTokens: data.message.usage.cache_creation_input_tokens ?? 0,
cacheReadInputTokens: data.message.usage.cache_read_input_tokens ?? 0,
},
costUSD,
model: data.message.model ?? '<synthetic>',
version: data.version,
});
}
catch {
const parseParser = Result.try({
try: () => JSON.parse(line) as unknown,
catch: () => new Error('Invalid JSON'),
});

const parseResult = parseParser();
if (Result.isFailure(parseResult)) {
// Skip malformed lines
continue;
}

const result = usageDataSchema.safeParse(parseResult.value);
if (!result.success) {
continue;
}
const data = result.data;

// Check for duplicates
const uniqueHash = createUniqueHash(data);
if (uniqueHash != null && this.processedHashes.has(uniqueHash)) {
continue;
}
if (uniqueHash != null) {
this.processedHashes.add(uniqueHash);
}

// Calculate cost if needed
const costUSD: number = await (this.config.mode === 'display'
? Promise.resolve(data.costUSD ?? 0)
: calculateCostForEntry(
data,
this.config.mode,
this.fetcher!,
));

// Add entry
this.allEntries.push({
timestamp: new Date(data.timestamp),
usage: {
inputTokens: data.message.usage.input_tokens ?? 0,
outputTokens: data.message.usage.output_tokens ?? 0,
cacheCreationInputTokens: data.message.usage.cache_creation_input_tokens ?? 0,
cacheReadInputTokens: data.message.usage.cache_read_input_tokens ?? 0,
},
costUSD,
model: data.message.model ?? '<synthetic>',
version: data.version,
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Functional error handling refactor is logically sound but has critical type safety issues.

The refactor successfully replaces try-catch blocks with the Result.try() pattern, maintaining the same robust error handling behavior (skipping unreadable files and malformed JSON lines). The logic correctly processes only successful results and continues on failures.

However, the same TypeScript type safety issues present in src/commands/_blocks.live.ts are evident here, where the Result type is being treated as any or error types.

The type safety issues need to be resolved across all files using the Result type. This appears to be a systemic issue with either:

  1. Missing or incorrect type definitions for @praha/byethrow
  2. TypeScript configuration not properly recognizing the Result types
  3. Incorrect usage of the Result API

Please ensure proper typing is established before merging to maintain type safety guarantees.

🧰 Tools
🪛 ESLint

[error] 95-98: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 95-95: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 95-95: Unsafe member access .try on an error typed value.

(ts/no-unsafe-member-access)


[error] 96-96: Unsafe return of a value of type error.

(ts/no-unsafe-return)


[error] 96-96: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 100-100: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 100-100: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 101-101: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 101-101: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 101-101: Unsafe member access .isFailure on an error typed value.

(ts/no-unsafe-member-access)


[error] 106-106: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 106-106: Unsafe member access .value on an error typed value.

(ts/no-unsafe-member-access)


[error] 107-110: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 107-110: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 107-109: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 107-108: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 108-108: Unsafe member access .trim on an error typed value.

(ts/no-unsafe-member-access)


[error] 109-109: Unsafe member access .split on an error typed value.

(ts/no-unsafe-member-access)


[error] 110-110: Unsafe member access .filter on an error typed value.

(ts/no-unsafe-member-access)


[error] 110-110: Unsafe member access .length on an any value.

(ts/no-unsafe-member-access)


[error] 113-116: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 113-113: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 113-113: Unsafe member access .try on an error typed value.

(ts/no-unsafe-member-access)


[error] 114-114: Unsafe argument of type error typed assigned to a parameter of type string.

(ts/no-unsafe-argument)


[error] 118-118: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 118-118: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 119-119: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 119-119: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 119-119: Unsafe member access .isFailure on an error typed value.

(ts/no-unsafe-member-access)


[error] 124-124: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 124-124: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 124-124: Unsafe member access .safeParse on an error typed value.

(ts/no-unsafe-member-access)


[error] 124-124: Unsafe member access .value on an error typed value.

(ts/no-unsafe-member-access)


[error] 125-125: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 125-125: Unsafe member access .success on an error typed value.

(ts/no-unsafe-member-access)


[error] 128-128: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 128-128: Unsafe member access .data on an error typed value.

(ts/no-unsafe-member-access)


[error] 140-146: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 141-141: Unsafe member access .costUSD on an error typed value.

(ts/no-unsafe-member-access)


[error] 150-150: Unsafe argument of type error typed assigned to a parameter of type string | number | Date.

(ts/no-unsafe-argument)


[error] 150-150: Unsafe member access .timestamp on an error typed value.

(ts/no-unsafe-member-access)


[error] 152-152: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 152-152: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 153-153: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 153-153: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 154-154: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 154-154: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 155-155: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 155-155: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 158-158: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 158-158: Unsafe member access .message on an error typed value.

(ts/no-unsafe-member-access)


[error] 159-159: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 159-159: Unsafe member access .version on an error typed value.

(ts/no-unsafe-member-access)

🤖 Prompt for AI Agents
In src/_live-monitor.ts between lines 95 and 161, the use of Result.try() and
related Result API calls lacks proper TypeScript type safety, causing the Result
type to be treated as any or error types. To fix this, explicitly type the
Result.try() calls with the expected return types, ensure the Result type is
correctly imported and used from @praha/byethrow, and verify that your
TypeScript configuration supports these types. Adjust the code to handle the
typed Result objects properly, avoiding any implicit any or error type
assumptions, so that type safety is preserved throughout the error handling
logic.

Comment thread src/pricing-fetcher.ts
*/

import type { ModelPricing } from './_types.ts';
import { Result } from '@praha/byethrow';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify TypeScript definitions for @praha/byethrow.

The functional error handling approach is a good improvement, but the numerous TypeScript safety warnings suggest potential issues with the library's type definitions.


🏁 Script executed:

#!/bin/bash
# Check if @praha/byethrow has proper TypeScript definitions
npm info @praha/byethrow

# Check if there are type definition files in node_modules
fd -t f "*.d.ts" node_modules/@praha/byethrow/ || echo "No TypeScript definitions found"

# Check the package.json for types field
cat node_modules/@praha/byethrow/package.json | jq '.types // .typings // "No types field found"'

Length of output: 1117


Missing TypeScript definitions for @praha/byethrow

It looks like @praha/[email protected] doesn’t ship any .d.ts files nor declare a types/typings field, which is why you’re seeing unsafe-any warnings. To restore full type safety when using Result:

• Have the library publish proper declaration files (e.g. index.d.ts) and set "types": "index.d.ts" in its package.json, or
• Provide a scoped ambient module in our codebase as a stop-gap (declare module '@praha/byethrow';), understanding that it relaxes type checks.

Either way, we need type definitions in place before approving this refactor.

🤖 Prompt for AI Agents
In src/pricing-fetcher.ts at line 12, the import of Result from
'@praha/byethrow' lacks TypeScript type definitions, causing unsafe-any
warnings. To fix this, either coordinate with the library maintainers to publish
proper .d.ts files and update their package.json with a "types" field, or
temporarily add a scoped ambient module declaration (declare module
'@praha/byethrow';) in our codebase to suppress type errors while awaiting
official typings.

Comment thread src/pricing-fetcher.ts
Comment on lines +100 to 136
const fetchWrapper = Result.try({
try: async () => {
logger.warn('Fetching latest model pricing from LiteLLM...');
const response = await fetch(LITELLM_PRICING_URL);
if (!response.ok) {
throw new Error(`Failed to fetch pricing data: ${response.statusText}`);
}

for (const [modelName, modelData] of Object.entries(
data as Record<string, unknown>,
)) {
if (typeof modelData === 'object' && modelData !== null) {
const parsed = modelPricingSchema.safeParse(modelData);
if (parsed.success) {
pricing.set(modelName, parsed.data);
const data = await response.json();
const pricing = new Map<string, ModelPricing>();

for (const [modelName, modelData] of Object.entries(
data as Record<string, unknown>,
)) {
if (typeof modelData === 'object' && modelData !== null) {
const parsed = modelPricingSchema.safeParse(modelData);
if (parsed.success) {
pricing.set(modelName, parsed.data);
}
// Skip models that don't match our schema
}
// Skip models that don't match our schema
}
}

this.cachedPricing = pricing;
logger.info(`Loaded pricing for ${pricing.size} models`);
return pricing;
}
catch (error) {
return this.handleFallbackToCachedPricing(error);
this.cachedPricing = pricing;
logger.info(`Loaded pricing for ${pricing.size} models`);
return pricing;
},
catch: error => error,
});

const fetchResult = await fetchWrapper();
if (Result.isFailure(fetchResult)) {
return this.handleFallbackToCachedPricing(fetchResult.error);
}

return fetchResult.value;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Excellent refactor of complex async operation to Result pattern.

This refactor successfully converts a complex try-catch block with multiple failure points (fetch, JSON parsing, schema validation) into a functional Result pattern while preserving all original behavior including proper error handling and fallback logic.

To address the TypeScript safety warnings, consider adding explicit typing to the Result operations:

-		const fetchWrapper = Result.try({
+		const fetchWrapper = Result.try<Map<string, ModelPricing>, Error>({
			try: async () => {
				logger.warn('Fetching latest model pricing from LiteLLM...');
				const response = await fetch(LITELLM_PRICING_URL);
				if (!response.ok) {
					throw new Error(`Failed to fetch pricing data: ${response.statusText}`);
				}

				const data = await response.json();
				const pricing = new Map<string, ModelPricing>();

				for (const [modelName, modelData] of Object.entries(
					data as Record<string, unknown>,
				)) {
					if (typeof modelData === 'object' && modelData !== null) {
						const parsed = modelPricingSchema.safeParse(modelData);
						if (parsed.success) {
							pricing.set(modelName, parsed.data);
						}
						// Skip models that don't match our schema
					}
				}

				this.cachedPricing = pricing;
				logger.info(`Loaded pricing for ${pricing.size} models`);
				return pricing;
			},
			catch: error => error,
		});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const fetchWrapper = Result.try({
try: async () => {
logger.warn('Fetching latest model pricing from LiteLLM...');
const response = await fetch(LITELLM_PRICING_URL);
if (!response.ok) {
throw new Error(`Failed to fetch pricing data: ${response.statusText}`);
}
for (const [modelName, modelData] of Object.entries(
data as Record<string, unknown>,
)) {
if (typeof modelData === 'object' && modelData !== null) {
const parsed = modelPricingSchema.safeParse(modelData);
if (parsed.success) {
pricing.set(modelName, parsed.data);
const data = await response.json();
const pricing = new Map<string, ModelPricing>();
for (const [modelName, modelData] of Object.entries(
data as Record<string, unknown>,
)) {
if (typeof modelData === 'object' && modelData !== null) {
const parsed = modelPricingSchema.safeParse(modelData);
if (parsed.success) {
pricing.set(modelName, parsed.data);
}
// Skip models that don't match our schema
}
// Skip models that don't match our schema
}
}
this.cachedPricing = pricing;
logger.info(`Loaded pricing for ${pricing.size} models`);
return pricing;
}
catch (error) {
return this.handleFallbackToCachedPricing(error);
this.cachedPricing = pricing;
logger.info(`Loaded pricing for ${pricing.size} models`);
return pricing;
},
catch: error => error,
});
const fetchResult = await fetchWrapper();
if (Result.isFailure(fetchResult)) {
return this.handleFallbackToCachedPricing(fetchResult.error);
}
return fetchResult.value;
}
const fetchWrapper = Result.try<Map<string, ModelPricing>, Error>({
try: async () => {
logger.warn('Fetching latest model pricing from LiteLLM...');
const response = await fetch(LITELLM_PRICING_URL);
if (!response.ok) {
throw new Error(`Failed to fetch pricing data: ${response.statusText}`);
}
const data = await response.json();
const pricing = new Map<string, ModelPricing>();
for (const [modelName, modelData] of Object.entries(
data as Record<string, unknown>,
)) {
if (typeof modelData === 'object' && modelData !== null) {
const parsed = modelPricingSchema.safeParse(modelData);
if (parsed.success) {
pricing.set(modelName, parsed.data);
}
// Skip models that don't match our schema
}
}
this.cachedPricing = pricing;
logger.info(`Loaded pricing for ${pricing.size} models`);
return pricing;
},
catch: error => error,
});
🧰 Tools
🪛 ESLint

[error] 100-128: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 100-100: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 100-100: Unsafe member access .try on an error typed value.

(ts/no-unsafe-member-access)


[error] 102-102: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 102-102: Unsafe member access .warn on an error typed value.

(ts/no-unsafe-member-access)


[error] 103-103: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 103-103: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 104-104: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 104-104: Unsafe member access .ok on an error typed value.

(ts/no-unsafe-member-access)


[error] 105-105: Unsafe member access .statusText on an error typed value.

(ts/no-unsafe-member-access)


[error] 108-108: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 108-108: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 108-108: Unsafe member access .json on an error typed value.

(ts/no-unsafe-member-access)


[error] 115-115: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 115-115: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 115-115: Unsafe member access .safeParse on an error typed value.

(ts/no-unsafe-member-access)


[error] 116-116: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 116-116: Unsafe member access .success on an error typed value.

(ts/no-unsafe-member-access)


[error] 117-117: Unsafe member access .data on an error typed value.

(ts/no-unsafe-member-access)


[error] 124-124: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 124-124: Unsafe member access .info on an error typed value.

(ts/no-unsafe-member-access)


[error] 127-127: Unsafe return of a value of type any.

(ts/no-unsafe-return)


[error] 130-130: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 130-130: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 131-131: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 131-131: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 131-131: Unsafe member access .isFailure on an error typed value.

(ts/no-unsafe-member-access)


[error] 132-132: Unsafe member access .error on an error typed value.

(ts/no-unsafe-member-access)


[error] 135-135: Unsafe return of a value of type error.

(ts/no-unsafe-return)


[error] 135-135: Unsafe member access .value on an error typed value.

(ts/no-unsafe-member-access)

🤖 Prompt for AI Agents
In src/pricing-fetcher.ts around lines 100 to 136, the Result.try and related
Result operations lack explicit TypeScript typings, which can cause safety
warnings. To fix this, add explicit generic type parameters to the Result.try
call and any other Result methods used here, specifying the expected success and
error types. This will ensure TypeScript can correctly infer types and eliminate
safety warnings while preserving the existing error handling and fallback logic.

Comment thread src/pricing-fetcher.ts
Comment on lines +68 to 83
const fallbackWrapper = Result.try({
try: async () => this.loadOfflinePricing(),
catch: error => error,
});

const fallbackResult = await fallbackWrapper();
if (Result.isFailure(fallbackResult)) {
logger.error('Failed to load cached pricing data as fallback:', fallbackResult.error);
logger.error('Original fetch error:', originalError);
throw new Error('Could not fetch model pricing data and fallback data is unavailable');
}

const fallbackPricing = fallbackResult.value;
logger.info(`Using cached pricing data for ${fallbackPricing.size} models`);
return fallbackPricing;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Well-implemented functional error handling refactor.

The refactor correctly implements the Result pattern while maintaining the original error handling logic. The fallback mechanism is preserved, and the error logging provides appropriate context.

Consider adding type assertions or proper typing to address the TypeScript safety warnings:

-		const fallbackWrapper = Result.try({
+		const fallbackWrapper = Result.try<Map<string, ModelPricing>, Error>({
			try: async () => this.loadOfflinePricing(),
			catch: error => error,
		});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const fallbackWrapper = Result.try({
try: async () => this.loadOfflinePricing(),
catch: error => error,
});
const fallbackResult = await fallbackWrapper();
if (Result.isFailure(fallbackResult)) {
logger.error('Failed to load cached pricing data as fallback:', fallbackResult.error);
logger.error('Original fetch error:', originalError);
throw new Error('Could not fetch model pricing data and fallback data is unavailable');
}
const fallbackPricing = fallbackResult.value;
logger.info(`Using cached pricing data for ${fallbackPricing.size} models`);
return fallbackPricing;
}
const fallbackWrapper = Result.try<Map<string, ModelPricing>, Error>({
try: async () => this.loadOfflinePricing(),
catch: error => error,
});
const fallbackResult = await fallbackWrapper();
if (Result.isFailure(fallbackResult)) {
logger.error('Failed to load cached pricing data as fallback:', fallbackResult.error);
logger.error('Original fetch error:', originalError);
throw new Error('Could not fetch model pricing data and fallback data is unavailable');
}
const fallbackPricing = fallbackResult.value;
logger.info(`Using cached pricing data for ${fallbackPricing.size} models`);
return fallbackPricing;
🧰 Tools
🪛 ESLint

[error] 68-71: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 68-68: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 68-68: Unsafe member access .try on an error typed value.

(ts/no-unsafe-member-access)


[error] 70-70: Unsafe return of a value of type any.

(ts/no-unsafe-return)


[error] 73-73: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 73-73: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 74-74: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 74-74: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 74-74: Unsafe member access .isFailure on an error typed value.

(ts/no-unsafe-member-access)


[error] 75-75: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 75-75: Unsafe member access .error on an error typed value.

(ts/no-unsafe-member-access)


[error] 75-75: Unsafe member access .error on an error typed value.

(ts/no-unsafe-member-access)


[error] 76-76: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 76-76: Unsafe member access .error on an error typed value.

(ts/no-unsafe-member-access)


[error] 80-80: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 80-80: Unsafe member access .value on an error typed value.

(ts/no-unsafe-member-access)


[error] 81-81: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 81-81: Unsafe member access .info on an error typed value.

(ts/no-unsafe-member-access)


[error] 81-81: Unsafe member access .size on an error typed value.

(ts/no-unsafe-member-access)


[error] 82-82: Unsafe return of a value of type error.

(ts/no-unsafe-return)

🤖 Prompt for AI Agents
In src/pricing-fetcher.ts around lines 68 to 83, add explicit type annotations
or assertions to the fallbackWrapper and fallbackResult variables to ensure
TypeScript correctly infers their types. This will eliminate safety warnings by
clearly defining the expected Result type returned by the try method and the
structure of fallbackResult, improving type safety without changing the existing
error handling logic.

Comment thread src/data-loader.ts
Comment on lines +830 to +839
const parseParser = Result.try({
try: () => JSON.parse(line) as unknown,
catch: () => new Error('Invalid JSON'),
});

// Check for duplicate message + request ID combination
const uniqueHash = createUniqueHash(data);
if (isDuplicateEntry(uniqueHash, processedHashes)) {
// Skip duplicate message
continue;
}
const parseResult = parseParser();
if (Result.isFailure(parseResult)) {
// Skip invalid JSON lines
continue;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Same TypeScript type safety issues as loadDailyUsageData

This implementation has identical type safety problems to the previous function.

Apply the same fix as suggested for loadDailyUsageData:

-			const parseParser = Result.try({
-				try: () => JSON.parse(line) as unknown,
-				catch: () => new Error('Invalid JSON'),
-			});
-
-			const parseResult = parseParser();
-			if (Result.isFailure(parseResult)) {
+			const parseResult = Result.try(() => JSON.parse(line) as unknown);
+			if (Result.isFailure(parseResult)) {
				// Skip invalid JSON lines
				continue;
			}

			const result = usageDataSchema.safeParse(parseResult.value);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const parseParser = Result.try({
try: () => JSON.parse(line) as unknown,
catch: () => new Error('Invalid JSON'),
});
// Check for duplicate message + request ID combination
const uniqueHash = createUniqueHash(data);
if (isDuplicateEntry(uniqueHash, processedHashes)) {
// Skip duplicate message
continue;
}
const parseResult = parseParser();
if (Result.isFailure(parseResult)) {
// Skip invalid JSON lines
continue;
}
const parseResult = Result.try(() => JSON.parse(line) as unknown);
if (Result.isFailure(parseResult)) {
// Skip invalid JSON lines
continue;
}
const result = usageDataSchema.safeParse(parseResult.value);
🧰 Tools
🪛 ESLint

[error] 830-833: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 830-830: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 830-830: Unsafe member access .try on an error typed value.

(ts/no-unsafe-member-access)


[error] 831-831: Unsafe argument of type error typed assigned to a parameter of type string.

(ts/no-unsafe-argument)


[error] 835-835: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 835-835: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 836-836: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 836-836: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 836-836: Unsafe member access .isFailure on an error typed value.

(ts/no-unsafe-member-access)

🤖 Prompt for AI Agents
In src/data-loader.ts around lines 830 to 839, the JSON parsing uses a generic
unknown type which lacks proper TypeScript type safety. To fix this, replace the
unknown type with a specific interface or type that represents the expected JSON
structure, and update the JSON.parse call to cast to that type. This ensures
type safety and prevents runtime errors by validating the parsed data against
the expected type.

Comment thread src/data-loader.ts
Comment on lines +666 to +675
const parseParser = Result.try({
try: () => JSON.parse(line) as unknown,
catch: () => new Error('Invalid JSON'),
});

const date = formatDate(data.timestamp);
// If fetcher is available, calculate cost based on mode and tokens
// If fetcher is null, use pre-calculated costUSD or default to 0
const cost = fetcher != null
? await calculateCostForEntry(data, mode, fetcher)
: data.costUSD ?? 0;
const parseResult = parseParser();
if (Result.isFailure(parseResult)) {
// Skip invalid JSON lines
continue;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Address TypeScript type safety issues with Result usage

The functional error handling pattern is correct, but there are significant TypeScript type safety issues that need to be resolved.

The static analysis is flagging multiple unsafe type operations. Apply this diff to improve type safety:

-			const parseParser = Result.try({
-				try: () => JSON.parse(line) as unknown,
-				catch: () => new Error('Invalid JSON'),
-			});
-
-			const parseResult = parseParser();
-			if (Result.isFailure(parseResult)) {
+			const parseResult = Result.try(() => JSON.parse(line) as unknown);
+			if (Result.isFailure(parseResult)) {
				// Skip invalid JSON lines
				continue;
			}

			const result = usageDataSchema.safeParse(parseResult.value);

This should resolve the type safety issues while maintaining the same functionality.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const parseParser = Result.try({
try: () => JSON.parse(line) as unknown,
catch: () => new Error('Invalid JSON'),
});
const date = formatDate(data.timestamp);
// If fetcher is available, calculate cost based on mode and tokens
// If fetcher is null, use pre-calculated costUSD or default to 0
const cost = fetcher != null
? await calculateCostForEntry(data, mode, fetcher)
: data.costUSD ?? 0;
const parseResult = parseParser();
if (Result.isFailure(parseResult)) {
// Skip invalid JSON lines
continue;
}
const parseResult = Result.try(() => JSON.parse(line) as unknown);
if (Result.isFailure(parseResult)) {
// Skip invalid JSON lines
continue;
}
const result = usageDataSchema.safeParse(parseResult.value);
🧰 Tools
🪛 ESLint

[error] 666-669: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 666-666: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 666-666: Unsafe member access .try on an error typed value.

(ts/no-unsafe-member-access)


[error] 667-667: Unsafe argument of type error typed assigned to a parameter of type string.

(ts/no-unsafe-argument)


[error] 671-671: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 671-671: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 672-672: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 672-672: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 672-672: Unsafe member access .isFailure on an error typed value.

(ts/no-unsafe-member-access)

🤖 Prompt for AI Agents
In src/data-loader.ts around lines 666 to 675, the current use of Result.try
with JSON.parse has TypeScript type safety issues due to unsafe type assertions.
To fix this, explicitly type the Result.try call to reflect that it returns a
Result with the parsed JSON type or an Error. Adjust the try function to return
the correct type without using 'as unknown', and ensure the catch function
returns an Error. This will improve static type checking while preserving the
existing error handling logic.

Comment thread src/data-loader.ts
Comment on lines +1060 to +1070
const parseParser = Result.try({
try: () => JSON.parse(line) as unknown,
catch: error => error,
});

const cost = fetcher != null
? await calculateCostForEntry(data, mode, fetcher)
: data.costUSD ?? 0;
const parseResult = parseParser();
if (Result.isFailure(parseResult)) {
// Skip invalid JSON lines but log for debugging purposes
logger.debug(`Skipping invalid JSON line in 5-hour blocks: ${parseResult.error instanceof Error ? parseResult.error.message : String(parseResult.error)}`);
continue;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix type safety issues and inconsistent error handling

This implementation has both the same TypeScript issues and an inconsistent error handling pattern compared to the other two functions.

Apply this diff to fix both issues:

-			const parseParser = Result.try({
-				try: () => JSON.parse(line) as unknown,
-				catch: error => error,
-			});
-
-			const parseResult = parseParser();
-			if (Result.isFailure(parseResult)) {
-				// Skip invalid JSON lines but log for debugging purposes
-				logger.debug(`Skipping invalid JSON line in 5-hour blocks: ${parseResult.error instanceof Error ? parseResult.error.message : String(parseResult.error)}`);
+			const parseResult = Result.try(() => JSON.parse(line) as unknown);
+			if (Result.isFailure(parseResult)) {
+				// Skip invalid JSON lines but log for debugging purposes
+				logger.debug(`Skipping invalid JSON line in 5-hour blocks: ${parseResult.error instanceof Error ? parseResult.error.message : 'Invalid JSON'}`);
				continue;
			}

			const result = usageDataSchema.safeParse(parseResult.value);

Note: The inconsistent catch: error => error pattern differs from the other functions and may cause additional type issues.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const parseParser = Result.try({
try: () => JSON.parse(line) as unknown,
catch: error => error,
});
const cost = fetcher != null
? await calculateCostForEntry(data, mode, fetcher)
: data.costUSD ?? 0;
const parseResult = parseParser();
if (Result.isFailure(parseResult)) {
// Skip invalid JSON lines but log for debugging purposes
logger.debug(`Skipping invalid JSON line in 5-hour blocks: ${parseResult.error instanceof Error ? parseResult.error.message : String(parseResult.error)}`);
continue;
}
const parseResult = Result.try(() => JSON.parse(line) as unknown);
if (Result.isFailure(parseResult)) {
// Skip invalid JSON lines but log for debugging purposes
logger.debug(`Skipping invalid JSON line in 5-hour blocks: ${parseResult.error instanceof Error ? parseResult.error.message : 'Invalid JSON'}`);
continue;
}
const result = usageDataSchema.safeParse(parseResult.value);
🧰 Tools
🪛 ESLint

[error] 1060-1063: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 1060-1060: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1060-1060: Unsafe member access .try on an error typed value.

(ts/no-unsafe-member-access)


[error] 1061-1061: Unsafe argument of type error typed assigned to a parameter of type string.

(ts/no-unsafe-argument)


[error] 1062-1062: Unsafe return of a value of type any.

(ts/no-unsafe-return)


[error] 1065-1065: Unsafe assignment of an error typed value.

(ts/no-unsafe-assignment)


[error] 1065-1065: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1066-1066: Unexpected any value in conditional. An explicit comparison or type conversion is required.

(ts/strict-boolean-expressions)


[error] 1066-1066: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1066-1066: Unsafe member access .isFailure on an error typed value.

(ts/no-unsafe-member-access)


[error] 1068-1068: Unsafe call of a(n) error type typed value.

(ts/no-unsafe-call)


[error] 1068-1068: Unsafe member access .debug on an error typed value.

(ts/no-unsafe-member-access)


[error] 1068-1068: Unsafe member access .error on an error typed value.

(ts/no-unsafe-member-access)


[error] 1068-1068: Unsafe member access .error on an error typed value.

(ts/no-unsafe-member-access)


[error] 1068-1068: Unsafe member access .error on an error typed value.

(ts/no-unsafe-member-access)

🤖 Prompt for AI Agents
In src/data-loader.ts around lines 1060 to 1070, the current JSON parsing uses a
catch handler that returns the error directly, causing type safety issues and
inconsistent error handling compared to other functions. To fix this, update the
catch handler to wrap the error in a consistent failure result or error type as
used elsewhere, ensuring the parseParser function returns a uniform result type.
Adjust the error handling logic accordingly to maintain type safety and
consistency.

Comment thread src/debug.ts
stats.matches++;
modelStat.matches++;
// Consider it a match if within the defined threshold (to account for floating point)
if (percentDiff < DEBUG_MATCH_THRESHOLD_PERCENT) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix threshold inconsistency between model and version matching

There's an inconsistency in threshold usage:

  • Line 172: Uses DEBUG_MATCH_THRESHOLD_PERCENT constant for version-level matching
  • Line 188: Uses hardcoded 0.1 for model-level matching

Both should use the same threshold for consistency.

Apply this diff to use the constant consistently:

-				// Consider it a match if within 0.1% difference (to account for floating point)
-				if (percentDiff < 0.1) {
+				// Consider it a match if within the defined threshold (to account for floating point)
+				if (percentDiff < DEBUG_MATCH_THRESHOLD_PERCENT) {

Also applies to: 188-188

🤖 Prompt for AI Agents
In src/debug.ts at lines 172 and 188, replace the hardcoded threshold value 0.1
used for model-level matching with the constant DEBUG_MATCH_THRESHOLD_PERCENT to
ensure consistent threshold usage across both version-level and model-level
matching.

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.

2 participants