The refresh token grants full read/write/delete access to the user's Drive.
The settings-tab field displayed it in plaintext, so anyone screenshotting
or screen-sharing the NeoGDSync settings tab would leak it. Masked as a
password input with a show/hide toggle.
Claude-Session: https://claude.ai/code/session_011DimwV9zDr1ViCXRCZPxC9
Co-authored-by: Claude <noreply@anthropic.com>
Correctness:
- Edits made while a sync runs are no longer dropped. Vault event handlers
now ignore only paths the sync itself wrote (Syncer.syncWrites) instead of
suppressing all events behind a global flag, and pending ops are cleared
per-op right after success instead of in a bulk sweep that also wiped
ops re-queued mid-sync.
- Renames now propagate to Drive: handlePush compares the Drive-side name
and calls files.rename, so a local a.md -> b.md no longer leaves the old
name on Drive (which later caused duplicate uploads after a rebuild).
- A 'modify' op with no index entry (index lost/reset) looks the file up on
Drive by path before uploading, preventing duplicate files on Drive.
- Excluded paths queued in pendingOps are cleared instead of sticking in
the "N pending" counter forever; exclusion logic is now shared between
event handlers and the sync engine (src/exclude.ts), so user glob
patterns apply consistently. computeDiff no longer reports
newly-excluded snapshot entries as deletions.
- Files first seen via unknown Drive changes (syncedAt=0) now go through
conflict handling instead of silently overwriting the remote copy.
- index.db is written via tmp-file + rename with recovery on load, so a
crash mid-save can no longer silently reset the index.
- Multipart upload boundary is randomized; a file containing the fixed
boundary string no longer corrupts the upload.
Auth / settings:
- authProxyUrl is actually used now (it was silently ignored) and is
editable in settings; DriveApi credentials update in place so PathIndex
never holds a stale instance; token cache is keyed by token+proxy.
- Exclude patterns are editable in settings (README advertised this but
there was no UI).
Performance / UX:
- Force Pull and pull-new-from-Drive download with a small concurrency
pool (settings.concurrency, previously dead config).
- Unknown-change resolution fetches each file's metadata once instead of
twice.
- First run with a non-empty vault shows a notice explaining that an
initial Force Push/Pull is required.
Security / hygiene:
- OAuth proxy escapes HTML in the callback pages (reflected XSS via
?error=...), and README now accurately describes that token refresh goes
through the proxy and how to self-host it.
- Removed stale compiled artifacts (src/*.js) and dead code
(listRevisions); fixed package.json metadata (MIT, author, scripts).
- Added vitest with unit tests for exclusion, snapshot diffing, and index
persistence/recovery (17 tests).
Claude-Session: https://claude.ai/code/session_011DimwV9zDr1ViCXRCZPxC9
Co-authored-by: Claude <noreply@anthropic.com>
1. Cross-folder rename: handlePush now calls drive.moveFile when op='create'
and a cached driveId exists (index.rename already ran). Previously,
updateFile only updated content — the Drive file stayed in the old parent
folder and was deleted when that folder was trashed.
New DriveApi.moveFile uses addParents/removeParents to atomically relocate
the file on Drive.
2. handleDelete no longer swallows Drive API errors silently. Failures are
now surfaced in result.errors and the index entry is preserved so the
next sync can retry, instead of orphaning the Drive file forever.
3. getChanges fields now include 'size' and confirm 'trashed' is requested
(aligns compiled JS with existing TS source).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
smartSync was checking only c.removed (permanent delete) to trigger
local deletion, but Drive changes API returns removed=false with
file.trashed=true for soft-trashed files. Other devices never received
the deletion signal when a folder/file was trashed.
Fix: treat c.file.trashed===true as removed in driveChanged map.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix: manifest description now lowercase (smart/push/pull) to match
community-plugins.json and satisfy ObsidianReviewBot check
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 'Vault root folder ID' and 'Google Drive folder ID' → keep uppercase ID
- Add eslint-plugin-obsidianmd config for local pre-commit checks
- All obsidianmd lint rules now pass: 0 errors on src/*.ts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>