-
Notifications
You must be signed in to change notification settings - Fork 10
fix(application): harden download-to-setup handoff and processing recovery #249
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
7d1971d
fix(application/electron): wait for completed torrent files before setup
Nat3z 6ff9fcb
feat(application): report moving-files progress during extraction
Nat3z ebc180b
feat(application/frontend): recover post-download processing after a …
Nat3z 2e6f08a
fix(application/frontend): serialize reactive setup payloads before RPC
Nat3z 1d3e271
fix(application): buffer terminal events for already-ready handshakes
Nat3z f75e571
fix(application/frontend): check updates per addon and fork update do…
Nat3z 53a8ce1
fix(application/electron): back up shortcuts.vdf before rewriting
Nat3z 5d30235
fix(application/frontend): mark my-addons tab as selected
Nat3z 5b7ea94
chore: tailwind css data, tsconfig rootDir, yargs override, drop clea…
Nat3z 54019e4
test(application): alias real modules in handshake test instead of stubs
Nat3z a3d43a0
fix(application/frontend): keep checking other addons when one update…
Nat3z f71ace6
fix(application/frontend): write pending recovery only after old_file…
Nat3z 7949447
docs(application/frontend): sync pending-recovery comment with write …
Nat3z 0dd8b86
fix(application/frontend): skip pending recovery when old_files stagi…
Nat3z f965c5d
fix(application/frontend): keep the first addon's update result per app
Nat3z 8a40143
fix(application/frontend): write first recovery after archive classif…
Nat3z File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| { | ||
| "version": 1.1, | ||
| "atDirectives": [ | ||
| { | ||
| "name": "@apply", | ||
| "description": "Inline Tailwind utility classes into a CSS rule." | ||
| }, | ||
| { | ||
| "name": "@config", | ||
| "description": "Load a legacy Tailwind JavaScript configuration file." | ||
| }, | ||
| { | ||
| "name": "@custom-variant", | ||
| "description": "Define a custom Tailwind variant." | ||
| }, | ||
| { | ||
| "name": "@plugin", | ||
| "description": "Load a legacy Tailwind JavaScript plugin." | ||
| }, | ||
| { | ||
| "name": "@reference", | ||
| "description": "Import a Tailwind stylesheet for theme and utility references without emitting CSS." | ||
| }, | ||
| { | ||
| "name": "@source", | ||
| "description": "Register source files for Tailwind class detection." | ||
| }, | ||
| { | ||
| "name": "@tailwind", | ||
| "description": "Insert a Tailwind CSS layer." | ||
| }, | ||
| { | ||
| "name": "@theme", | ||
| "description": "Define Tailwind theme variables." | ||
| }, | ||
| { | ||
| "name": "@utility", | ||
| "description": "Define a custom Tailwind utility." | ||
| }, | ||
| { | ||
| "name": "@variant", | ||
| "description": "Apply a Tailwind variant to CSS rules." | ||
| } | ||
| ] | ||
| } |
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| import { open, stat } from 'node:fs/promises'; | ||
|
|
||
| export interface TorrentFileExpectation { | ||
| path: string; | ||
| length: number; | ||
| } | ||
|
|
||
| interface TorrentFileReadinessOptions { | ||
| timeoutMs?: number; | ||
| intervalMs?: number; | ||
| probe?: (file: TorrentFileExpectation) => Promise<void>; | ||
| } | ||
|
|
||
| async function probeTorrentFile(file: TorrentFileExpectation): Promise<void> { | ||
| const fileStat = await stat(file.path); | ||
| if (fileStat.size !== file.length) { | ||
| throw new Error( | ||
| `Torrent file has size ${fileStat.size}, expected ${file.length}: ${file.path}` | ||
| ); | ||
| } | ||
|
|
||
| const handle = await open(file.path, 'r'); | ||
| await handle.close(); | ||
| } | ||
|
|
||
| /** Wait until WebTorrent's completed files can be safely reopened by setup/seeding. */ | ||
| export async function waitForTorrentFiles( | ||
| files: readonly TorrentFileExpectation[], | ||
| options: TorrentFileReadinessOptions = {} | ||
| ): Promise<void> { | ||
| const timeoutMs = options.timeoutMs ?? 10_000; | ||
| const intervalMs = options.intervalMs ?? 100; | ||
| const probe = options.probe ?? probeTorrentFile; | ||
| const deadline = Date.now() + timeoutMs; | ||
| let lastError: unknown; | ||
|
|
||
| do { | ||
| try { | ||
| await Promise.all(files.map(probe)); | ||
| return; | ||
| } catch (error) { | ||
| lastError = error; | ||
| if (Date.now() >= deadline) break; | ||
| await new Promise<void>((resolveDelay) => | ||
| setTimeout(resolveDelay, intervalMs) | ||
| ); | ||
| } | ||
| } while (Date.now() <= deadline); | ||
|
|
||
| throw lastError instanceof Error | ||
| ? lastError | ||
| : new Error('Torrent files did not become ready'); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Enforce
timeoutMswhile a probe is pending.Line 39 waits for every probe before it checks the deadline. If
statoropendoes not settle, the download remains indownloadingstate indefinitely. Race each probe attempt against the remaining deadline, clear the timer, and add a never-settlingproberegression test.🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Skipping. The probes are stat and open on local files that WebTorrent just finished writing, and the deadline is checked between rounds. A local stat that never settles would be a filesystem hang that a Promise.race could not actually cancel, so the extra timer would only leak a dangling promise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Nat3z, correct:Promise.racecannot cancel a stalledstatoropencall.The race is not intended to cancel the filesystem operation. It enforces the
waitForTorrentFilestimeout and lets the download workflow fail instead of waiting forever. The timer can be cleared when the race settles. The original probe promise can remain pending, but it does not keep this workflow indownloading.I understand the local-filesystem assumption and the decision to skip the change. The timeout behavior still differs from the configured
timeoutMs, so I will keep the finding open.🐇
✏️ Learnings added
You are interacting with an AI system.