Repository navigation
Separate 404 from other errors - #60
Merged
revett merged 18 commits intoJul 18, 2026
Merged
Conversation
Contributor
|
@kwame-Owusu Could you resolve conflicts please? |
Resolves 8thpark#36. `s3PutObject`, `s3GetObject`, and `s3DeleteObject` interpolate keys into URLs unencoded (`${baseUrl}/${key}`). A filename containing `#` gets truncated at the fragment, `?` starts a query string, and a literal `%` can produce invalid escapes, so requests hit the wrong key or fail. This is inconsistent with `s3ListObjects`, which already encodes its prefix with `encodeURIComponent`. - Add module-private `encodeKey` helper that splits a key on `/`, encodes each segment with `encodeURIComponent`, and rejoins with `/`, preserving path structure while escaping special characters within filenames. - Update `s3PutObject`, `s3GetObject`, and `s3DeleteObject` to pass keys through `encodeKey` before building the request URL. - Add two integration tests against live MinIO: one round-trips a key with space and ampersand (`Foo & Bar.md`), the other with percent and hash (`100% #special.md`). Vault files aren't restricted to URL-safe characters, Obsidian lets users create notes with `#`, `?`, `%`, spaces, and `&` in their names. Without encoding, these keys silently corrupt the request URL or produce 404s. `s3ListObjects` already handled this correctly; the other three functions were the gap.
Resolves 8thpark#36. `s3PutObject`, `s3GetObject`, and `s3DeleteObject` interpolate keys into URLs unencoded (`${baseUrl}/${key}`). A filename containing `#` gets truncated at the fragment, `?` starts a query string, and a literal `%` can produce invalid escapes, so requests hit the wrong key or fail. This is inconsistent with `s3ListObjects`, which already encodes its prefix with `encodeURIComponent`. - Add module-private `encodeKey` helper that splits a key on `/`, encodes each segment with `encodeURIComponent`, and rejoins with `/`, preserving path structure while escaping special characters within filenames. - Update `s3PutObject`, `s3GetObject`, and `s3DeleteObject` to pass keys through `encodeKey` before building the request URL. - Add two integration tests against live MinIO: one round-trips a key with space and ampersand (`Foo & Bar.md`), the other with percent and hash (`100% #special.md`). Vault files aren't restricted to URL-safe characters, Obsidian lets users create notes with `#`, `?`, `%`, spaces, and `&` in their names. Without encoding, these keys silently corrupt the request URL or produce 404s. `s3ListObjects` already handled this correctly; the other three functions were the gap.
Resolves 8thpark#4. ## Problem src/main.ts registers the vault create handler in onload. Obsidian fires create for every file during vault load, so startup schedules a redundant debounced refresh on top of the onLayoutReady one. The documented pattern is to register vault event handlers inside onLayoutReady. ## Changes Move the event handlers inside the `onLayoutReady` callback. ## Why This is the documented pattern and also we do not want to fire these register vault event handlers for every file. --------- Co-authored-by: Charlie Revett <[email protected]>
Resolves 8thpark#36. `s3PutObject`, `s3GetObject`, and `s3DeleteObject` interpolate keys into URLs unencoded (`${baseUrl}/${key}`). A filename containing `#` gets truncated at the fragment, `?` starts a query string, and a literal `%` can produce invalid escapes, so requests hit the wrong key or fail. This is inconsistent with `s3ListObjects`, which already encodes its prefix with `encodeURIComponent`. - Add module-private `encodeKey` helper that splits a key on `/`, encodes each segment with `encodeURIComponent`, and rejoins with `/`, preserving path structure while escaping special characters within filenames. - Update `s3PutObject`, `s3GetObject`, and `s3DeleteObject` to pass keys through `encodeKey` before building the request URL. - Add two integration tests against live MinIO: one round-trips a key with space and ampersand (`Foo & Bar.md`), the other with percent and hash (`100% #special.md`). Vault files aren't restricted to URL-safe characters, Obsidian lets users create notes with `#`, `?`, `%`, spaces, and `&` in their names. Without encoding, these keys silently corrupt the request URL or produce 404s. `s3ListObjects` already handled this correctly; the other three functions were the gap.
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
revett
approved these changes
Jul 18, 2026
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Fixes #41.
Problem
GetResultandListResult(and the other storage result types) collapse every failure intook: falseplus a message string. A 404 (object genuinely absent) is indistinguishable from a 403, 500, or network error without parsing the message. The sync engine must tell "remote file deleted" apart from "transient failure", otherwise it will propagate deletions on a flaky connection.Changes
ResultStatustype ("ok" | "not_found" | "auth" | "server" | "network")to all five storage result types:
ConnectionResult,PutResult,GetResult,DeleteResult,ListResultS3 function (
testConnection,s3PutObject,s3GetObject,s3DeleteObject,s3ListObjects)fieldFrom,decodeXmlText,parseListObjectsXml)into
src/utils/storage/xml.tsmessageFor,statusForHttp) intosrc/utils/storage/errors.tsstatusfieldWhy
The sync engine needs structural error classification to make safe decisions. With
status, a caller canswitchon the outcome:"ok"— use the result"not_found"— safe to treat as remote deletion"auth"— stop, credentials or config need fixing"server"/"network"— retry with backoffThe utility extraction keeps
storage.tsfocused on the S3 client interface and implementation while giving XML parsing and error mapping their own single-concern modules.Greptile Summary
This PR adds a structured
ResultStatustype ("ok" | "not_found" | "auth" | "server" | "network") to all five storage result types and maps HTTP status codes and fetch exceptions to the appropriate status in every S3 function, letting the sync engine distinguish a genuine remote deletion from a transient failure without parsing message strings. It also extracts XML parsing and error-mapping helpers into dedicated single-concern modules undersrc/utils/storage/.ResultStatusis threaded throughConnectionResult,PutResult,GetResult,DeleteResult, andListResult, with every return site updated consistently.statusForHttpinerrors.tscovers 404 →"not_found", 401/403 →"auth", and 500+ →"server", but theif (code >= 500)guard and the final fallback both return"server", leaving the guard as dead code.statusfield, including the"not_found"case on a missing key.Confidence Score: 5/5
Safe to merge — the change is additive, every return site is updated consistently, and tests cover the key status values including not_found.
The core classification logic is correct and all five storage operations are updated. The only finding is dead code in statusForHttp where the if (code >= 500) guard returns the same value as the fallback, which does not affect runtime behaviour.
src/utils/storage/errors.ts — the if (code >= 500) guard is dead code worth cleaning up before the function grows.
Important Files Changed
Reviews (2): Last reviewed commit: "Update src/utils/storage/errors.ts" | Re-trigger Greptile