Release 1.5.1

Bug fixes and code quality improvements:

- Fix mirror index dirtied on every sync (MirrorRecord.updatedAt in equality)
- Fix mirror URL corruption: encode calendar/source IDs before joining with ';'
  and use percentEncodedPath to prevent double-encoding
- Fix cleanup route mutating UI calendar picker selection unnecessarily
- Fix --exit flag redundancy (isCLIRun no longer implies termination)
- Remove dead SKIP_ALL_DAY_DEFAULT constant
- Replace deprecated FileHandle(forWritingAtPath:) with throwing variant
- Add EKEventStoreChanged observer for live calendar list refresh
- Extract AppLogStore into its own file (AppLogStore.swift)
- Add Block.span(start🔚) factory; replace verbose nil-field constructions
- Remove redundant MainActor.run{} wrappers inside @MainActor MirrorEngine
- Fix SettingsPayload indentation inside ContentView

All 45 unit tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 15:48:08 +02:00
parent 2c319808c2
commit ad6ae396da
11 changed files with 220 additions and 120 deletions
+8 -4
View File
@@ -57,10 +57,12 @@ func mirrorTimeKey(start: Date, end: Date) -> String {
func buildMirrorURL(targetCalID: String, sourceCalID: String, sourceStableID: String?, occurrence: Date?, start: Date, end: Date) -> URL? {
let sourceID = sourceStableID ?? ""
let occ = occurrence.map { String($0.timeIntervalSince1970) } ?? "-"
// Percent-encode IDs so that any embedded ";" doesn't corrupt the
// semicolon-delimited path when the URL is later parsed.
let parts = [
targetCalID,
sourceCalID,
sourceID,
mirrorURLComponentEncode(targetCalID),
mirrorURLComponentEncode(sourceCalID),
mirrorURLComponentEncode(sourceID),
occ,
String(start.timeIntervalSince1970),
String(end.timeIntervalSince1970)
@@ -68,7 +70,9 @@ func buildMirrorURL(targetCalID: String, sourceCalID: String, sourceStableID: St
var components = URLComponents()
components.scheme = "mirror"
components.host = "x"
components.path = "/" + parts.joined(separator: ";")
// Use percentEncodedPath so URLComponents does not re-encode the already
// percent-encoded IDs (double-encoding would break round-trip parsing).
components.percentEncodedPath = "/" + parts.joined(separator: ";")
return components.url
}