### Google Drive API Client Factory Source: https://context7.com/richardx366/obsidian-google-drive/llms.txt Creates a Google Drive API v3 client instance, pre-scoped to the current vault. It uses `ky` for HTTP requests and automatically injects Bearer tokens. Queries are filtered to the specific vault and exclude trashed items. ```typescript import { getDriveClient } from "helpers/drive"; const drive = getDriveClient(plugin); // Search files with flexible query DSL const files = await drive.searchFiles({ matches: [ { mimeType: { not: "application/vnd.google-apps.folder" } }, { modifiedTime: { gt: new Date(Date.now() - 86400000).toISOString() } }, ], include: ["id", "name", "modifiedTime", "properties"], }); // Returns: FileMetadata[] filtered to current vault only // Upload a new file const id = await drive.uploadFile( new Blob([fileContent], { type: "text/markdown" }), "my-note.md", parentFolderId, { properties: { path: "Notes/my-note.md", vault: "MyVault" }, modifiedTime: new Date().toISOString(), } ); // Update existing file content await drive.updateFile(existingFileId, new Blob([newContent]), { modifiedTime: new Date().toISOString(), }); // Update metadata only (no content change) await drive.updateFileMetadata(fileId, { starred: true }); // Get paginated change feed since a token const changes = await drive.getChanges(plugin.settings.changesToken); // Returns: [{ kind, removed, file: FileMetadata, fileId, time }] // Batch-delete multiple files in a single HTTP request await drive.batchDelete(["fileId1", "fileId2", "fileId3"]); // Get a fresh changes start token (called after every sync) const token = await drive.getChangesStartToken(); ``` -------------------------------- ### getConfigFilesToSync Source: https://context7.com/richardx366/obsidian-google-drive/llms.txt Scans the Obsidian config directory (`.obsidian/`) for files modified since the last sync, excluding layout files and only including specific plugin files. ```APIDOC ## `getConfigFilesToSync()` — Configuration File Discovery Scans the Obsidian config directory (`.obsidian/`) for files modified since the last sync. Excludes layout files (`graph.json`, `workspace.json`, `workspace-mobile.json`). For plugins, only syncs `manifest.json`, `styles.css`, `main.js`, and `data.json`. ```typescript const configFiles = await plugin.drive.getConfigFilesToSync(); // Returns string[] of relative paths, e.g.: // [ // ".obsidian/app.json", // ".obsidian/hotkeys.json", // ".obsidian/plugins/dataview/data.json", // ".obsidian/plugins/dataview/main.js", // ] // Excluded: ".obsidian/workspace.json", ".obsidian/graph.json" // Only files with mtime > plugin.settings.lastSyncedAt are included ``` ``` -------------------------------- ### batchAsyncs Source: https://context7.com/richardx366/obsidian-google-drive/llms.txt Executes an array of async factory functions in batches to manage Google Drive API rate limits and network load. The default batch size is 10. ```APIDOC ## `batchAsyncs(requests, batchSize)` — Concurrency Limiter Executes an array of async factory functions in batches of `batchSize` (default 10) to avoid hitting Google Drive API rate limits or overwhelming the network. ```typescript import { batchAsyncs } from "helpers/drive"; const fileIds = ["id1", "id2", /* ...hundreds more */ "id100"]; const results = await batchAsyncs( fileIds.map((id) => () => drive.getFileMetadata(id)), 10 // process 10 concurrent requests at a time ); // results = [FileMetadata, FileMetadata, ...] // Total: 10 parallel batches sequentially, not 100 simultaneous requests ``` ``` -------------------------------- ### Vault Write Helpers Source: https://context7.com/richardx366/obsidian-google-drive/llms.txt Provides methods to write files to the local vault while suppressing event handlers, ensuring synchronization integrity. ```APIDOC ## Vault Write Helpers ### Description Three methods on the plugin class that write files to the local vault while suppressing the vault event handlers (to avoid re-queuing synced changes). All accept an optional `modificationDate` to preserve the remote `modifiedTime`. ### `createFile(path, content, modificationDate)` Creates a brand-new binary file. - **Parameters**: - `path` (string) - The path for the new file. - `content` (ArrayBuffer) - The content of the file. - `modificationDate` (string, optional) - The modification date to preserve. ```typescript // Create a brand-new binary file (suppresses "create" event tracking) await plugin.createFile( "Notes/synced-note.md", arrayBuffer, // ArrayBuffer from Drive API "2024-03-15T10:30:00Z" // preserves Drive modifiedTime ); ``` ### `modifyFile(file, content, modificationDate)` Modifies an existing TFile. - **Parameters**: - `file` (TFile) - The file object to modify. - `content` (ArrayBuffer) - The new content for the file. - `modificationDate` (string, optional) - The modification date to preserve. ```typescript // Modify an existing TFile (suppresses "modify" event tracking) const file = plugin.app.vault.getFileByPath("Notes/existing.md"); await plugin.modifyFile(file, arrayBuffer, new Date("2024-03-15")); ``` ### `upsertFile(path, content, modificationDate)` Upserts a file using the adapter, bypassing the Obsidian file cache. Primarily used for configuration files. - **Parameters**: - `path` (string) - The path for the file. - `content` (ArrayBuffer) - The content of the file. - `modificationDate` (number, optional) - The modification date to preserve. ```typescript // Upsert via adapter (bypasses Obsidian file cache — used for config files) await plugin.upsertFile( ".obsidian/plugins/my-plugin/data.json", configArrayBuffer, Date.now() ); ``` ``` -------------------------------- ### Discover Modified Configuration Files for Sync Source: https://context7.com/richardx366/obsidian-google-drive/llms.txt The `getConfigFilesToSync` function scans the `.obsidian/` directory to find configuration files that have been modified since the last sync. It excludes layout files and selectively syncs plugin-specific files like `manifest.json`, `styles.css`, `main.js`, and `data.json`. ```typescript const configFiles = await plugin.drive.getConfigFilesToSync(); // Returns string[] of relative paths, e.g.: // [ // ".obsidian/app.json", // ".obsidian/hotkeys.json", // ".obsidian/plugins/dataview/data.json", // ".obsidian/plugins/dataview/main.js", // ] // Excluded: ".obsidian/workspace.json", ".obsidian/graph.json" // Only files with mtime > plugin.settings.lastSyncedAt are included ``` -------------------------------- ### getDriveClient(t) Source: https://context7.com/richardx366/obsidian-google-drive/llms.txt Factory function to obtain a Google Drive API v3 client instance, pre-scoped to the current vault. ```APIDOC ## getDriveClient(t) ### Description Factory that returns a collection of Google Drive API v3 methods pre-scoped to the current vault (all queries automatically append `properties has { key='vault' and value='' } and trashed=false`). Uses `ky` as the HTTP client with automatic Bearer token injection. ### Usage ```typescript import { getDriveClient } from "helpers/drive"; const drive = getDriveClient(plugin); ``` ### Methods #### `searchFiles(options)` Searches files with a flexible query DSL. - **Parameters**: - `options` (object) - Search criteria. - `matches` (array) - Conditions for matching files. - `mimeType` (object) - Filter by MIME type. - `modifiedTime` (object) - Filter by modification time. - `include` (array) - Fields to include in the response. - **Returns**: `FileMetadata[]` filtered to the current vault. ```typescript const files = await drive.searchFiles({ matches: [ { mimeType: { not: "application/vnd.google-apps.folder" } }, { modifiedTime: { gt: new Date(Date.now() - 86400000).toISOString() } }, ], include: ["id", "name", "modifiedTime", "properties"], }); ``` #### `uploadFile(content, name, parentFolderId, metadata)` Uploads a new file to Google Drive. - **Parameters**: - `content` (Blob) - The file content. - `name` (string) - The name of the file. - `parentFolderId` (string) - The ID of the parent folder. - `metadata` (object) - Additional metadata for the file. - `properties` (object) - Custom properties. - `modifiedTime` (string) - The modification time. - **Returns**: `string` - The ID of the uploaded file. ```typescript const id = await drive.uploadFile( new Blob([fileContent], { type: "text/markdown" }), "my-note.md", parentFolderId, { properties: { path: "Notes/my-note.md", vault: "MyVault" }, modifiedTime: new Date().toISOString(), } ); ``` #### `updateFile(fileId, content, metadata)` Updates the content of an existing file. - **Parameters**: - `fileId` (string) - The ID of the file to update. - `content` (Blob) - The new file content. - `metadata` (object) - Metadata to update. - `modifiedTime` (string) - The new modification time. ```typescript await drive.updateFile(existingFileId, new Blob([newContent]), { modifiedTime: new Date().toISOString(), }); ``` #### `updateFileMetadata(fileId, metadata)` Updates only the metadata of an existing file. - **Parameters**: - `fileId` (string) - The ID of the file. - `metadata` (object) - The metadata to update (e.g., `starred: true`). ```typescript await drive.updateFileMetadata(fileId, { starred: true }); ``` #### `getChanges(changesToken)` Retrieves a paginated feed of changes since a given token. - **Parameters**: - `changesToken` (string) - The token representing the point in time to retrieve changes from. - **Returns**: `Array<{ kind: string, removed: boolean, file: FileMetadata, fileId: string, time: string }>` ```typescript const changes = await drive.getChanges(plugin.settings.changesToken); ``` #### `batchDelete(fileIds)` Deletes multiple files in a single batch request. - **Parameters**: - `fileIds` (array) - An array of file IDs to delete. ```typescript await drive.batchDelete(["fileId1", "fileId2", "fileId3"]); ``` #### `getChangesStartToken()` Retrieves a fresh token to mark the starting point for future change feed requests. - **Returns**: `string` - The new changes start token. ```typescript const token = await drive.getChangesStartToken(); ``` ``` -------------------------------- ### Push Local Changes to Google Drive - TypeScript Source: https://context7.com/richardx366/obsidian-google-drive/llms.txt Uploads pending local changes (create, modify, delete) to Google Drive after a pull to prevent conflicts. It processes operations in batches and syncs configuration files. A confirmation modal lists all changes, allowing individual operations to be removed before proceeding. ```typescript import { push } from "helpers/push"; // Triggered by ribbon icon menu or command: "Push to Google Drive" await push(plugin); // Opens ConfirmPushModal listing pending operations, e.g.: // Create: Notes/new-note.md // Modify: Notes/existing-note.md // Delete: Archive/old-note.md // User clicks Confirm → proceeds, or can remove individual operations via trash icon // Progress: "Syncing (0%)" → "Syncing (33%)" (deletes) → "Syncing (33-66%)" (creates) → "Syncing (66-99%)" (modifies) // On completion: Notice("Sync complete!") ``` -------------------------------- ### Vault Write Helpers: createFile, modifyFile, upsertFile Source: https://context7.com/richardx366/obsidian-google-drive/llms.txt Plugin methods to write files to the local vault while suppressing Obsidian's event handlers, preventing re-queuing of synced changes. An optional `modificationDate` can be provided to maintain the remote `modifiedTime`. ```typescript // Create a brand-new binary file (suppresses "create" event tracking) await plugin.createFile( "Notes/synced-note.md", arrayBuffer, // ArrayBuffer from Drive API "2024-03-15T10:30:00Z" // preserves Drive modifiedTime ); // Modify an existing TFile (suppresses "modify" event tracking) const file = plugin.app.vault.getFileByPath("Notes/existing.md"); await plugin.modifyFile(file, arrayBuffer, new Date("2024-03-15")); // Upsert via adapter (bypasses Obsidian file cache — used for config files) await plugin.upsertFile( ".obsidian/plugins/my-plugin/data.json", configArrayBuffer, Date.now() ); ``` -------------------------------- ### Plugin Settings Interface - TypeScript Source: https://context7.com/richardx366/obsidian-google-drive/llms.txt Defines the structure for persistent plugin state, including authentication tokens, operation queues, and synchronization tracking. Use this interface to understand the data stored in Obsidian's `data.json`. ```typescript interface PluginSettings { refreshToken: string; // OAuth2 refresh token operations: Record; // Pending local changes driveIdToPath: Record; // Drive file ID → vault path lastSyncedAt: number; // Epoch ms of last sync changesToken: string; // Google Drive changes page token } // Default state on first install const DEFAULT_SETTINGS: PluginSettings = { refreshToken: "", operations: {}, driveIdToPath: {}, lastSyncedAt: 0, changesToken: "", }; ``` -------------------------------- ### reset(t) Source: https://context7.com/richardx366/obsidian-google-drive/llms.txt Resets the local vault to match the state of Google Drive. This action is destructive and requires user confirmation. ```APIDOC ## reset(t) ### Description Discards all pending local operations and overwrites the local vault with the Google Drive state. Locally-created files are deleted, locally-modified files are reverted to the Drive version, and locally-deleted files are restored from Drive. Shows a destructive-action confirmation modal before proceeding. ### Usage ```typescript import { reset } from "helpers/reset"; // Triggered by ribbon menu or command: "Reset local vault to Google Drive" await reset(plugin); ``` ### Confirmation Modal Opens `ConfirmResetModal` with the message: "Are you sure you want to reset the data from Google Drive? You'll lose all the local changes... This step is irreversible." ### Actions on Reset - Deletes locally-created files - Reverts locally-modified files to Drive content - Restores locally-deleted files from Drive ### Completion On completion, a notice "Reset complete." is shown. ``` -------------------------------- ### Group Folders by Depth for Ordered Operations Source: https://context7.com/richardx366/obsidian-google-drive/llms.txt The `foldersToBatches` utility groups folders by their path depth, ensuring that parent folders are processed before their children. This is crucial for operations like creating or deleting folders in a hierarchical structure. ```typescript import { foldersToBatches } from "helpers/drive"; const folders = ["a/b/c", "a/b", "a", "x/y"]; const batches = foldersToBatches(folders); // batches = [ // ["a", "x/y"], // depth 1 — created first // ["a/b"], // ["a/b/c"], // depth 3 — created last // ] // Used in push() to create Google Drive folders in correct order: for (const batch of batches) { await batchAsyncs( batch.map((folder) => () => drive.createFolder({ name: folder.split("/").pop(), parent: parentId })) ); } ``` -------------------------------- ### Batch Execute Async Functions with Concurrency Limit Source: https://context7.com/richardx366/obsidian-google-drive/llms.txt Use `batchAsyncs` to execute an array of async factory functions in batches. This prevents overwhelming the network or hitting API rate limits, especially for operations like fetching file metadata. ```typescript import { batchAsyncs } from "helpers/drive"; const fileIds = ["id1", "id2", /* ...hundreds more */ "id100"]; const results = await batchAsyncs( fileIds.map((id) => () => drive.getFileMetadata(id)), 10 // process 10 concurrent requests at a time ); // results = [FileMetadata, FileMetadata, ...] // Total: 10 parallel batches sequentially, not 100 simultaneous requests ``` -------------------------------- ### Pull Changes from Google Drive - TypeScript Source: https://context7.com/richardx366/obsidian-google-drive/llms.txt Fetches and applies changes from Google Drive, including file updates, deletions, and new files. It handles conflict resolution by favoring local modifications and propagates remote deletions. Use `true` for `silenceNotices` to suppress UI feedback during automatic syncs. ```typescript import { pull } from "helpers/pull"; // Triggered automatically on Obsidian startup (silenced) await pull(plugin, true); // silenceNotices=true: no UI notices shown // Triggered by ribbon menu or command palette (with progress notices) await pull(plugin); // Shows: "Syncing (0%)" → "Syncing (33%)" → "Syncing (66-100%)" // On completion: Notice("Files have been synced from Google Drive!") // On no changes: Notice("You're up to date!") // On error: Notice("An error occurred fetching Google Drive files.") ``` -------------------------------- ### Vault Event Handlers Source: https://context7.com/richardx366/obsidian-google-drive/llms.txt Handles changes within the Obsidian vault by tracking file system activity and updating the operations record. ```APIDOC ## Vault Event Handlers ### Description Four event handlers on the Obsidian vault observe all file system activity and update the `operations` record. The handlers de-duplicate redundant operations (e.g., create+delete = no-op; delete+create = modify) and debounce settings saves by 500 ms. ### Handlers Registered in `onload()`: - `vault.on("create", plugin.handleCreate.bind(plugin));` - `vault.on("delete", plugin.handleDelete.bind(plugin));` - `vault.on("modify", plugin.handleModify.bind(plugin));` - `vault.on("rename", plugin.handleRename.bind(plugin));` ### Logic Examples ```typescript // Create operation plugin.handleCreate({ path: "Notes/new.md" }); // operations["Notes/new.md"] = "create" // Delete operation (previously created) plugin.handleDelete({ path: "Notes/new.md" }); // previously "create" // delete operations["Notes/new.md"] → no-op (never existed on Drive) // Modify operation plugin.handleModify({ path: "Notes/existing.md" }); // operations["Notes/existing.md"] = "modify" (skipped if already "create" or "modify") // Rename operation plugin.handleRename({ path: "Notes/renamed.md" }, "Notes/old.md"); // operations["Notes/old.md"] = "delete" // operations["Notes/renamed.md"] = "create" ``` ``` -------------------------------- ### Reset Local Vault to Google Drive State Source: https://context7.com/richardx366/obsidian-google-drive/llms.txt Discards local changes and synchronizes the vault with Google Drive. This action is destructive and requires user confirmation. It deletes local-only files, reverts modified files, and restores deleted files. ```typescript import { reset } from "helpers/reset"; // Triggered by ribbon menu or command: "Reset local vault to Google Drive" await reset(plugin); // Opens ConfirmResetModal: // "Are you sure you want to reset the data from Google Drive? // You'll lose all the local changes... This step is irreversible." // User clicks RESET! → proceeds // - Deletes locally-created files // - Reverts locally-modified files to Drive content // - Restores locally-deleted files from Drive // On completion: Notice("Reset complete.") ``` -------------------------------- ### foldersToBatches Source: https://context7.com/richardx366/obsidian-google-drive/llms.txt Groups folders by path depth to ensure parent folders are processed before their children. Returns an array of arrays, ordered from shallowest to deepest. ```APIDOC ## `foldersToBatches(folders)` — Depth-Ordered Folder Batching Groups folders by path depth so parent folders are always created/deleted before their children. Returns an array of arrays where index 0 = shallowest depth. ```typescript import { foldersToBatches } from "helpers/drive"; const folders = ["a/b/c", "a/b", "a", "x/y"]; const batches = foldersToBatches(folders); // batches = [ // ["a", "x/y"], // depth 1 — created first // ["a/b"], // depth 2 // ["a/b/c"], // depth 3 — created last // ] // Used in push() to create Google Drive folders in correct order: for (const batch of batches) { await batchAsyncs( batch.map((folder) => () => drive.createFolder({ name: folder.split("/").pop(), parent: parentId })) ); } ``` ``` -------------------------------- ### Vault Event Handlers for Change Tracking Source: https://context7.com/richardx366/obsidian-google-drive/llms.txt Registers handlers for Obsidian vault events (create, delete, modify, rename) to track file system changes. These handlers de-duplicate operations and debounce settings saves. ```typescript // Registered in onload(): vault.on("create", plugin.handleCreate.bind(plugin)); vault.on("delete", plugin.handleDelete.bind(plugin)); vault.on("modify", plugin.handleModify.bind(plugin)); vault.on("rename", plugin.handleRename.bind(plugin)); // Logic examples: plugin.handleCreate({ path: "Notes/new.md" }); // operations["Notes/new.md"] = "create" plugin.handleDelete({ path: "Notes/new.md" }); // previously "create" // delete operations["Notes/new.md"] → no-op (never existed on Drive) plugin.handleModify({ path: "Notes/existing.md" }); // operations["Notes/existing.md"] = "modify" (skipped if already "create" or "modify") plugin.handleRename({ path: "Notes/renamed.md" }, "Notes/old.md"); // operations["Notes/old.md"] = "delete" // operations["Notes/renamed.md"] = "create" ``` -------------------------------- ### OAuth Token Refresh - TypeScript Source: https://context7.com/richardx366/obsidian-google-drive/llms.txt Exchanges a refresh token for a short-lived access token using the proxy server. The access token is cached and automatically refreshed when nearing expiry. Handles authentication errors by clearing the refresh token. ```typescript import { refreshAccessToken } from "helpers/ky"; // Called automatically before each Drive API request (via ky beforeRequest hook) // Can also be called manually to pre-warm the token const token = await refreshAccessToken(plugin); // token = { token: "ya29.xxx", expiresAt: 1700000000000 } // The ky client auto-injects the Bearer token on every request: // helpers/ky.ts — beforeRequest hook async (request) => { if (t.accessToken.token) { if (t.accessToken.expiresAt - Date.now() < 60000) { await refreshAccessToken(t); // proactive refresh } request.headers.set("Authorization", `Bearer ${t.accessToken.token}`); } return request; } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.