Conversation
WalkthroughAdds TorrServer support across core: new TorrServerDebridService, a TorrServerConverter for converting P2P streams to TorrServer /stream URLs, integration into AIOStreams processing, wiring in the debrid factory, and registration of TORRSERVER_SERVICE with public exports. (47 words) Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant AIOStreams
participant TorrConv as TorrServerConverter
participant Debrid as TorrServerDebridService
participant TorrAPI as TorrServer API
Client->>AIOStreams: request streams / playback
AIOStreams->>AIOStreams: deduplicate streams
AIOStreams->>TorrConv: convert(streams)
alt TorrServer configured
TorrConv->>TorrConv: build magnet & /stream URL (apikey or Basic auth)
TorrConv-->>AIOStreams: converted streams (type: debrid, cached:true)
else Not configured
TorrConv-->>AIOStreams: streams unchanged
end
AIOStreams->>Debrid: resolve(playbackInfo, filename, cacheAndPlay)
Debrid->>TorrAPI: add magnet / list / stream (with auth)
TorrAPI-->>Debrid: status / files / stream URL
Debrid->>Debrid: poll, select file, construct final URL, cache
Debrid-->>AIOStreams: playback URL
AIOStreams-->>Client: stream available
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
packages/core/src/streams/torrserver-converter.ts (1)
42-49: Consider extracting duplicated API key logic.This
addApiKeyToUrlmethod is duplicated frompackages/core/src/debrid/torrserver.ts(lines 76-83). Both implementations handle API key detection and URL augmentation identically.Consider extracting this to a shared utility function in the TorrServer module to follow the DRY principle and ensure consistency.
packages/core/src/debrid/torrserver.ts (1)
231-247: Hardcoded delay and fallback might need adjustment.After adding a torrent (line 229), the code waits 1 second (lines 231-233) before fetching the torrent list. If the torrent isn't found, it returns a dummy object (lines 240-247).
- The 1-second delay might be insufficient for slower TorrServer instances or under heavy load
- The comment describes this as a "rare race condition", but it might occur more frequently than expected
Consider if a short polling loop (similar to
_resolvelines 310-317) would be more robust than a single retry with dummy fallback.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
packages/core/src/debrid/index.tspackages/core/src/debrid/torrserver.tspackages/core/src/main.tspackages/core/src/streams/index.tspackages/core/src/streams/torrserver-converter.tspackages/core/src/utils/constants.ts
🧰 Additional context used
🧬 Code graph analysis (3)
packages/core/src/debrid/index.ts (1)
packages/core/src/debrid/torrserver.ts (1)
TorrServerDebridService(56-383)
packages/core/src/main.ts (1)
packages/core/src/streams/index.ts (1)
TorrServerConverter(16-16)
packages/core/src/debrid/torrserver.ts (3)
packages/core/src/debrid/base.ts (5)
DebridService(164-194)DebridDownload(87-104)DebridServiceConfig(196-199)DebridError(32-73)PlaybackInfo(160-160)packages/core/src/utils/cache.ts (1)
Cache(24-306)packages/core/src/debrid/utils.ts (1)
selectFileInTorrentOrNZB(161-350)
🔇 Additional comments (13)
packages/core/src/streams/index.ts (1)
7-7: LGTM! Clean module export.The TorrServerConverter is properly imported and exported, following the established pattern for other stream utilities in this module.
Also applies to: 16-16
packages/core/src/debrid/index.ts (1)
7-7: LGTM! Proper service registration.The TorrServer service is correctly wired into the debrid service factory, following the established pattern used by other services like TorBox, NzbDAV, and Altmount.
Also applies to: 18-18, 41-42
packages/core/src/streams/torrserver-converter.ts (2)
21-40: Credentials parsing fails silently – verify this is intentional.When TorrServer credentials fail to parse (line 29), the error is logged but the converter continues with
hasTorrServer = false. This means misconfigured credentials will result in P2P streams not being converted, with only a log entry to indicate the problem.Users may not notice their credentials are invalid unless they check logs. Consider if this graceful degradation is the intended behaviour or if users should receive more visible feedback.
95-103: TorrServer streams marked as instantly cached – verify user expectations.P2P streams converted to TorrServer URLs are marked with
cached: true(line 101) to enable instant playback. While TorrServer does support streaming during download, this assumption may not hold if:
- The torrent has no seeders
- The TorrServer instance is under heavy load
- Network conditions are poor
This matches the behaviour in
checkMagnets()(line 197 in torrserver.ts), but users might experience delays that contradict the "cached" status. Consider if additional health checks would be beneficial.packages/core/src/utils/constants.ts (1)
216-216: LGTM! Service constant properly integrated.The
TORRSERVER_SERVICEconstant is correctly defined, added to the appropriate service arrays, and exported, following the established pattern for other built-in services.Also applies to: 234-234, 251-251, 1339-1339
packages/core/src/main.ts (2)
45-45: LGTM! Converter properly initialised.The TorrServerConverter is correctly imported, declared as a private field, and initialised in the constructor with userData, following the same pattern as other stream processors (fetcher, filterer, deduplicator, etc.).
Also applies to: 94-94, 115-115
1382-1384: Verify converter placement in processing pipeline.The TorrServerConverter runs after deduplication (line 1381) but before filtering and sorting (lines 1390-1399). This means:
- ✅ Avoids converting duplicate P2P streams
- ✅ Converted streams are included in sorting/filtering
- ❓ Runs even when TorrServer isn't configured (relies on early return in convert method)
Consider if there's a performance benefit to checking TorrServer configuration before calling convert, though the current implementation with early return is acceptable.
packages/core/src/debrid/torrserver.ts (6)
28-34: LGTM! Well-designed configuration schema.The TorrServerConfig schema properly validates and normalises the URL by trimming whitespace and removing trailing slashes, preventing common configuration errors.
140-167: ListMagnets errors fail silently with empty array return.When
listMagnets()fails (line 163-166), it logs the error but returns an empty array. This graceful degradation might mask persistent API issues. Consider if callers need to distinguish between "no torrents" and "failed to fetch torrents".However, this may be intentional for non-critical operations.
169-180: LGTM! Well-documented status mapping decision.The comment (lines 174-175) clearly explains why downloading torrents are marked as 'cached' – to enable instant playback without waiting for completion. This aligns with TorrServer's streaming-while-downloading capability.
182-202: CheckMagnets assumes instant availability without verification.The method optimistically returns
cached: truefor all valid magnets (line 197) without checking TorrServer's actual availability or seeder count. This prioritises performance over accuracy.While this aligns with TorrServer's instant-play model, users might encounter delays if torrents have no seeders. This is an acceptable trade-off given that actual verification would require adding each magnet to TorrServer.
271-285: LGTM! Proper concurrency control with distributed locking.Using a distributed lock prevents race conditions when multiple requests try to resolve the same torrent simultaneously. The timeout (30s) and TTL (10s) values are appropriate for this operation.
358-373: Verify security implications of authentication in stream URLs.Line 371 adds authentication to the stream URL using
addApiKeyToUrl(). This means API keys or auth tokens will be exposed in playback URLs that are:
- Returned to clients
- Potentially logged
- May be cached
If these URLs are accessed over HTTP (not HTTPS), credentials could be intercepted. Ensure:
- TorrServer instances use HTTPS in production
- Stream URLs are treated as sensitive and not logged in plaintext
- Users are aware that authentication credentials are embedded in playback URLs
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (3)
packages/core/src/debrid/torrserver.ts (3)
163-174: Consider documenting the 'cached' status assumption.The implementation returns
'cached'for all TorrServer states (0, 1, 2), including the downloading state. Whilst the comment at lines 168-169 explains this decision, this assumption could cause issues if upstream code expects a distinction between actively downloading and fully cached torrents.Consider whether returning
'downloading'for state 1 would provide more accurate status information, or document this design decision in the class-level documentation if the instant-streaming behaviour is intentional and tested.
304-317: Improve file availability polling robustness.The polling logic at lines 304-311 only checks if
filesarray exists and has length > 0, but doesn't verify that:
- Files are actually ready for streaming (TorrServer might still be fetching metadata)
- The file list is complete
- The torrent is in a streamable state
Additionally, line 316 logs a warning about proceeding with "blind stream" when no files are found, but doesn't return
undefinedto signal failure. This could result in broken playback attempts.🔎 Suggested improvements
// Poll until files are populated for (let i = 0; i < TORRSERVER_MAX_POLL_ATTEMPTS; i++) { - if (magnetDownload.files && magnetDownload.files.length > 0) break; + if (magnetDownload.files && magnetDownload.files.length > 0) { + logger.debug(`Files populated for ${hash} after ${i} attempts`); + break; + } await new Promise((resolve) => setTimeout(resolve, TORRSERVER_POLL_INTERVAL_MS)); const list = await this.listMagnets(); const found = list.find(t => t.hash === hash); if (found) magnetDownload = found; } if (!magnetDownload.files?.length) { - // Fallback: If we can't get file list, we can't select file index. - // However, we can try to return a link without index and let TorrServer guess/play first file - logger.warn(`No files found for ${hash}, trying blind stream`); + logger.error(`No files found for ${hash} after ${TORRSERVER_MAX_POLL_ATTEMPTS} attempts`); + return undefined; }
24-26: Consider making TorrServer timing constants configurable.The hardcoded constants for polling intervals and attempts might need adjustment based on:
- TorrServer instance performance
- Network conditions
- Torrent metadata fetch time
Consider exposing these as optional configuration parameters or environment variables (e.g.,
TORRSERVER_POLL_INTERVAL_MS,TORRSERVER_MAX_POLL_ATTEMPTS) to allow users to tune behaviour for their deployment.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/core/src/debrid/torrserver.tspackages/core/src/streams/torrserver-converter.tspackages/core/src/utils/constants.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/core/src/streams/torrserver-converter.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/core/src/debrid/torrserver.ts (4)
packages/core/src/debrid/base.ts (3)
DebridService(164-194)DebridDownload(87-104)DebridError(32-73)packages/core/src/utils/cache.ts (1)
Cache(24-306)packages/core/src/utils/distributed-lock.ts (1)
DistributedLock(27-432)packages/core/src/debrid/utils.ts (1)
selectFileInTorrentOrNZB(161-350)
🔇 Additional comments (5)
packages/core/src/utils/constants.ts (1)
216-216: LGTM: Service constant definition follows established patterns.The
TORRSERVER_SERVICEconstant is correctly defined following the naming convention used by other services in this file.packages/core/src/debrid/torrserver.ts (4)
28-34: LGTM: Config schema uses Zod 4 correctly.The schema properly validates and transforms the TorrServer URL by trimming and removing trailing slashes. The optional
torrserverAuthfield aligns with the flexible authentication approach.
76-83: LGTM: API key handling is correctly scoped.The method correctly distinguishes between Basic auth credentials (containing
:) and API keys, only adding the latter as query parameters.
100-108: LGTM: Basic authentication is correctly implemented for API requests.The code properly Base64-encodes username:password credentials and only sets the Authorization header when the credentials contain a colon, correctly distinguishing between Basic auth and API key authentication patterns.
Based on past review comments, this addresses the previously identified authentication encoding issue.
358-362: No changes needed – TorrServer uses one-based indexing for the /stream endpoint.TorrServer's /stream endpoint uses one-based indexing, as shown in API examples using "index=1". The default value of
'1'correctly references the first file in the torrent, not the second file. The concern about zero-based indexing is not applicable to TorrServer's API implementation.
There was a problem hiding this comment.
Actionable comments posted: 3
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/core/src/debrid/torrserver.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/core/src/debrid/torrserver.ts (3)
packages/core/src/debrid/base.ts (4)
DebridService(164-194)DebridDownload(87-104)DebridError(32-73)PlaybackInfo(160-160)packages/core/src/utils/distributed-lock.ts (1)
DistributedLock(27-432)packages/core/src/debrid/utils.ts (1)
selectFileInTorrentOrNZB(161-350)
🔇 Additional comments (9)
packages/core/src/debrid/torrserver.ts (9)
1-34: LGTM! Clean imports and configuration schema.The Zod v4 schema definition is correct, and the URL transformation to trim and remove trailing slashes is a good practice for normalising user input.
36-54: LGTM! Interface definitions match TorrServer API.The type definitions appropriately model the TorrServer API response structures.
76-103: LGTM! Authentication handling correctly addresses past review feedback.The implementation now properly handles both Basic authentication (username:password) and API key authentication. Line 94 correctly handles passwords that contain colons by splitting only on the first colon occurrence.
105-152: LGTM! Request wrapper with proper authentication.The method correctly handles authentication by setting Basic auth headers only for username:password credentials (lines 121-124) and delegating API key handling to
addApiKeyToUrl(line 128). Error handling properly wraps failures inDebridError.
154-194: LGTM! Torrent listing and status mapping logic is sound.The status mapping at line 190 intentionally treats downloading torrents as 'cached' to allow streaming whilst downloading, which is a key feature of TorrServer.
196-221: LGTM! Instant availability check is appropriate for TorrServer.The
checkMagnetsmethod correctly returns 'cached' status for all valid magnets (line 211), which aligns with TorrServer's instant streaming capability. Hash extraction logic is correct.
223-276: LGTM! Magnet addition with appropriate error handling.The method properly validates the magnet, adds it to TorrServer, waits for processing, and handles race conditions by returning a dummy object if the torrent isn't immediately found (lines 254-261).
285-299: LGTM! Distributed lock prevents concurrent resolution.The 30-second timeout with 10-second TTL is reasonable for torrent resolution operations and prevents race conditions.
373-396: LGTM! Stream URL construction correctly addresses past review feedback.Lines 374 and 385 correctly implement the fixes from past reviews:
- Line 374 uses the torrent hash instead of the full magnet link, reducing payload size
- Line 385 adds authentication credentials via
addAuthToStreamUrl, supporting both Basic auth and API keysThe caching strategy with TTL is appropriate for playback links.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
packages/core/src/debrid/torrserver.ts (3)
62-64: Unused cache instance.
checkCacheis instantiated but never used anywhere in this class. Consider removing it to avoid unnecessary resource allocation, or implement caching incheckMagnetsif intended.🔎 Proposed fix
- private static checkCache = Cache.getInstance<string, DebridDownload>( - 'ts:instant-check' - );
147-161: Handle non-JSON error responses gracefully.If TorrServer returns a non-JSON response (e.g., HTML error page or empty body),
response.json()will throw a cryptic JSON parsing error. Consider catching this scenario for clearer error messages.🔎 Proposed fix
if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); + const text = await response.text(); + throw new Error(`HTTP ${response.status}: ${response.statusText} - ${text.slice(0, 200)}`); } - return (await response.json()) as T; + const text = await response.text(); + if (!text) { + return {} as T; + } + try { + return JSON.parse(text) as T; + } catch { + throw new Error(`Invalid JSON response from TorrServer: ${text.slice(0, 200)}`); + } } catch (error: any) {
229-232: Support both hexadecimal and Base32 encoded info hashes in magnet URIs.The current regex only matches 40-character hexadecimal hashes. BitTorrent standards specify that info hashes can also be encoded as 32-character Base32 strings using the alphabet A–Z and 2–7 (RFC 3548). Valid magnets like
magnet:?xt=urn:btih:KRWPCX3LJWCMM6WJG24CXJWVKQ7ZNNUHwill be silently rejected at line 217.Consider updating the implementation to handle both formats:
private extractHashFromMagnet(magnet: string): string | null { - const match = magnet.match(/btih:([a-fA-F0-9]{40})/i); - return match ? match[1].toLowerCase() : null; + const hexMatch = magnet.match(/btih:([a-fA-F0-9]{40})/i); + if (hexMatch) return hexMatch[1].toLowerCase(); + + const base32Match = magnet.match(/btih:([A-Za-z2-7]{32})/i); + if (base32Match) return base32Match[1].toUpperCase(); + + return null; }
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/core/src/debrid/torrserver.tspackages/core/src/streams/torrserver-converter.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/core/src/streams/torrserver-converter.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/core/src/debrid/torrserver.ts (7)
packages/core/src/utils/logger.ts (1)
createLogger(84-132)packages/core/src/debrid/base.ts (3)
DebridService(164-194)DebridDownload(87-104)PlaybackInfo(160-160)packages/core/src/utils/cache.ts (1)
Cache(24-306)packages/core/src/utils/torrent.ts (1)
torrent(120-212)packages/core/src/utils/distributed-lock.ts (1)
DistributedLock(27-432)packages/core/src/debrid/utils.ts (1)
selectFileInTorrentOrNZB(161-350)packages/core/src/utils/env.ts (1)
Env(318-2071)
🔇 Additional comments (10)
packages/core/src/debrid/torrserver.ts (10)
1-27: LGTM!Imports are well-organised and constants for polling are sensible defaults for TorrServer operations.
28-55: LGTM!The Zod schema properly normalises the URL by trimming whitespace and removing trailing slashes. The interfaces correctly model the TorrServer API responses.
69-84: LGTM!The constructor properly handles JSON parsing errors with a descriptive message, addressing the previous review feedback. Zod validation provides type safety for the parsed config.
95-113: LGTM!The
addAuthToStreamUrlmethod correctly handles both authentication modes and properly splits on the first colon to support passwords containing colons. This addresses the previous review feedback about stream URL authentication.
164-192: LGTM!The method correctly handles both array and object response formats from TorrServer, and gracefully returns an empty array on failure rather than propagating errors.
194-205: LGTM!Good documentation explaining the design decision to treat downloading state as 'cached', since TorrServer supports streaming while downloading.
207-227: LGTM!The implementation correctly reflects TorrServer's streaming model where torrents can be played immediately. Returning 'cached' status triggers instant play logic as intended.
234-287: LGTM!The method handles the asynchronous nature of TorrServer well by adding a delay before listing. The fallback return for race conditions is a sensible defensive approach.
289-294: LGTM!Pass-through implementation is appropriate for TorrServer, which handles links directly without additional transformations.
312-419: LGTM!The resolution workflow is well-structured with proper caching, polling with timeout, and robust file selection. Previous review feedback has been addressed:
parseTorrentTitleis wrapped in try-catch, default index is 0, and the stream URL uses the hash instead of the full magnet link.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
packages/core/src/streams/torrserver-converter.ts (1)
126-133: Consider making this method static.The
buildMagnetLinkmethod doesn't access any instance state, so it could be declared asprivate staticfor clarity and to indicate its stateless nature.🔎 Suggested refactor
- private buildMagnetLink(infoHash: string, trackers: string[]): string { + private static buildMagnetLink(infoHash: string, trackers: string[]): string { let magnet = `magnet:?xt=urn:btih:${infoHash}`; if (trackers && trackers.length > 0) { const encodedTrackers = trackers.map((t) => encodeURIComponent(t)); magnet += `&tr=${encodedTrackers.join('&tr=')}`; } return magnet; }Then update the call on line 75:
- const magnet = this.buildMagnetLink( + const magnet = TorrServerConverter.buildMagnetLink( infoHash, stream.torrent.sources || [] );
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/core/src/streams/torrserver-converter.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/core/src/streams/torrserver-converter.ts (3)
packages/core/src/utils/constants.ts (2)
TORRSERVER_SERVICE(1339-1339)ServiceId(254-254)packages/core/src/debrid/torrserver.ts (1)
TorrServerConfig(29-35)packages/core/src/db/schemas.ts (1)
ParsedStream(778-778)
🔇 Additional comments (6)
packages/core/src/streams/torrserver-converter.ts (6)
1-5: LGTM! Imports and logger properly configured.The imports are correct, including the properly resolved TORRSERVER_SERVICE constant from the centralized constants file. Logger initialization follows the standard pattern.
7-16: LGTM! Clean class structure and initialization.The class structure appropriately encapsulates TorrServer configuration state and follows good initialization patterns.
18-37: LGTM! Robust initialization with proper error handling.The initialization method correctly:
- Uses optional chaining to safely check for configured services
- Validates credentials with Zod schema parsing
- Handles parsing errors gracefully with appropriate logging
- Sets up the converter state only when configuration is valid
39-57: Excellent authentication handling implementation!This method is particularly well-implemented:
- Correctly handles passwords containing colons by splitting only on the first occurrence (lines 48-50)
- Appropriately uses the URL object's username/password properties for Basic Auth
- Cleanly distinguishes between API key and Basic Auth based on the presence of a colon
- Includes early returns for undefined or empty auth
59-124: LGTM! Comprehensive conversion logic with proper safeguards.The conversion method correctly:
- Guards against unconfigured TorrServer with an early return
- Filters streams appropriately (P2P type, has infoHash, no existing URL)
- Applies the fileIdx offset (+ 1) that was addressed in previous reviews
- Constructs proper TorrServer URLs with all required query parameters
- Applies authentication to the playback URL
- Transforms streams to 'debrid' type with appropriate service metadata
- Provides useful logging for conversion activity
The non-null assertion on line 81 is justified by the guard on line 60.
136-136: LGTM! Appropriate default export.The class is correctly exported as the default export for the module.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
packages/core/src/streams/torrserver-converter.ts (1)
59-133: LGTM! Solid conversion logic with resolved off-by-one issue.The conversion logic is well-implemented:
- The fileIdx handling now correctly applies a +1 offset (line 89), addressing the previous review concern
- The magnet link construction properly encodes trackers
- Authentication is correctly applied to the generated URLs
- The guard clauses ensure safe operation
One minor note: The type assertion on line 108 (
as ServiceId) is redundant. SinceTORRSERVER_SERVICEis already typed as the literal'torrserver'and is included in theSERVICESarray (which defines theServiceIdtype), it's already assignable toServiceIdwithout the assertion. You can safely remove it for cleaner code.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/core/src/streams/torrserver-converter.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/core/src/streams/torrserver-converter.ts (4)
packages/core/src/utils/logger.ts (1)
createLogger(84-132)packages/core/src/utils/constants.ts (1)
TORRSERVER_SERVICE(1339-1339)packages/core/src/debrid/torrserver.ts (1)
TorrServerConfig(29-35)packages/core/src/db/schemas.ts (1)
ParsedStream(778-778)
🔇 Additional comments (4)
packages/core/src/streams/torrserver-converter.ts (4)
1-5: LGTM! Clean imports and logger setup.The imports are well-organised, and the previous issue with importing
TORRSERVER_SERVICEhas been properly addressed.
7-16: LGTM! Well-structured class design.The class encapsulation is appropriate, with private fields and a clean constructor that delegates to the initialisation method.
18-37: LGTM! Robust service initialisation.The method correctly locates and validates the TorrServer service configuration. Error handling is appropriate for this use case—logging failures without throwing allows graceful degradation when TorrServer isn't properly configured.
39-57: LGTM! Thoughtful authentication handling.The method correctly distinguishes between basic authentication and API key formats. The special handling for passwords containing colons (lines 48-50) demonstrates good attention to edge cases.
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.