@happyvertical/files 0.105.2 → 0.105.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -47,7 +47,43 @@ await fs.write('documents/readme.txt', 'Hello from Drive');
47
47
  const content = await fs.read('documents/readme.txt');
48
48
  ```
49
49
 
50
- Supports OAuth2, service account keys, and short-lived access tokens. Google Docs native formats are automatically exported (Docs → text, Sheets → CSV, etc.).
50
+ Supports OAuth2, service account keys, and short-lived access tokens. Google Docs native formats are automatically exported (Docs → text, Sheets → CSV, etc.) for the legacy path API.
51
+
52
+ ### Drive catalog discovery and pinned acquisition
53
+
54
+ `GoogleDriveProvider` also exposes ID-based Drive operations for catalog
55
+ adapters. `listMetadataPage()` performs metadata-only, one-folder pagination;
56
+ persist the returned `nextPageToken` and enqueue folder IDs yourself for a
57
+ resumable traversal. It never requests media or exports content.
58
+
59
+ ```typescript
60
+ import { GoogleDriveProvider } from '@happyvertical/files';
61
+
62
+ const drive = new GoogleDriveProvider({
63
+ type: 'gdrive',
64
+ accessToken: process.env.DRIVE_READ_TOKEN,
65
+ folderId: 'configured-source-root',
66
+ // Optional additional source roots and shared-drive support:
67
+ allowedFolderIds: ['configured-source-root'],
68
+ sharedDriveId: 'shared-drive-id',
69
+ });
70
+
71
+ const page = await drive.listMetadataPage({ pageToken: checkpoint });
72
+ // page.entries include stable id, revisionId, parents, driveId, and shortcut metadata.
73
+
74
+ const snapshot = await drive.acquireById(page.entries[0].id, {
75
+ expectedRevision: page.entries[0].revisionId,
76
+ maxBytes: 25 * 1024 * 1024,
77
+ // Native Docs/Sheets/Slides require an intentional export format.
78
+ exportMimeType: 'application/pdf',
79
+ });
80
+ ```
81
+
82
+ `acquireById()` validates ancestor scope against `allowedFolderIds`, validates
83
+ shortcut targets independently, streams with a hard byte bound, checks the
84
+ Drive revision before and after acquisition, and returns a SHA-256 content hash.
85
+ Use `application/pdf` for native files that need layout-preserving printing.
86
+ It never stores credentials in returned metadata.
51
87
 
52
88
  ### File Operations
53
89
 
@@ -1,4 +1,4 @@
1
- import { r as FilesystemError, s as __require } from "./types-OwkcCRpq.js";
1
+ import { o as FilesystemError, u as __require } from "./types-DaP78DEi.js";
2
2
  //#region src/shared/base.ts
3
3
  /**
4
4
  * Abstract base class for filesystem providers.
@@ -174,4 +174,4 @@ var BaseFilesystemProvider = class {
174
174
  //#endregion
175
175
  export { BaseFilesystemProvider as t };
176
176
 
177
- //# sourceMappingURL=base-C9husdYd.js.map
177
+ //# sourceMappingURL=base-CYInq2Yk.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"base-C9husdYd.js","names":[],"sources":["../../src/shared/base.ts"],"sourcesContent":["import {\n type BaseProviderOptions,\n type CacheOptions,\n type CreateDirOptions,\n type DownloadOptions,\n type FileInfo,\n type FileStats,\n type FilesystemCapabilities,\n FilesystemError,\n type FilesystemInterface,\n type ListFilesOptions,\n type ListOptions,\n type ReadOptions,\n type UploadOptions,\n type WriteOptions,\n} from './types';\n\n/**\n * Abstract base class for filesystem providers.\n *\n * Implements {@link FilesystemInterface} with shared logic for path normalization,\n * validation, caching, and legacy method adapters. Concrete providers (e.g.\n * {@link LocalFilesystemProvider}, {@link GoogleDriveProvider}) extend this class\n * and implement the abstract methods for their specific storage backend.\n */\nexport abstract class BaseFilesystemProvider implements FilesystemInterface {\n protected basePath: string;\n protected applyBasePath: boolean;\n protected cacheDir: string;\n protected createMissing: boolean;\n protected providerType: string;\n\n constructor(options: BaseProviderOptions = {}) {\n this.basePath = options.basePath || '';\n this.applyBasePath = options.applyBasePath ?? true;\n // Use a universal cache directory approach - will be context-specific\n this.cacheDir = options.cacheDir || this.getDefaultCacheDir();\n this.createMissing = options.createMissing ?? true;\n this.providerType = this.constructor.name\n .toLowerCase()\n .replace('filesystemprovider', '');\n }\n\n /**\n * Get default cache directory for the current context\n */\n private getDefaultCacheDir(): string {\n // Use context-aware temp directory from utils\n try {\n const { getTempDirectory } = require('@happyvertical/utils');\n return getTempDirectory('files-cache');\n } catch {\n // Fallback if utils not available\n if (process?.versions?.node) {\n try {\n const { tmpdir } = require('node:os');\n const { join } = require('node:path');\n return join(tmpdir(), 'have-sdk', 'files-cache');\n } catch {\n return './tmp/have-sdk/files-cache';\n }\n }\n return './tmp/have-sdk/files-cache';\n }\n }\n\n /**\n * Throw error for unsupported operations\n */\n protected throwUnsupported(operation: string): never {\n throw new FilesystemError(\n `Operation '${operation}' not supported by ${this.providerType} provider`,\n 'ENOTSUP',\n undefined,\n this.providerType,\n );\n }\n\n /**\n * Normalize path by removing leading/trailing slashes and resolving relative paths\n */\n protected normalizePath(path: string): string {\n if (!path) return '';\n\n // Remove leading slash for consistency\n let normalized = path.startsWith('/') ? path.slice(1) : path;\n\n // Combine with base path if configured\n if (this.applyBasePath && this.basePath) {\n normalized = this.joinPaths(this.basePath, normalized);\n }\n\n return normalized;\n }\n\n /**\n * Universal path joining function that works in both Node.js and browser\n */\n private joinPaths(...paths: string[]): string {\n return paths\n .filter((p) => p && p.length > 0)\n .map((p) => p.replace(/^\\/+|\\/+$/g, ''))\n .join('/');\n }\n\n /**\n * Validate that a path is safe (no directory traversal)\n */\n protected validatePath(path: string): void {\n if (!path) {\n throw new FilesystemError('Path cannot be empty', 'EINVAL', path);\n }\n\n // Check for directory traversal attempts\n if (path.includes('..') || path.includes('~')) {\n throw new FilesystemError(\n 'Path contains invalid characters (directory traversal)',\n 'EINVAL',\n path,\n );\n }\n }\n\n /**\n * Get cache key for a given path\n */\n protected getCacheKey(path: string): string {\n return `${this.constructor.name}-${path}`;\n }\n\n /**\n * Abstract methods that must be implemented by providers\n */\n abstract exists(path: string): Promise<boolean>;\n abstract read(path: string, options?: ReadOptions): Promise<string | Buffer>;\n abstract write(\n path: string,\n content: string | Buffer,\n options?: WriteOptions,\n ): Promise<void>;\n abstract delete(path: string): Promise<void>;\n abstract copy(sourcePath: string, destPath: string): Promise<void>;\n abstract move(sourcePath: string, destPath: string): Promise<void>;\n abstract createDirectory(\n path: string,\n options?: CreateDirOptions,\n ): Promise<void>;\n abstract list(path: string, options?: ListOptions): Promise<FileInfo[]>;\n abstract getStats(path: string): Promise<FileStats>;\n abstract getMimeType(path: string): Promise<string>;\n abstract getCapabilities(): Promise<FilesystemCapabilities>;\n\n /**\n * Provider methods with default implementations (may be overridden)\n */\n async upload(\n _localPath: string,\n _remotePath: string,\n _options: UploadOptions = {},\n ): Promise<void> {\n this.throwUnsupported('upload');\n }\n\n async download(\n _remotePath: string,\n _localPath?: string,\n _options: DownloadOptions = {},\n ): Promise<string> {\n this.throwUnsupported('download');\n }\n\n async downloadWithCache(\n remotePath: string,\n options: CacheOptions = {},\n ): Promise<string> {\n const cacheKey = this.getCacheKey(remotePath);\n\n // Check cache first\n if (!options.force) {\n const cached = await this.cache.get(cacheKey, options.expiry);\n if (cached) {\n return cached;\n }\n }\n\n // Download and cache\n const localPath = await this.download(remotePath, undefined, options);\n await this.cache.set(cacheKey, localPath);\n\n return localPath;\n }\n\n /**\n * Cache implementation - providers can override for their specific storage\n */\n cache = {\n get: async (\n _key: string,\n _expiry?: number,\n ): Promise<string | undefined> => {\n // Default implementation - providers should override this\n this.throwUnsupported('cache.get');\n },\n\n set: async (_key: string, _data: string): Promise<void> => {\n // Default implementation - providers should override this\n this.throwUnsupported('cache.set');\n },\n\n clear: async (_key?: string): Promise<void> => {\n // Default implementation - providers should override this\n this.throwUnsupported('cache.clear');\n },\n };\n\n // Legacy method implementations - providers can override or use default ENOTSUP errors\n\n /**\n * Check if a path is a file (legacy)\n */\n async isFile(file: string): Promise<false | FileStats> {\n try {\n const stats = await this.getStats(file);\n return stats.isFile ? stats : false;\n } catch {\n return false;\n }\n }\n\n /**\n * Check if a path is a directory (legacy)\n */\n async isDirectory(dir: string): Promise<boolean> {\n try {\n const stats = await this.getStats(dir);\n return stats.isDirectory;\n } catch {\n return false;\n }\n }\n\n /**\n * Create a directory if it doesn't exist (legacy)\n */\n async ensureDirectoryExists(dir: string): Promise<void> {\n if (!(await this.isDirectory(dir))) {\n await this.createDirectory(dir, { recursive: true });\n }\n }\n\n /**\n * Upload data to a URL using PUT method (legacy)\n */\n async uploadToUrl(_url: string, _data: string | Buffer): Promise<Response> {\n this.throwUnsupported('uploadToUrl');\n }\n\n /**\n * Download a file from a URL and save it to a local file (legacy)\n */\n async downloadFromUrl(_url: string, _filepath: string): Promise<void> {\n this.throwUnsupported('downloadFromUrl');\n }\n\n /**\n * Download a file with caching support (legacy)\n */\n async downloadFileWithCache(\n _url: string,\n _targetPath?: string | null,\n ): Promise<string> {\n this.throwUnsupported('downloadFileWithCache');\n }\n\n /**\n * List files in a directory with optional filtering (legacy)\n */\n async listFiles(\n dirPath: string,\n options: ListFilesOptions = { match: /.*/ },\n ): Promise<string[]> {\n const files = await this.list(dirPath);\n const fileNames = files\n .filter((file) => !file.isDirectory)\n .map((file) => file.name);\n\n return options.match\n ? fileNames.filter((name) => options.match?.test(name))\n : fileNames;\n }\n\n /**\n * Get data from cache if available and not expired (legacy)\n */\n async getCached(file: string, expiry = 300000): Promise<string | undefined> {\n return await this.cache.get(file, expiry);\n }\n\n /**\n * Set data in cache (legacy)\n */\n async setCached(file: string, data: string): Promise<void> {\n await this.cache.set(file, data);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"base-CYInq2Yk.js","names":[],"sources":["../../src/shared/base.ts"],"sourcesContent":["import {\n type BaseProviderOptions,\n type CacheOptions,\n type CreateDirOptions,\n type DownloadOptions,\n type FileInfo,\n type FileStats,\n type FilesystemCapabilities,\n FilesystemError,\n type FilesystemInterface,\n type ListFilesOptions,\n type ListOptions,\n type ReadOptions,\n type UploadOptions,\n type WriteOptions,\n} from './types';\n\n/**\n * Abstract base class for filesystem providers.\n *\n * Implements {@link FilesystemInterface} with shared logic for path normalization,\n * validation, caching, and legacy method adapters. Concrete providers (e.g.\n * {@link LocalFilesystemProvider}, {@link GoogleDriveProvider}) extend this class\n * and implement the abstract methods for their specific storage backend.\n */\nexport abstract class BaseFilesystemProvider implements FilesystemInterface {\n protected basePath: string;\n protected applyBasePath: boolean;\n protected cacheDir: string;\n protected createMissing: boolean;\n protected providerType: string;\n\n constructor(options: BaseProviderOptions = {}) {\n this.basePath = options.basePath || '';\n this.applyBasePath = options.applyBasePath ?? true;\n // Use a universal cache directory approach - will be context-specific\n this.cacheDir = options.cacheDir || this.getDefaultCacheDir();\n this.createMissing = options.createMissing ?? true;\n this.providerType = this.constructor.name\n .toLowerCase()\n .replace('filesystemprovider', '');\n }\n\n /**\n * Get default cache directory for the current context\n */\n private getDefaultCacheDir(): string {\n // Use context-aware temp directory from utils\n try {\n const { getTempDirectory } = require('@happyvertical/utils');\n return getTempDirectory('files-cache');\n } catch {\n // Fallback if utils not available\n if (process?.versions?.node) {\n try {\n const { tmpdir } = require('node:os');\n const { join } = require('node:path');\n return join(tmpdir(), 'have-sdk', 'files-cache');\n } catch {\n return './tmp/have-sdk/files-cache';\n }\n }\n return './tmp/have-sdk/files-cache';\n }\n }\n\n /**\n * Throw error for unsupported operations\n */\n protected throwUnsupported(operation: string): never {\n throw new FilesystemError(\n `Operation '${operation}' not supported by ${this.providerType} provider`,\n 'ENOTSUP',\n undefined,\n this.providerType,\n );\n }\n\n /**\n * Normalize path by removing leading/trailing slashes and resolving relative paths\n */\n protected normalizePath(path: string): string {\n if (!path) return '';\n\n // Remove leading slash for consistency\n let normalized = path.startsWith('/') ? path.slice(1) : path;\n\n // Combine with base path if configured\n if (this.applyBasePath && this.basePath) {\n normalized = this.joinPaths(this.basePath, normalized);\n }\n\n return normalized;\n }\n\n /**\n * Universal path joining function that works in both Node.js and browser\n */\n private joinPaths(...paths: string[]): string {\n return paths\n .filter((p) => p && p.length > 0)\n .map((p) => p.replace(/^\\/+|\\/+$/g, ''))\n .join('/');\n }\n\n /**\n * Validate that a path is safe (no directory traversal)\n */\n protected validatePath(path: string): void {\n if (!path) {\n throw new FilesystemError('Path cannot be empty', 'EINVAL', path);\n }\n\n // Check for directory traversal attempts\n if (path.includes('..') || path.includes('~')) {\n throw new FilesystemError(\n 'Path contains invalid characters (directory traversal)',\n 'EINVAL',\n path,\n );\n }\n }\n\n /**\n * Get cache key for a given path\n */\n protected getCacheKey(path: string): string {\n return `${this.constructor.name}-${path}`;\n }\n\n /**\n * Abstract methods that must be implemented by providers\n */\n abstract exists(path: string): Promise<boolean>;\n abstract read(path: string, options?: ReadOptions): Promise<string | Buffer>;\n abstract write(\n path: string,\n content: string | Buffer,\n options?: WriteOptions,\n ): Promise<void>;\n abstract delete(path: string): Promise<void>;\n abstract copy(sourcePath: string, destPath: string): Promise<void>;\n abstract move(sourcePath: string, destPath: string): Promise<void>;\n abstract createDirectory(\n path: string,\n options?: CreateDirOptions,\n ): Promise<void>;\n abstract list(path: string, options?: ListOptions): Promise<FileInfo[]>;\n abstract getStats(path: string): Promise<FileStats>;\n abstract getMimeType(path: string): Promise<string>;\n abstract getCapabilities(): Promise<FilesystemCapabilities>;\n\n /**\n * Provider methods with default implementations (may be overridden)\n */\n async upload(\n _localPath: string,\n _remotePath: string,\n _options: UploadOptions = {},\n ): Promise<void> {\n this.throwUnsupported('upload');\n }\n\n async download(\n _remotePath: string,\n _localPath?: string,\n _options: DownloadOptions = {},\n ): Promise<string> {\n this.throwUnsupported('download');\n }\n\n async downloadWithCache(\n remotePath: string,\n options: CacheOptions = {},\n ): Promise<string> {\n const cacheKey = this.getCacheKey(remotePath);\n\n // Check cache first\n if (!options.force) {\n const cached = await this.cache.get(cacheKey, options.expiry);\n if (cached) {\n return cached;\n }\n }\n\n // Download and cache\n const localPath = await this.download(remotePath, undefined, options);\n await this.cache.set(cacheKey, localPath);\n\n return localPath;\n }\n\n /**\n * Cache implementation - providers can override for their specific storage\n */\n cache = {\n get: async (\n _key: string,\n _expiry?: number,\n ): Promise<string | undefined> => {\n // Default implementation - providers should override this\n this.throwUnsupported('cache.get');\n },\n\n set: async (_key: string, _data: string): Promise<void> => {\n // Default implementation - providers should override this\n this.throwUnsupported('cache.set');\n },\n\n clear: async (_key?: string): Promise<void> => {\n // Default implementation - providers should override this\n this.throwUnsupported('cache.clear');\n },\n };\n\n // Legacy method implementations - providers can override or use default ENOTSUP errors\n\n /**\n * Check if a path is a file (legacy)\n */\n async isFile(file: string): Promise<false | FileStats> {\n try {\n const stats = await this.getStats(file);\n return stats.isFile ? stats : false;\n } catch {\n return false;\n }\n }\n\n /**\n * Check if a path is a directory (legacy)\n */\n async isDirectory(dir: string): Promise<boolean> {\n try {\n const stats = await this.getStats(dir);\n return stats.isDirectory;\n } catch {\n return false;\n }\n }\n\n /**\n * Create a directory if it doesn't exist (legacy)\n */\n async ensureDirectoryExists(dir: string): Promise<void> {\n if (!(await this.isDirectory(dir))) {\n await this.createDirectory(dir, { recursive: true });\n }\n }\n\n /**\n * Upload data to a URL using PUT method (legacy)\n */\n async uploadToUrl(_url: string, _data: string | Buffer): Promise<Response> {\n this.throwUnsupported('uploadToUrl');\n }\n\n /**\n * Download a file from a URL and save it to a local file (legacy)\n */\n async downloadFromUrl(_url: string, _filepath: string): Promise<void> {\n this.throwUnsupported('downloadFromUrl');\n }\n\n /**\n * Download a file with caching support (legacy)\n */\n async downloadFileWithCache(\n _url: string,\n _targetPath?: string | null,\n ): Promise<string> {\n this.throwUnsupported('downloadFileWithCache');\n }\n\n /**\n * List files in a directory with optional filtering (legacy)\n */\n async listFiles(\n dirPath: string,\n options: ListFilesOptions = { match: /.*/ },\n ): Promise<string[]> {\n const files = await this.list(dirPath);\n const fileNames = files\n .filter((file) => !file.isDirectory)\n .map((file) => file.name);\n\n return options.match\n ? fileNames.filter((name) => options.match?.test(name))\n : fileNames;\n }\n\n /**\n * Get data from cache if available and not expired (legacy)\n */\n async getCached(file: string, expiry = 300000): Promise<string | undefined> {\n return await this.cache.get(file, expiry);\n }\n\n /**\n * Set data in cache (legacy)\n */\n async setCached(file: string, data: string): Promise<void> {\n await this.cache.set(file, data);\n }\n}\n"],"mappings":"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"}
@@ -36,6 +36,27 @@ var FileNotFoundError = class extends FilesystemError {
36
36
  this.name = "FileNotFoundError";
37
37
  }
38
38
  };
39
+ /** The requested Drive resource is not reachable from a configured source root. */
40
+ var DriveScopeError = class extends FilesystemError {
41
+ constructor(fileId) {
42
+ super(`Google Drive file ${fileId} is outside the configured source roots`, "EACCES", fileId, "gdrive");
43
+ this.name = "DriveScopeError";
44
+ }
45
+ };
46
+ /** A Drive resource changed while it was being acquired. */
47
+ var DriveRevisionChangedError = class extends FilesystemError {
48
+ constructor(fileId) {
49
+ super(`Google Drive file ${fileId} changed during acquisition`, "EAGAIN", fileId, "gdrive");
50
+ this.name = "DriveRevisionChangedError";
51
+ }
52
+ };
53
+ /** A Drive acquisition exceeded its caller-provided byte bound. */
54
+ var DriveAcquisitionLimitError = class extends FilesystemError {
55
+ constructor(fileId, maxBytes) {
56
+ super(`Google Drive file ${fileId} exceeds the ${maxBytes}-byte acquisition limit`, "EFBIG", fileId, "gdrive");
57
+ this.name = "DriveAcquisitionLimitError";
58
+ }
59
+ };
39
60
  var PermissionError = class extends FilesystemError {
40
61
  constructor(path, provider) {
41
62
  super(`Permission denied: ${path}`, "EACCES", path, provider);
@@ -55,6 +76,6 @@ var InvalidPathError = class extends FilesystemError {
55
76
  }
56
77
  };
57
78
  //#endregion
58
- export { PermissionError as a, InvalidPathError as i, FileNotFoundError as n, __exportAll as o, FilesystemError as r, __require as s, DirectoryNotEmptyError as t };
79
+ export { FileNotFoundError as a, PermissionError as c, DriveScopeError as i, __exportAll as l, DriveAcquisitionLimitError as n, FilesystemError as o, DriveRevisionChangedError as r, InvalidPathError as s, DirectoryNotEmptyError as t, __require as u };
59
80
 
60
- //# sourceMappingURL=types-OwkcCRpq.js.map
81
+ //# sourceMappingURL=types-DaP78DEi.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types-OwkcCRpq.js","names":[],"sources":["../../src/shared/types.ts"],"sourcesContent":["/**\n * Core types and interfaces for the Files library\n */\n\n/**\n * Options for reading files\n */\nexport interface ReadOptions {\n /**\n * Text encoding for reading the file\n */\n encoding?: BufferEncoding;\n\n /**\n * Whether to return raw buffer data instead of string\n */\n raw?: boolean;\n}\n\n/**\n * Options for writing files\n */\nexport interface WriteOptions {\n /**\n * Text encoding for writing the file\n */\n encoding?: BufferEncoding;\n\n /**\n * File mode (permissions)\n */\n mode?: number;\n\n /**\n * Whether to create parent directories if they don't exist\n */\n createParents?: boolean;\n}\n\n/**\n * Options for creating directories\n */\nexport interface CreateDirOptions {\n /**\n * Whether to create parent directories recursively\n */\n recursive?: boolean;\n\n /**\n * Directory mode (permissions)\n */\n mode?: number;\n}\n\n/**\n * Options for listing directory contents\n */\nexport interface ListOptions {\n /**\n * Whether to include subdirectories\n */\n recursive?: boolean;\n\n /**\n * Filter pattern for file names\n */\n filter?: RegExp | string;\n\n /**\n * Whether to return full file information\n */\n detailed?: boolean;\n}\n\n/**\n * Options for file upload operations\n */\nexport interface UploadOptions {\n /**\n * Content type for the upload\n */\n contentType?: string;\n\n /**\n * Whether to overwrite existing files\n */\n overwrite?: boolean;\n\n /**\n * Custom metadata to attach to the file\n */\n metadata?: Record<string, string>;\n\n /**\n * Progress callback function\n */\n onProgress?: (progress: { loaded: number; total: number }) => void;\n}\n\n/**\n * Options for file download operations\n */\nexport interface DownloadOptions {\n /**\n * Whether to force download even if local copy exists\n */\n force?: boolean;\n\n /**\n * Progress callback function\n */\n onProgress?: (progress: { loaded: number; total: number }) => void;\n}\n\n/**\n * Options for caching operations\n */\nexport interface CacheOptions {\n /**\n * Cache expiry time in milliseconds\n */\n expiry?: number;\n\n /**\n * Whether to force download even if cached\n */\n force?: boolean;\n}\n\n/**\n * Options for listing files (legacy compatibility)\n */\nexport interface ListFilesOptions {\n /**\n * Optional regular expression to filter files by name\n */\n match?: RegExp;\n}\n\n/**\n * File information structure\n */\nexport interface FileInfo {\n /**\n * File name\n */\n name: string;\n\n /**\n * Full path to the file\n */\n path: string;\n\n /**\n * File size in bytes\n */\n size: number;\n\n /**\n * Whether this is a directory\n */\n isDirectory: boolean;\n\n /**\n * Last modified date\n */\n lastModified: Date;\n\n /**\n * MIME type of the file\n */\n mimeType?: string;\n\n /**\n * File extension\n */\n extension?: string;\n}\n\n/**\n * File statistics structure\n */\nexport interface FileStats {\n /**\n * File size in bytes\n */\n size: number;\n\n /**\n * Whether this is a directory\n */\n isDirectory: boolean;\n\n /**\n * Whether this is a regular file\n */\n isFile: boolean;\n\n /**\n * Creation time\n */\n birthtime: Date;\n\n /**\n * Last access time\n */\n atime: Date;\n\n /**\n * Last modification time\n */\n mtime: Date;\n\n /**\n * Last status change time\n */\n ctime: Date;\n\n /**\n * File mode (permissions)\n */\n mode: number;\n\n /**\n * User ID of file owner\n */\n uid: number;\n\n /**\n * Group ID of file owner\n */\n gid: number;\n}\n\n/**\n * Filesystem capabilities structure\n */\nexport interface FilesystemCapabilities {\n /**\n * Whether the filesystem supports streaming\n */\n streaming: boolean;\n\n /**\n * Whether the filesystem supports atomic operations\n */\n atomicOperations: boolean;\n\n /**\n * Whether the filesystem supports file versioning\n */\n versioning: boolean;\n\n /**\n * Whether the filesystem supports sharing/permissions\n */\n sharing: boolean;\n\n /**\n * Whether the filesystem supports real-time synchronization\n */\n realTimeSync: boolean;\n\n /**\n * Whether the filesystem can work offline\n */\n offlineCapable: boolean;\n\n /**\n * Maximum file size supported (in bytes)\n */\n maxFileSize?: number;\n\n /**\n * Supported file operations\n */\n supportedOperations: string[];\n}\n\n/**\n * Core filesystem interface that all providers must implement\n */\nexport interface FilesystemInterface {\n /**\n * Check if a file or directory exists\n */\n exists(path: string): Promise<boolean>;\n\n /**\n * Read file contents\n */\n read(path: string, options?: ReadOptions): Promise<string | Buffer>;\n\n /**\n * Write content to a file\n */\n write(\n path: string,\n content: string | Buffer,\n options?: WriteOptions,\n ): Promise<void>;\n\n /**\n * Delete a file or directory\n */\n delete(path: string): Promise<void>;\n\n /**\n * Copy a file from source to destination\n */\n copy(sourcePath: string, destPath: string): Promise<void>;\n\n /**\n * Move a file from source to destination\n */\n move(sourcePath: string, destPath: string): Promise<void>;\n\n /**\n * Create a directory\n */\n createDirectory(path: string, options?: CreateDirOptions): Promise<void>;\n\n /**\n * List directory contents\n */\n list(path: string, options?: ListOptions): Promise<FileInfo[]>;\n\n /**\n * Get file statistics\n */\n getStats(path: string): Promise<FileStats>;\n\n /**\n * Get MIME type for a file\n */\n getMimeType(path: string): Promise<string>;\n\n /**\n * Upload a file (for remote providers)\n */\n upload(\n localPath: string,\n remotePath: string,\n options?: UploadOptions,\n ): Promise<void>;\n\n /**\n * Download a file (for remote providers)\n */\n download(\n remotePath: string,\n localPath?: string,\n options?: DownloadOptions,\n ): Promise<string>;\n\n /**\n * Download file with caching\n */\n downloadWithCache(\n remotePath: string,\n options?: CacheOptions,\n ): Promise<string>;\n\n /**\n * Caching operations\n */\n cache: {\n get(key: string, expiry?: number): Promise<string | undefined>;\n set(key: string, data: string): Promise<void>;\n clear(key?: string): Promise<void>;\n };\n\n /**\n * Get provider capabilities\n */\n getCapabilities(): Promise<FilesystemCapabilities>;\n\n // Legacy method compatibility - all providers must implement these\n\n /**\n * Check if a path is a file (legacy)\n */\n isFile(file: string): Promise<false | FileStats>;\n\n /**\n * Check if a path is a directory (legacy)\n */\n isDirectory(dir: string): Promise<boolean>;\n\n /**\n * Create a directory if it doesn't exist (legacy)\n */\n ensureDirectoryExists(dir: string): Promise<void>;\n\n /**\n * Upload data to a URL using PUT method (legacy)\n */\n uploadToUrl(url: string, data: string | Buffer): Promise<Response>;\n\n /**\n * Download a file from a URL and save it to a local file (legacy)\n */\n downloadFromUrl(url: string, filepath: string): Promise<void>;\n\n /**\n * Download a file with caching support (legacy)\n */\n downloadFileWithCache(\n url: string,\n targetPath?: string | null,\n ): Promise<string>;\n\n /**\n * List files in a directory with optional filtering (legacy)\n */\n listFiles(dirPath: string, options?: ListFilesOptions): Promise<string[]>;\n\n /**\n * Get data from cache if available and not expired (legacy)\n */\n getCached(file: string, expiry?: number): Promise<string | undefined>;\n\n /**\n * Set data in cache (legacy)\n */\n setCached(file: string, data: string): Promise<void>;\n}\n\n/**\n * Base configuration options for all providers\n */\nexport interface BaseProviderOptions {\n /**\n * Base path for operations\n */\n basePath?: string;\n\n /**\n * Whether shared path normalization should prefix basePath.\n * Providers that map basePath into their own storage root can disable this.\n */\n applyBasePath?: boolean;\n\n /**\n * Cache directory location\n */\n cacheDir?: string;\n\n /**\n * Whether to create missing directories\n */\n createMissing?: boolean;\n}\n\n/**\n * Local filesystem provider options\n */\nexport interface LocalOptions extends BaseProviderOptions {\n type?: 'local';\n}\n\n/**\n * S3-compatible provider options\n */\nexport interface S3Options extends BaseProviderOptions {\n type: 's3';\n region: string;\n bucket: string;\n accessKeyId?: string;\n secretAccessKey?: string;\n endpoint?: string;\n forcePathStyle?: boolean;\n}\n\n/**\n * Google Drive provider options\n *\n * Supports three authentication modes:\n * 1. OAuth2: clientId + clientSecret + refreshToken\n * 2. Service account: serviceAccountKey (JSON string)\n * 3. Access token: accessToken (short-lived, no auto-refresh)\n */\nexport interface GoogleDriveOptions extends BaseProviderOptions {\n type: 'gdrive';\n /** OAuth2 client ID */\n clientId?: string;\n /** OAuth2 client secret */\n clientSecret?: string;\n /** OAuth2 refresh token */\n refreshToken?: string;\n /** Short-lived access token (no auto-refresh) */\n accessToken?: string;\n /** Service account key as JSON string */\n serviceAccountKey?: string;\n /** Root folder ID to scope operations (defaults to 'root') */\n folderId?: string;\n /** OAuth2 scopes (defaults to drive.file) */\n scopes?: string[];\n /** TTL for path-to-ID cache in ms (default 300000 / 5 min) */\n pathCacheTTL?: number;\n /** Page size for list pagination (default 100) */\n pageSize?: number;\n}\n\n/**\n * WebDAV provider options (supports Nextcloud, ownCloud, Apache, etc.)\n */\nexport interface WebDAVOptions extends BaseProviderOptions {\n type: 'webdav';\n baseUrl: string;\n username: string;\n password: string;\n davPath?: string;\n}\n\n/**\n * Browser storage provider options (uses IndexedDB for app storage)\n */\nexport interface BrowserStorageOptions extends BaseProviderOptions {\n type: 'browser-storage';\n /**\n * Database name for IndexedDB\n */\n databaseName?: string;\n /**\n * Maximum storage quota to request (in bytes)\n */\n storageQuota?: number;\n}\n\n/**\n * Union type for all provider options\n */\nexport type GetFilesystemOptions =\n | LocalOptions\n | S3Options\n | GoogleDriveOptions\n | WebDAVOptions\n | BrowserStorageOptions;\n\n/**\n * Error types for filesystem operations\n */\nexport class FilesystemError extends Error {\n constructor(\n message: string,\n public code: string,\n public path?: string,\n public provider?: string,\n ) {\n super(message);\n this.name = 'FilesystemError';\n }\n}\n\nexport class FileNotFoundError extends FilesystemError {\n constructor(path: string, provider?: string) {\n super(`File not found: ${path}`, 'ENOENT', path, provider);\n this.name = 'FileNotFoundError';\n }\n}\n\nexport class PermissionError extends FilesystemError {\n constructor(path: string, provider?: string) {\n super(`Permission denied: ${path}`, 'EACCES', path, provider);\n this.name = 'PermissionError';\n }\n}\n\nexport class DirectoryNotEmptyError extends FilesystemError {\n constructor(path: string, provider?: string) {\n super(`Directory not empty: ${path}`, 'ENOTEMPTY', path, provider);\n this.name = 'DirectoryNotEmptyError';\n }\n}\n\nexport class InvalidPathError extends FilesystemError {\n constructor(path: string, provider?: string) {\n super(`Invalid path: ${path}`, 'EINVAL', path, provider);\n this.name = 'InvalidPathError';\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA+hBA,IAAa,kBAAb,cAAqC,MAAM;CAGhC;CACA;CACA;CAJT,YACE,SACA,MACA,MACA,UACA;EACA,MAAM,OAAO;EAJN,KAAA,OAAA;EACA,KAAA,OAAA;EACA,KAAA,WAAA;EAGP,KAAK,OAAO;CACd;AACF;AAEA,IAAa,oBAAb,cAAuC,gBAAgB;CACrD,YAAY,MAAc,UAAmB;EAC3C,MAAM,mBAAmB,QAAQ,UAAU,MAAM,QAAQ;EACzD,KAAK,OAAO;CACd;AACF;AAEA,IAAa,kBAAb,cAAqC,gBAAgB;CACnD,YAAY,MAAc,UAAmB;EAC3C,MAAM,sBAAsB,QAAQ,UAAU,MAAM,QAAQ;EAC5D,KAAK,OAAO;CACd;AACF;AAEA,IAAa,yBAAb,cAA4C,gBAAgB;CAC1D,YAAY,MAAc,UAAmB;EAC3C,MAAM,wBAAwB,QAAQ,aAAa,MAAM,QAAQ;EACjE,KAAK,OAAO;CACd;AACF;AAEA,IAAa,mBAAb,cAAsC,gBAAgB;CACpD,YAAY,MAAc,UAAmB;EAC3C,MAAM,iBAAiB,QAAQ,UAAU,MAAM,QAAQ;EACvD,KAAK,OAAO;CACd;AACF"}
1
+ {"version":3,"file":"types-DaP78DEi.js","names":[],"sources":["../../src/shared/types.ts"],"sourcesContent":["/**\n * Core types and interfaces for the Files library\n */\n\n/**\n * Options for reading files\n */\nexport interface ReadOptions {\n /**\n * Text encoding for reading the file\n */\n encoding?: BufferEncoding;\n\n /**\n * Whether to return raw buffer data instead of string\n */\n raw?: boolean;\n}\n\n/**\n * Options for writing files\n */\nexport interface WriteOptions {\n /**\n * Text encoding for writing the file\n */\n encoding?: BufferEncoding;\n\n /**\n * File mode (permissions)\n */\n mode?: number;\n\n /**\n * Whether to create parent directories if they don't exist\n */\n createParents?: boolean;\n}\n\n/**\n * Options for creating directories\n */\nexport interface CreateDirOptions {\n /**\n * Whether to create parent directories recursively\n */\n recursive?: boolean;\n\n /**\n * Directory mode (permissions)\n */\n mode?: number;\n}\n\n/**\n * Options for listing directory contents\n */\nexport interface ListOptions {\n /**\n * Whether to include subdirectories\n */\n recursive?: boolean;\n\n /**\n * Filter pattern for file names\n */\n filter?: RegExp | string;\n\n /**\n * Whether to return full file information\n */\n detailed?: boolean;\n}\n\n/**\n * Options for file upload operations\n */\nexport interface UploadOptions {\n /**\n * Content type for the upload\n */\n contentType?: string;\n\n /**\n * Whether to overwrite existing files\n */\n overwrite?: boolean;\n\n /**\n * Custom metadata to attach to the file\n */\n metadata?: Record<string, string>;\n\n /**\n * Progress callback function\n */\n onProgress?: (progress: { loaded: number; total: number }) => void;\n}\n\n/**\n * Options for file download operations\n */\nexport interface DownloadOptions {\n /**\n * Whether to force download even if local copy exists\n */\n force?: boolean;\n\n /**\n * Progress callback function\n */\n onProgress?: (progress: { loaded: number; total: number }) => void;\n}\n\n/**\n * Options for caching operations\n */\nexport interface CacheOptions {\n /**\n * Cache expiry time in milliseconds\n */\n expiry?: number;\n\n /**\n * Whether to force download even if cached\n */\n force?: boolean;\n}\n\n/**\n * Options for listing files (legacy compatibility)\n */\nexport interface ListFilesOptions {\n /**\n * Optional regular expression to filter files by name\n */\n match?: RegExp;\n}\n\n/**\n * File information structure\n */\nexport interface FileInfo {\n /**\n * File name\n */\n name: string;\n\n /**\n * Full path to the file\n */\n path: string;\n\n /**\n * File size in bytes\n */\n size: number;\n\n /**\n * Whether this is a directory\n */\n isDirectory: boolean;\n\n /**\n * Last modified date\n */\n lastModified: Date;\n\n /**\n * MIME type of the file\n */\n mimeType?: string;\n\n /**\n * File extension\n */\n extension?: string;\n}\n\n/**\n * File statistics structure\n */\nexport interface FileStats {\n /**\n * File size in bytes\n */\n size: number;\n\n /**\n * Whether this is a directory\n */\n isDirectory: boolean;\n\n /**\n * Whether this is a regular file\n */\n isFile: boolean;\n\n /**\n * Creation time\n */\n birthtime: Date;\n\n /**\n * Last access time\n */\n atime: Date;\n\n /**\n * Last modification time\n */\n mtime: Date;\n\n /**\n * Last status change time\n */\n ctime: Date;\n\n /**\n * File mode (permissions)\n */\n mode: number;\n\n /**\n * User ID of file owner\n */\n uid: number;\n\n /**\n * Group ID of file owner\n */\n gid: number;\n}\n\n/**\n * Filesystem capabilities structure\n */\nexport interface FilesystemCapabilities {\n /**\n * Whether the filesystem supports streaming\n */\n streaming: boolean;\n\n /**\n * Whether the filesystem supports atomic operations\n */\n atomicOperations: boolean;\n\n /**\n * Whether the filesystem supports file versioning\n */\n versioning: boolean;\n\n /**\n * Whether the filesystem supports sharing/permissions\n */\n sharing: boolean;\n\n /**\n * Whether the filesystem supports real-time synchronization\n */\n realTimeSync: boolean;\n\n /**\n * Whether the filesystem can work offline\n */\n offlineCapable: boolean;\n\n /**\n * Maximum file size supported (in bytes)\n */\n maxFileSize?: number;\n\n /**\n * Supported file operations\n */\n supportedOperations: string[];\n}\n\n/**\n * Core filesystem interface that all providers must implement\n */\nexport interface FilesystemInterface {\n /**\n * Check if a file or directory exists\n */\n exists(path: string): Promise<boolean>;\n\n /**\n * Read file contents\n */\n read(path: string, options?: ReadOptions): Promise<string | Buffer>;\n\n /**\n * Write content to a file\n */\n write(\n path: string,\n content: string | Buffer,\n options?: WriteOptions,\n ): Promise<void>;\n\n /**\n * Delete a file or directory\n */\n delete(path: string): Promise<void>;\n\n /**\n * Copy a file from source to destination\n */\n copy(sourcePath: string, destPath: string): Promise<void>;\n\n /**\n * Move a file from source to destination\n */\n move(sourcePath: string, destPath: string): Promise<void>;\n\n /**\n * Create a directory\n */\n createDirectory(path: string, options?: CreateDirOptions): Promise<void>;\n\n /**\n * List directory contents\n */\n list(path: string, options?: ListOptions): Promise<FileInfo[]>;\n\n /**\n * Get file statistics\n */\n getStats(path: string): Promise<FileStats>;\n\n /**\n * Get MIME type for a file\n */\n getMimeType(path: string): Promise<string>;\n\n /**\n * Upload a file (for remote providers)\n */\n upload(\n localPath: string,\n remotePath: string,\n options?: UploadOptions,\n ): Promise<void>;\n\n /**\n * Download a file (for remote providers)\n */\n download(\n remotePath: string,\n localPath?: string,\n options?: DownloadOptions,\n ): Promise<string>;\n\n /**\n * Download file with caching\n */\n downloadWithCache(\n remotePath: string,\n options?: CacheOptions,\n ): Promise<string>;\n\n /**\n * Caching operations\n */\n cache: {\n get(key: string, expiry?: number): Promise<string | undefined>;\n set(key: string, data: string): Promise<void>;\n clear(key?: string): Promise<void>;\n };\n\n /**\n * Get provider capabilities\n */\n getCapabilities(): Promise<FilesystemCapabilities>;\n\n // Legacy method compatibility - all providers must implement these\n\n /**\n * Check if a path is a file (legacy)\n */\n isFile(file: string): Promise<false | FileStats>;\n\n /**\n * Check if a path is a directory (legacy)\n */\n isDirectory(dir: string): Promise<boolean>;\n\n /**\n * Create a directory if it doesn't exist (legacy)\n */\n ensureDirectoryExists(dir: string): Promise<void>;\n\n /**\n * Upload data to a URL using PUT method (legacy)\n */\n uploadToUrl(url: string, data: string | Buffer): Promise<Response>;\n\n /**\n * Download a file from a URL and save it to a local file (legacy)\n */\n downloadFromUrl(url: string, filepath: string): Promise<void>;\n\n /**\n * Download a file with caching support (legacy)\n */\n downloadFileWithCache(\n url: string,\n targetPath?: string | null,\n ): Promise<string>;\n\n /**\n * List files in a directory with optional filtering (legacy)\n */\n listFiles(dirPath: string, options?: ListFilesOptions): Promise<string[]>;\n\n /**\n * Get data from cache if available and not expired (legacy)\n */\n getCached(file: string, expiry?: number): Promise<string | undefined>;\n\n /**\n * Set data in cache (legacy)\n */\n setCached(file: string, data: string): Promise<void>;\n}\n\n/**\n * Base configuration options for all providers\n */\nexport interface BaseProviderOptions {\n /**\n * Base path for operations\n */\n basePath?: string;\n\n /**\n * Whether shared path normalization should prefix basePath.\n * Providers that map basePath into their own storage root can disable this.\n */\n applyBasePath?: boolean;\n\n /**\n * Cache directory location\n */\n cacheDir?: string;\n\n /**\n * Whether to create missing directories\n */\n createMissing?: boolean;\n}\n\n/**\n * Local filesystem provider options\n */\nexport interface LocalOptions extends BaseProviderOptions {\n type?: 'local';\n}\n\n/**\n * S3-compatible provider options\n */\nexport interface S3Options extends BaseProviderOptions {\n type: 's3';\n region: string;\n bucket: string;\n accessKeyId?: string;\n secretAccessKey?: string;\n endpoint?: string;\n forcePathStyle?: boolean;\n}\n\n/**\n * Google Drive provider options\n *\n * Supports three authentication modes:\n * 1. OAuth2: clientId + clientSecret + refreshToken\n * 2. Service account: serviceAccountKey (JSON string)\n * 3. Access token: accessToken (short-lived, no auto-refresh)\n */\nexport interface GoogleDriveOptions extends BaseProviderOptions {\n type: 'gdrive';\n /** OAuth2 client ID */\n clientId?: string;\n /** OAuth2 client secret */\n clientSecret?: string;\n /** OAuth2 refresh token */\n refreshToken?: string;\n /** Short-lived access token (no auto-refresh) */\n accessToken?: string;\n /** Service account key as JSON string */\n serviceAccountKey?: string;\n /** Root folder ID to scope operations (defaults to 'root') */\n folderId?: string;\n /** OAuth2 scopes (defaults to drive.file) */\n scopes?: string[];\n /** TTL for path-to-ID cache in ms (default 300000 / 5 min) */\n pathCacheTTL?: number;\n /** Page size for list pagination (default 100) */\n pageSize?: number;\n /**\n * Drive folder IDs that may be discovered or acquired by ID. Defaults to\n * `folderId` (or `root` when no folder is configured).\n */\n allowedFolderIds?: string[];\n /**\n * Shared drive ID. When supplied, Drive requests opt into shared-drive\n * semantics and are restricted to this drive.\n */\n sharedDriveId?: string;\n}\n\n/** One metadata-only page from a Google Drive folder. */\nexport interface DriveMetadataPageOptions {\n /** Folder ID to enumerate. Defaults to the provider's configured root. */\n folderId?: string;\n /** Durable Drive cursor returned by a preceding page. */\n pageToken?: string;\n /** Number of entries requested from Drive. */\n pageSize?: number;\n}\n\n/** Stable Drive metadata used for catalog discovery and acquisition. */\nexport interface DriveFileMetadata {\n id: string;\n name: string;\n mimeType: string;\n size?: number;\n createdTime?: Date;\n modifiedTime?: Date;\n /** Google Drive head revision; absent for resources that do not expose one. */\n revisionId?: string;\n /** Checksum supplied by Drive for binary files. Native Google files omit it. */\n md5Checksum?: string;\n trashed?: boolean;\n parents: string[];\n driveId?: string;\n shortcutDetails?: {\n targetId: string;\n targetMimeType?: string;\n targetResourceKey?: string;\n };\n}\n\nexport interface DriveMetadataPage {\n entries: DriveFileMetadata[];\n nextPageToken?: string;\n}\n\n/** Options for an explicit, bounded acquisition by stable Drive ID. */\nexport interface DriveAcquisitionOptions {\n /** Reject the read if the current Drive revision differs. */\n expectedRevision?: string;\n /** Maximum response bytes. The provider stops the stream at this boundary. */\n maxBytes: number;\n /** Required for native Google files; e.g. application/pdf for printable layout. */\n exportMimeType?: string;\n}\n\n/** Bytes and the metadata that pinned their source version. */\nexport interface DriveAcquisition {\n bytes: Buffer;\n metadata: DriveFileMetadata;\n /** SHA-256 of returned bytes, including native exports without a Drive checksum. */\n contentHash: string;\n exportMimeType?: string;\n}\n\n/**\n * WebDAV provider options (supports Nextcloud, ownCloud, Apache, etc.)\n */\nexport interface WebDAVOptions extends BaseProviderOptions {\n type: 'webdav';\n baseUrl: string;\n username: string;\n password: string;\n davPath?: string;\n}\n\n/**\n * Browser storage provider options (uses IndexedDB for app storage)\n */\nexport interface BrowserStorageOptions extends BaseProviderOptions {\n type: 'browser-storage';\n /**\n * Database name for IndexedDB\n */\n databaseName?: string;\n /**\n * Maximum storage quota to request (in bytes)\n */\n storageQuota?: number;\n}\n\n/**\n * Union type for all provider options\n */\nexport type GetFilesystemOptions =\n | LocalOptions\n | S3Options\n | GoogleDriveOptions\n | WebDAVOptions\n | BrowserStorageOptions;\n\n/**\n * Error types for filesystem operations\n */\nexport class FilesystemError extends Error {\n constructor(\n message: string,\n public code: string,\n public path?: string,\n public provider?: string,\n ) {\n super(message);\n this.name = 'FilesystemError';\n }\n}\n\nexport class FileNotFoundError extends FilesystemError {\n constructor(path: string, provider?: string) {\n super(`File not found: ${path}`, 'ENOENT', path, provider);\n this.name = 'FileNotFoundError';\n }\n}\n\n/** The requested Drive resource is not reachable from a configured source root. */\nexport class DriveScopeError extends FilesystemError {\n constructor(fileId: string) {\n super(\n `Google Drive file ${fileId} is outside the configured source roots`,\n 'EACCES',\n fileId,\n 'gdrive',\n );\n this.name = 'DriveScopeError';\n }\n}\n\n/** A Drive resource changed while it was being acquired. */\nexport class DriveRevisionChangedError extends FilesystemError {\n constructor(fileId: string) {\n super(\n `Google Drive file ${fileId} changed during acquisition`,\n 'EAGAIN',\n fileId,\n 'gdrive',\n );\n this.name = 'DriveRevisionChangedError';\n }\n}\n\n/** A Drive acquisition exceeded its caller-provided byte bound. */\nexport class DriveAcquisitionLimitError extends FilesystemError {\n constructor(fileId: string, maxBytes: number) {\n super(\n `Google Drive file ${fileId} exceeds the ${maxBytes}-byte acquisition limit`,\n 'EFBIG',\n fileId,\n 'gdrive',\n );\n this.name = 'DriveAcquisitionLimitError';\n }\n}\n\nexport class PermissionError extends FilesystemError {\n constructor(path: string, provider?: string) {\n super(`Permission denied: ${path}`, 'EACCES', path, provider);\n this.name = 'PermissionError';\n }\n}\n\nexport class DirectoryNotEmptyError extends FilesystemError {\n constructor(path: string, provider?: string) {\n super(`Directory not empty: ${path}`, 'ENOTEMPTY', path, provider);\n this.name = 'DirectoryNotEmptyError';\n }\n}\n\nexport class InvalidPathError extends FilesystemError {\n constructor(path: string, provider?: string) {\n super(`Invalid path: ${path}`, 'EINVAL', path, provider);\n this.name = 'InvalidPathError';\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAimBA,IAAa,kBAAb,cAAqC,MAAM;CAGhC;CACA;CACA;CAJT,YACE,SACA,MACA,MACA,UACA;EACA,MAAM,OAAO;EAJN,KAAA,OAAA;EACA,KAAA,OAAA;EACA,KAAA,WAAA;EAGP,KAAK,OAAO;CACd;AACF;AAEA,IAAa,oBAAb,cAAuC,gBAAgB;CACrD,YAAY,MAAc,UAAmB;EAC3C,MAAM,mBAAmB,QAAQ,UAAU,MAAM,QAAQ;EACzD,KAAK,OAAO;CACd;AACF;;AAGA,IAAa,kBAAb,cAAqC,gBAAgB;CACnD,YAAY,QAAgB;EAC1B,MACE,qBAAqB,OAAO,0CAC5B,UACA,QACA,QACF;EACA,KAAK,OAAO;CACd;AACF;;AAGA,IAAa,4BAAb,cAA+C,gBAAgB;CAC7D,YAAY,QAAgB;EAC1B,MACE,qBAAqB,OAAO,8BAC5B,UACA,QACA,QACF;EACA,KAAK,OAAO;CACd;AACF;;AAGA,IAAa,6BAAb,cAAgD,gBAAgB;CAC9D,YAAY,QAAgB,UAAkB;EAC5C,MACE,qBAAqB,OAAO,eAAe,SAAS,0BACpD,SACA,QACA,QACF;EACA,KAAK,OAAO;CACd;AACF;AAEA,IAAa,kBAAb,cAAqC,gBAAgB;CACnD,YAAY,MAAc,UAAmB;EAC3C,MAAM,sBAAsB,QAAQ,UAAU,MAAM,QAAQ;EAC5D,KAAK,OAAO;CACd;AACF;AAEA,IAAa,yBAAb,cAA4C,gBAAgB;CAC1D,YAAY,MAAc,UAAmB;EAC3C,MAAM,wBAAwB,QAAQ,aAAa,MAAM,QAAQ;EACjE,KAAK,OAAO;CACd;AACF;AAEA,IAAa,mBAAb,cAAsC,gBAAgB;CACpD,YAAY,MAAc,UAAmB;EAC3C,MAAM,iBAAiB,QAAQ,UAAU,MAAM,QAAQ;EACvD,KAAK,OAAO;CACd;AACF"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { a as PermissionError, i as InvalidPathError, n as FileNotFoundError, o as __exportAll, r as FilesystemError, t as DirectoryNotEmptyError } from "./chunks/types-OwkcCRpq.js";
2
- import { t as BaseFilesystemProvider } from "./chunks/base-C9husdYd.js";
3
- import { randomUUID } from "node:crypto";
1
+ import { a as FileNotFoundError, c as PermissionError, i as DriveScopeError, l as __exportAll, n as DriveAcquisitionLimitError, o as FilesystemError, r as DriveRevisionChangedError, s as InvalidPathError, t as DirectoryNotEmptyError } from "./chunks/types-DaP78DEi.js";
2
+ import { t as BaseFilesystemProvider } from "./chunks/base-CYInq2Yk.js";
3
+ import { createHash, randomUUID } from "node:crypto";
4
4
  import { constants, createWriteStream, existsSync, statSync } from "node:fs";
5
5
  import { access, chmod, chown, copyFile, lstat, mkdir, readFile, readdir, realpath, rename, rm, rmdir, stat, unlink, writeFile } from "node:fs/promises";
6
6
  import * as path from "node:path";
@@ -1875,6 +1875,8 @@ var GOOGLE_EXPORT_MIMES = {
1875
1875
  "application/vnd.google-apps.presentation": "application/pdf",
1876
1876
  "application/vnd.google-apps.drawing": "image/png"
1877
1877
  };
1878
+ var GOOGLE_NATIVE_MIME_PREFIX = "application/vnd.google-apps.";
1879
+ var DRIVE_METADATA_FIELDS = "id,name,mimeType,size,createdTime,modifiedTime,headRevisionId,version,md5Checksum,trashed,parents,driveId,shortcutDetails(targetId,targetMimeType,targetResourceKey)";
1878
1880
  /**
1879
1881
  * Google Drive filesystem provider using the Drive API v3
1880
1882
  *
@@ -1906,12 +1908,49 @@ var GoogleDriveProvider = class extends BaseFilesystemProvider {
1906
1908
  pathCache = /* @__PURE__ */ new Map();
1907
1909
  pathCacheTTL;
1908
1910
  pageSize;
1911
+ allowedFolderIds;
1912
+ rootsCanonicalization;
1909
1913
  constructor(options) {
1910
1914
  super(options);
1911
1915
  this.options = options;
1912
1916
  this.rootFolderId = options.folderId || "root";
1913
1917
  this.pathCacheTTL = options.pathCacheTTL ?? 3e5;
1914
1918
  this.pageSize = options.pageSize ?? 100;
1919
+ this.allowedFolderIds = new Set(options.allowedFolderIds?.length ? options.allowedFolderIds : [this.rootFolderId]);
1920
+ }
1921
+ /** Shared-drive flags accepted by files.list. */
1922
+ get driveListOptions() {
1923
+ if (!this.options.sharedDriveId) return {};
1924
+ return {
1925
+ supportsAllDrives: true,
1926
+ includeItemsFromAllDrives: true,
1927
+ corpora: "drive",
1928
+ driveId: this.options.sharedDriveId
1929
+ };
1930
+ }
1931
+ /** Shared-drive flag accepted by files.get and files.export. */
1932
+ get driveFileOptions() {
1933
+ return this.options.sharedDriveId ? { supportsAllDrives: true } : {};
1934
+ }
1935
+ toMetadata(file) {
1936
+ return {
1937
+ id: file.id,
1938
+ name: file.name,
1939
+ mimeType: file.mimeType,
1940
+ size: file.size === void 0 ? void 0 : Number(file.size),
1941
+ createdTime: file.createdTime ? new Date(file.createdTime) : void 0,
1942
+ modifiedTime: file.modifiedTime ? new Date(file.modifiedTime) : void 0,
1943
+ revisionId: file.headRevisionId ?? file.version,
1944
+ md5Checksum: file.md5Checksum,
1945
+ trashed: file.trashed,
1946
+ parents: file.parents ?? [],
1947
+ driveId: file.driveId,
1948
+ shortcutDetails: file.shortcutDetails ? {
1949
+ targetId: file.shortcutDetails.targetId,
1950
+ targetMimeType: file.shortcutDetails.targetMimeType,
1951
+ targetResourceKey: file.shortcutDetails.targetResourceKey
1952
+ } : void 0
1953
+ };
1915
1954
  }
1916
1955
  /**
1917
1956
  * Lazily initialize the Google Drive client on first use
@@ -1961,7 +2000,8 @@ var GoogleDriveProvider = class extends BaseFilesystemProvider {
1961
2000
  const res = await this.wrapApiCall(() => drive.files.list({
1962
2001
  q,
1963
2002
  fields: "files(id, name)",
1964
- pageSize: 1
2003
+ pageSize: 1,
2004
+ ...this.driveListOptions
1965
2005
  }), path);
1966
2006
  if (!res.data.files || res.data.files.length === 0) return null;
1967
2007
  parentId = res.data.files[0].id;
@@ -2020,7 +2060,7 @@ var GoogleDriveProvider = class extends BaseFilesystemProvider {
2020
2060
  */
2021
2061
  invalidateCache(path) {
2022
2062
  const normalized = this.normalizePath(path);
2023
- for (const key of this.pathCache.keys()) if (key === normalized || key.startsWith(normalized + "/")) this.pathCache.delete(key);
2063
+ for (const key of this.pathCache.keys()) if (key === normalized || key.startsWith(`${normalized}/`)) this.pathCache.delete(key);
2024
2064
  }
2025
2065
  /**
2026
2066
  * Wrap a Drive API call and map HTTP errors to typed filesystem errors
@@ -2037,6 +2077,146 @@ var GoogleDriveProvider = class extends BaseFilesystemProvider {
2037
2077
  throw new FilesystemError(`Google Drive API error: ${error.message}`, error.code?.toString() || "UNKNOWN", path, "gdrive");
2038
2078
  }
2039
2079
  }
2080
+ /**
2081
+ * Returns one metadata-only page. Callers persist `nextPageToken` as their
2082
+ * checkpoint and schedule recursive folder traversal themselves.
2083
+ */
2084
+ async listMetadataPage(options = {}) {
2085
+ const folderId = options.folderId ?? this.rootFolderId;
2086
+ await this.assertAllowedId(folderId);
2087
+ const drive = await this.getDrive();
2088
+ const res = await this.wrapApiCall(() => drive.files.list({
2089
+ q: `'${folderId}' in parents and trashed = false`,
2090
+ fields: `nextPageToken,files(${DRIVE_METADATA_FIELDS})`,
2091
+ pageSize: options.pageSize ?? this.pageSize,
2092
+ pageToken: options.pageToken,
2093
+ ...this.driveListOptions
2094
+ }), folderId);
2095
+ return {
2096
+ entries: (res.data.files ?? []).map((file) => this.toMetadata(file)),
2097
+ nextPageToken: res.data.nextPageToken ?? void 0
2098
+ };
2099
+ }
2100
+ /** Gets stable metadata by ID after validating its configured source scope. */
2101
+ async getMetadataById(fileId) {
2102
+ await this.canonicalizeRoots();
2103
+ const metadata = await this.getMetadataUnchecked(fileId);
2104
+ await this.assertAllowedMetadata(metadata);
2105
+ return metadata;
2106
+ }
2107
+ /**
2108
+ * Acquires a stable ID with an explicit byte bound. Native Google files must
2109
+ * name an export MIME type so callers choose fidelity deliberately.
2110
+ */
2111
+ async acquireById(fileId, options) {
2112
+ if (!Number.isSafeInteger(options.maxBytes) || options.maxBytes < 0) throw new FilesystemError("Google Drive acquisition maxBytes must be a non-negative safe integer", "EINVAL", fileId, "gdrive");
2113
+ const requested = await this.getMetadataById(fileId);
2114
+ const target = await this.resolveAcquisitionTarget(requested);
2115
+ if (options.expectedRevision !== void 0 && target.revisionId !== options.expectedRevision) throw new DriveRevisionChangedError(fileId);
2116
+ if (target.size !== void 0 && target.size > options.maxBytes) throw new DriveAcquisitionLimitError(target.id, options.maxBytes);
2117
+ const isNative = target.mimeType.startsWith(GOOGLE_NATIVE_MIME_PREFIX);
2118
+ if (isNative && !options.exportMimeType) throw new FilesystemError("Google Drive native files require an explicit exportMimeType for acquisition", "EINVAL", target.id, "gdrive");
2119
+ if (isNative && !target.revisionId) throw new DriveRevisionChangedError(target.id);
2120
+ const drive = await this.getDrive();
2121
+ const response = isNative ? await this.wrapApiCall(() => drive.files.export({
2122
+ fileId: target.id,
2123
+ mimeType: options.exportMimeType,
2124
+ ...this.driveFileOptions
2125
+ }, { responseType: "stream" }), target.id) : await this.wrapApiCall(() => drive.files.get({
2126
+ fileId: target.id,
2127
+ alt: "media",
2128
+ ...this.driveFileOptions
2129
+ }, { responseType: "stream" }), target.id);
2130
+ const bytes = await this.readBoundedResponse(response.data, target.id, options.maxBytes);
2131
+ const after = await this.getMetadataById(target.id);
2132
+ if (after.revisionId !== target.revisionId) throw new DriveRevisionChangedError(target.id);
2133
+ return {
2134
+ bytes,
2135
+ metadata: after,
2136
+ contentHash: createHash("sha256").update(bytes).digest("hex"),
2137
+ exportMimeType: isNative ? options.exportMimeType : void 0
2138
+ };
2139
+ }
2140
+ async getMetadataUnchecked(fileId) {
2141
+ const drive = await this.getDrive();
2142
+ const res = await this.wrapApiCall(() => drive.files.get({
2143
+ fileId,
2144
+ fields: DRIVE_METADATA_FIELDS,
2145
+ ...this.driveFileOptions
2146
+ }), fileId);
2147
+ return this.toMetadata(res.data);
2148
+ }
2149
+ async canonicalizeRoots() {
2150
+ if (!this.allowedFolderIds.has("root")) return;
2151
+ if (!this.rootsCanonicalization) {
2152
+ const pending = (async () => {
2153
+ const root = await this.getMetadataUnchecked("root");
2154
+ if (root.trashed) throw new FileNotFoundError("root", "gdrive");
2155
+ if (!root.id || root.id === "root" || root.mimeType !== "application/vnd.google-apps.folder") throw new DriveScopeError("root");
2156
+ if (this.allowedFolderIds.has("root")) {
2157
+ this.allowedFolderIds.delete("root");
2158
+ this.allowedFolderIds.add(root.id);
2159
+ }
2160
+ if (this.rootFolderId === "root") this.rootFolderId = root.id;
2161
+ })();
2162
+ this.rootsCanonicalization = pending;
2163
+ pending.catch(() => {
2164
+ if (this.rootsCanonicalization === pending) this.rootsCanonicalization = void 0;
2165
+ });
2166
+ }
2167
+ await this.rootsCanonicalization;
2168
+ }
2169
+ async assertAllowedId(fileId) {
2170
+ await this.canonicalizeRoots();
2171
+ if (fileId === "root" && this.rootFolderId === "root") throw new DriveScopeError(fileId);
2172
+ await this.assertAllowedMetadata(await this.getMetadataUnchecked(fileId));
2173
+ }
2174
+ async assertAllowedMetadata(metadata) {
2175
+ if (metadata.trashed) throw new FileNotFoundError(metadata.id, "gdrive");
2176
+ if (this.allowedFolderIds.has(metadata.id)) return;
2177
+ const visited = /* @__PURE__ */ new Set();
2178
+ const pending = [...metadata.parents];
2179
+ while (pending.length > 0) {
2180
+ const parentId = pending.pop();
2181
+ if (!parentId) continue;
2182
+ if (visited.has(parentId)) continue;
2183
+ visited.add(parentId);
2184
+ const parent = await this.getMetadataUnchecked(parentId);
2185
+ if (parent.trashed) throw new FileNotFoundError(parent.id, "gdrive");
2186
+ if (this.allowedFolderIds.has(parent.id)) return;
2187
+ pending.push(...parent.parents);
2188
+ }
2189
+ throw new DriveScopeError(metadata.id);
2190
+ }
2191
+ async resolveAcquisitionTarget(metadata) {
2192
+ const visited = /* @__PURE__ */ new Set();
2193
+ let target = metadata;
2194
+ while (target.shortcutDetails) {
2195
+ if (visited.has(target.id)) throw new DriveScopeError(target.id);
2196
+ visited.add(target.id);
2197
+ target = await this.getMetadataById(target.shortcutDetails.targetId);
2198
+ }
2199
+ return target;
2200
+ }
2201
+ async readBoundedResponse(body, fileId, maxBytes) {
2202
+ if (Buffer.isBuffer(body) || body instanceof Uint8Array) {
2203
+ if (body.byteLength > maxBytes) throw new DriveAcquisitionLimitError(fileId, maxBytes);
2204
+ return Buffer.from(body);
2205
+ }
2206
+ if (!body || typeof body[Symbol.asyncIterator] !== "function") throw new FilesystemError("Google Drive acquisition did not return a readable stream", "EIO", fileId, "gdrive");
2207
+ const chunks = [];
2208
+ let total = 0;
2209
+ for await (const chunk of body) {
2210
+ const bytes = Buffer.from(chunk);
2211
+ total += bytes.byteLength;
2212
+ if (total > maxBytes) {
2213
+ if (typeof body.destroy === "function") body.destroy();
2214
+ throw new DriveAcquisitionLimitError(fileId, maxBytes);
2215
+ }
2216
+ chunks.push(bytes);
2217
+ }
2218
+ return Buffer.concat(chunks, total);
2219
+ }
2040
2220
  /** Checks whether a non-trashed file or folder exists at the given path. */
2041
2221
  async exists(path) {
2042
2222
  return await this.resolvePathToId(path) !== null;
@@ -2051,19 +2231,22 @@ var GoogleDriveProvider = class extends BaseFilesystemProvider {
2051
2231
  const drive = await this.getDrive();
2052
2232
  const exportMime = GOOGLE_EXPORT_MIMES[(await this.wrapApiCall(() => drive.files.get({
2053
2233
  fileId,
2054
- fields: "mimeType"
2234
+ fields: "mimeType",
2235
+ ...this.driveFileOptions
2055
2236
  }), path)).data.mimeType];
2056
2237
  let data;
2057
2238
  if (exportMime) {
2058
2239
  const res = await this.wrapApiCall(() => drive.files.export({
2059
2240
  fileId,
2060
- mimeType: exportMime
2241
+ mimeType: exportMime,
2242
+ ...this.driveFileOptions
2061
2243
  }, { responseType: "arraybuffer" }), path);
2062
2244
  data = Buffer.from(res.data);
2063
2245
  } else {
2064
2246
  const res = await this.wrapApiCall(() => drive.files.get({
2065
2247
  fileId,
2066
- alt: "media"
2248
+ alt: "media",
2249
+ ...this.driveFileOptions
2067
2250
  }, { responseType: "arraybuffer" }), path);
2068
2251
  data = Buffer.from(res.data);
2069
2252
  }
@@ -2212,7 +2395,8 @@ var GoogleDriveProvider = class extends BaseFilesystemProvider {
2212
2395
  q: `'${folderId}' in parents and trashed = false`,
2213
2396
  fields: "nextPageToken, files(id, name, mimeType, size, modifiedTime)",
2214
2397
  pageSize: this.pageSize,
2215
- pageToken
2398
+ pageToken,
2399
+ ...this.driveListOptions
2216
2400
  }), path);
2217
2401
  const files = res.data.files || [];
2218
2402
  for (const file of files) {
@@ -2252,7 +2436,8 @@ var GoogleDriveProvider = class extends BaseFilesystemProvider {
2252
2436
  const drive = await this.getDrive();
2253
2437
  const file = (await this.wrapApiCall(() => drive.files.get({
2254
2438
  fileId,
2255
- fields: "id, name, mimeType, size, createdTime, modifiedTime"
2439
+ fields: "id, name, mimeType, size, createdTime, modifiedTime",
2440
+ ...this.driveFileOptions
2256
2441
  }), path)).data;
2257
2442
  const isDir = file.mimeType === "application/vnd.google-apps.folder";
2258
2443
  const modifiedTime = new Date(file.modifiedTime);
@@ -3030,6 +3215,6 @@ var src_default = factory_exports;
3030
3215
  /** @internal */
3031
3216
  var PACKAGE_VERSION_INITIALIZED = true;
3032
3217
  //#endregion
3033
- export { DEFAULT_ZIP_MANIFEST_LIMITS, DirectoryNotEmptyError, FileNotFoundError, FilesystemAdapter, FilesystemError, GoogleDriveProvider, InvalidPathError, InvalidZipArchiveError, LocalFilesystemProvider, PACKAGE_VERSION_INITIALIZED, PermissionError, S3FilesystemProvider, UnsafeZipEntryError, UnsupportedZipFeatureError, ZipManifestError, ZipManifestLimitError, addRateLimit, src_default as default, download, downloadFileWithCache, ensureDirectoryExists, fetchBuffer, fetchJSON, fetchText, fetchToFile, getAvailableProviders, getCached, getFilesystem, getMimeType, getProviderInfo, getRateLimit, initializeProviders, inspectZipManifest, isDirectory, isFile, isProviderAvailable, listFiles, redactFilesystemConfig, registerProvider, setCached, upload, writeResponseToFile };
3218
+ export { DEFAULT_ZIP_MANIFEST_LIMITS, DirectoryNotEmptyError, DriveAcquisitionLimitError, DriveRevisionChangedError, DriveScopeError, FileNotFoundError, FilesystemAdapter, FilesystemError, GoogleDriveProvider, InvalidPathError, InvalidZipArchiveError, LocalFilesystemProvider, PACKAGE_VERSION_INITIALIZED, PermissionError, S3FilesystemProvider, UnsafeZipEntryError, UnsupportedZipFeatureError, ZipManifestError, ZipManifestLimitError, addRateLimit, src_default as default, download, downloadFileWithCache, ensureDirectoryExists, fetchBuffer, fetchJSON, fetchText, fetchToFile, getAvailableProviders, getCached, getFilesystem, getMimeType, getProviderInfo, getRateLimit, initializeProviders, inspectZipManifest, isDirectory, isFile, isProviderAvailable, listFiles, redactFilesystemConfig, registerProvider, setCached, upload, writeResponseToFile };
3034
3219
 
3035
3220
  //# sourceMappingURL=index.js.map