@aigne/afs-s3 1.11.0-beta.10
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/LICENSE.md +26 -0
- package/dist/index.d.mts +202 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1410 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["AFSError","response","objectCount","prefixCount","lines"],"sources":["../src/cache.ts","../src/client.ts","../src/errors.ts","../src/operations/multipart.ts","../src/operations/select.ts","../src/platform-ref.ts","../src/types.ts","../src/s3-afs.ts"],"sourcesContent":["/**\n * S3 Response Caching (Phase 4)\n *\n * LRU cache with TTL for S3 list and stat results.\n */\n\n/**\n * Cache entry with expiration\n */\ninterface CacheEntry<T> {\n value: T;\n expiresAt: number;\n}\n\n/**\n * LRU Cache with TTL support\n */\nexport class LRUCache<T> {\n private cache = new Map<string, CacheEntry<T>>();\n private maxSize: number;\n private defaultTtl: number;\n\n /**\n * Create a new LRU cache\n *\n * @param maxSize - Maximum number of entries (default: 1000)\n * @param defaultTtl - Default TTL in seconds (default: 60)\n */\n constructor(maxSize = 1000, defaultTtl = 60) {\n this.maxSize = maxSize;\n this.defaultTtl = defaultTtl;\n }\n\n /**\n * Get a value from the cache\n *\n * @param key - Cache key\n * @returns Cached value or undefined if not found/expired\n */\n get(key: string): T | undefined {\n const entry = this.cache.get(key);\n\n if (!entry) {\n return undefined;\n }\n\n // Check if expired\n if (Date.now() > entry.expiresAt) {\n this.cache.delete(key);\n return undefined;\n }\n\n // Move to end (most recently used)\n this.cache.delete(key);\n this.cache.set(key, entry);\n\n return entry.value;\n }\n\n /**\n * Set a value in the cache\n *\n * @param key - Cache key\n * @param value - Value to cache\n * @param ttl - TTL in seconds (optional, uses default)\n */\n set(key: string, value: T, ttl?: number): void {\n // Remove if already exists (to update position)\n if (this.cache.has(key)) {\n this.cache.delete(key);\n }\n\n // Evict oldest entries if at capacity\n while (this.cache.size >= this.maxSize) {\n const oldestKey = this.cache.keys().next().value;\n if (oldestKey) {\n this.cache.delete(oldestKey);\n }\n }\n\n const expiresAt = Date.now() + (ttl ?? this.defaultTtl) * 1000;\n this.cache.set(key, { value, expiresAt });\n }\n\n /**\n * Delete a value from the cache\n *\n * @param key - Cache key\n */\n delete(key: string): void {\n this.cache.delete(key);\n }\n\n /**\n * Delete all entries matching a prefix\n *\n * @param prefix - Key prefix to match\n */\n deleteByPrefix(prefix: string): void {\n for (const key of this.cache.keys()) {\n if (key.startsWith(prefix)) {\n this.cache.delete(key);\n }\n }\n }\n\n /**\n * Clear all entries\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Get the number of entries in the cache\n */\n get size(): number {\n return this.cache.size;\n }\n\n /**\n * Prune expired entries\n */\n prune(): void {\n const now = Date.now();\n for (const [key, entry] of this.cache.entries()) {\n if (now > entry.expiresAt) {\n this.cache.delete(key);\n }\n }\n }\n}\n\n/**\n * Create a cache key from bucket, prefix, and path\n */\nexport function createCacheKey(\n bucket: string,\n prefix: string,\n path: string,\n suffix?: string,\n): string {\n const base = `${bucket}:${prefix}:${path}`;\n return suffix ? `${base}:${suffix}` : base;\n}\n","/**\n * S3 Client Factory\n *\n * Creates and configures AWS S3 clients.\n */\n\nimport { S3Client, type S3ClientConfig } from \"@aws-sdk/client-s3\";\nimport type { AFSS3Options } from \"./types.js\";\n\n/**\n * Create an S3 client with the given options\n *\n * Uses AWS SDK's default credential chain:\n * 1. Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)\n * 2. Shared credentials file (~/.aws/credentials)\n * 3. IAM role (EC2/ECS/Lambda)\n * 4. SSO credentials\n *\n * @param options - S3 provider options\n * @returns Configured S3 client\n */\nexport function createS3Client(options: AFSS3Options): S3Client {\n const config: S3ClientConfig = {};\n\n // Region configuration\n // For S3-compatible services (MinIO, R2, B2), default to \"us-east-1\" if no region specified\n if (options.region) {\n config.region = options.region;\n } else if (options.endpoint) {\n config.region = \"us-east-1\";\n }\n\n // Custom endpoint for S3-compatible services\n if (options.endpoint) {\n config.endpoint = options.endpoint;\n }\n\n // Force path-style URLs (needed for MinIO, some S3-compatible services)\n if (options.forcePathStyle) {\n config.forcePathStyle = true;\n }\n\n // Explicit credentials (for testing or non-AWS environments)\n if (options.credentials) {\n config.credentials = {\n accessKeyId: options.credentials.accessKeyId,\n secretAccessKey: options.credentials.secretAccessKey,\n sessionToken: options.credentials.sessionToken,\n };\n }\n\n return new S3Client(config);\n}\n","/**\n * S3 Error Handling\n *\n * Maps AWS S3 errors to AFS errors.\n */\n\nimport { AFSNotFoundError } from \"@aigne/afs\";\n\n/**\n * AFS error codes\n */\nexport const AFSErrorCode = {\n ENTRY_NOT_FOUND: \"ENTRY_NOT_FOUND\",\n MODULE_NOT_FOUND: \"MODULE_NOT_FOUND\",\n PERMISSION_DENIED: \"PERMISSION_DENIED\",\n AUTH_ERROR: \"AUTH_ERROR\",\n RATE_LIMITED: \"RATE_LIMITED\",\n TYPE_MISMATCH: \"TYPE_MISMATCH\",\n INTERNAL_ERROR: \"INTERNAL_ERROR\",\n} as const;\n\nexport type AFSErrorCode = (typeof AFSErrorCode)[keyof typeof AFSErrorCode];\n\n/**\n * AFS Error class\n */\nexport class AFSError extends Error {\n readonly code: AFSErrorCode;\n readonly retryAfter?: number;\n\n constructor(code: AFSErrorCode, message: string, options?: { retryAfter?: number }) {\n super(message);\n this.name = \"AFSError\";\n this.code = code;\n this.retryAfter = options?.retryAfter;\n }\n}\n\n/**\n * Map S3 error to AFS error\n *\n * @param error - AWS S3 error\n * @param path - Optional path for AFSNotFoundError (with leading slash)\n * @returns AFS error (or throws the original error if it's already an AFS error)\n */\nexport function mapS3Error(error: unknown, path?: string): AFSError | AFSNotFoundError | never {\n // Pass through AFS errors unchanged (check by name since different module instances)\n if (error && typeof error === \"object\" && \"name\" in error) {\n const errorObj = error as { name?: string; code?: string };\n if (\n errorObj.name === \"AFSNotFoundError\" ||\n errorObj.name === \"AFSError\" ||\n errorObj.code === \"AFS_NOT_FOUND\"\n ) {\n throw error;\n }\n }\n\n // Handle AWS SDK errors\n if (error && typeof error === \"object\" && \"name\" in error) {\n const awsError = error as {\n name: string;\n message?: string;\n $metadata?: { httpStatusCode?: number };\n };\n const message = awsError.message ?? \"Unknown S3 error\";\n\n switch (awsError.name) {\n case \"NoSuchBucket\":\n return new AFSError(AFSErrorCode.MODULE_NOT_FOUND, `Bucket not found: ${message}`);\n\n case \"NoSuchKey\":\n case \"NotFound\":\n if (path) {\n return new AFSNotFoundError(path);\n }\n return new AFSError(AFSErrorCode.ENTRY_NOT_FOUND, `Object not found: ${message}`);\n\n case \"AccessDenied\":\n case \"Forbidden\":\n return new AFSError(AFSErrorCode.PERMISSION_DENIED, `Access denied: ${message}`);\n\n case \"InvalidAccessKeyId\":\n case \"SignatureDoesNotMatch\":\n case \"ExpiredToken\":\n case \"TokenRefreshRequired\":\n return new AFSError(AFSErrorCode.AUTH_ERROR, `Authentication failed: ${message}`);\n\n case \"SlowDown\":\n case \"ServiceUnavailable\":\n return new AFSError(AFSErrorCode.RATE_LIMITED, `Rate limited: ${message}`, {\n retryAfter: 1000,\n });\n\n default: {\n // Check HTTP status code\n const statusCode = awsError.$metadata?.httpStatusCode;\n if (statusCode === 404) {\n if (path) {\n return new AFSNotFoundError(path);\n }\n return new AFSError(AFSErrorCode.ENTRY_NOT_FOUND, message);\n }\n if (statusCode === 403) {\n return new AFSError(AFSErrorCode.PERMISSION_DENIED, message);\n }\n if (statusCode === 401) {\n return new AFSError(AFSErrorCode.AUTH_ERROR, message);\n }\n if (statusCode === 429 || statusCode === 503) {\n return new AFSError(AFSErrorCode.RATE_LIMITED, message, { retryAfter: 1000 });\n }\n\n return new AFSError(AFSErrorCode.INTERNAL_ERROR, message);\n }\n }\n }\n\n // Handle generic errors\n if (error instanceof Error) {\n return new AFSError(AFSErrorCode.INTERNAL_ERROR, error.message);\n }\n\n return new AFSError(AFSErrorCode.INTERNAL_ERROR, String(error));\n}\n","/**\n * S3 Multipart Upload (Phase 2)\n *\n * Handles multipart upload for large files (>5GB).\n * S3 requires multipart upload for objects larger than 5GB.\n */\n\nimport {\n AbortMultipartUploadCommand,\n CompleteMultipartUploadCommand,\n CreateMultipartUploadCommand,\n type S3Client,\n UploadPartCommand,\n} from \"@aws-sdk/client-s3\";\nimport { mapS3Error } from \"../errors.js\";\n\n/**\n * Minimum part size (5MB) - AWS S3 requirement\n */\nconst MIN_PART_SIZE = 5 * 1024 * 1024;\n\n/**\n * Maximum part size (5GB) - AWS S3 requirement\n */\nconst MAX_PART_SIZE = 5 * 1024 * 1024 * 1024;\n\n/**\n * Threshold for switching to multipart upload (100MB)\n * Files larger than this will use multipart upload\n */\nexport const MULTIPART_THRESHOLD = 100 * 1024 * 1024;\n\n/**\n * Default part size (10MB)\n */\nconst DEFAULT_PART_SIZE = 10 * 1024 * 1024;\n\n/**\n * Maximum number of parts (10,000) - AWS S3 requirement\n */\nconst MAX_PARTS = 10000;\n\ninterface MultipartUploadOptions {\n /** Part size in bytes (default: 10MB) */\n partSize?: number;\n /** Content type */\n contentType?: string;\n /** Metadata to attach */\n metadata?: Record<string, string>;\n}\n\ninterface MultipartUploadResult {\n /** ETag of the completed upload */\n etag: string;\n /** Version ID if versioning is enabled */\n versionId?: string;\n}\n\n/**\n * Calculate optimal part size based on file size\n */\nfunction calculatePartSize(fileSize: number, requestedPartSize?: number): number {\n let partSize = requestedPartSize ?? DEFAULT_PART_SIZE;\n\n // Ensure minimum part size\n if (partSize < MIN_PART_SIZE) {\n partSize = MIN_PART_SIZE;\n }\n\n // Ensure maximum part size\n if (partSize > MAX_PART_SIZE) {\n partSize = MAX_PART_SIZE;\n }\n\n // If file is too large for current part size, increase it\n const partsNeeded = Math.ceil(fileSize / partSize);\n if (partsNeeded > MAX_PARTS) {\n // Calculate minimum part size needed to fit in MAX_PARTS\n partSize = Math.ceil(fileSize / MAX_PARTS);\n // Round up to next MB for efficiency\n partSize = Math.ceil(partSize / (1024 * 1024)) * 1024 * 1024;\n }\n\n return partSize;\n}\n\n/**\n * Upload a large file using multipart upload\n *\n * @param client - S3 client\n * @param bucket - Bucket name\n * @param key - Object key\n * @param data - File content as Buffer\n * @param options - Upload options\n * @returns Upload result with ETag\n */\nexport async function multipartUpload(\n client: S3Client,\n bucket: string,\n key: string,\n data: Buffer,\n options?: MultipartUploadOptions,\n): Promise<MultipartUploadResult> {\n const fileSize = data.length;\n const partSize = calculatePartSize(fileSize, options?.partSize);\n\n // Initiate multipart upload\n const createCommand = new CreateMultipartUploadCommand({\n Bucket: bucket,\n Key: key,\n ContentType: options?.contentType ?? \"application/octet-stream\",\n Metadata: options?.metadata,\n });\n\n const createResponse = await client.send(createCommand);\n const uploadId = createResponse.UploadId;\n\n if (!uploadId) {\n throw new Error(\"Failed to initiate multipart upload: no uploadId returned\");\n }\n\n const parts: { ETag: string; PartNumber: number }[] = [];\n\n try {\n // Upload parts\n const totalParts = Math.ceil(fileSize / partSize);\n\n for (let partNumber = 1; partNumber <= totalParts; partNumber++) {\n const start = (partNumber - 1) * partSize;\n const end = Math.min(start + partSize, fileSize);\n const partData = data.subarray(start, end);\n\n const uploadPartCommand = new UploadPartCommand({\n Bucket: bucket,\n Key: key,\n UploadId: uploadId,\n PartNumber: partNumber,\n Body: partData,\n });\n\n const uploadPartResponse = await client.send(uploadPartCommand);\n\n if (!uploadPartResponse.ETag) {\n throw new Error(`Failed to upload part ${partNumber}: no ETag returned`);\n }\n\n parts.push({\n ETag: uploadPartResponse.ETag,\n PartNumber: partNumber,\n });\n }\n\n // Complete multipart upload\n const completeCommand = new CompleteMultipartUploadCommand({\n Bucket: bucket,\n Key: key,\n UploadId: uploadId,\n MultipartUpload: {\n Parts: parts,\n },\n });\n\n const completeResponse = await client.send(completeCommand);\n\n return {\n etag: completeResponse.ETag?.replace(/\"/g, \"\") ?? \"\",\n versionId: completeResponse.VersionId,\n };\n } catch (error) {\n // Abort the multipart upload on failure\n try {\n const abortCommand = new AbortMultipartUploadCommand({\n Bucket: bucket,\n Key: key,\n UploadId: uploadId,\n });\n await client.send(abortCommand);\n } catch {\n // Ignore abort errors\n }\n\n throw mapS3Error(error);\n }\n}\n\n/**\n * Check if a file should use multipart upload\n */\nexport function shouldUseMultipart(size: number): boolean {\n return size >= MULTIPART_THRESHOLD;\n}\n","/**\n * S3 Select Support (Phase 3)\n *\n * Runs SQL-like queries on CSV and JSON files stored in S3.\n * This enables server-side filtering, reducing data transfer.\n */\n\nimport {\n type S3Client,\n SelectObjectContentCommand,\n type SelectObjectContentEventStream,\n} from \"@aws-sdk/client-s3\";\nimport { mapS3Error } from \"../errors.js\";\n\n/**\n * Input serialization format\n */\nexport type InputFormat = \"CSV\" | \"JSON\" | \"Parquet\";\n\n/**\n * Select query options\n */\nexport interface SelectOptions {\n /** Input format (default: auto-detected from extension) */\n inputFormat?: InputFormat;\n /** CSV options */\n csv?: {\n /** Field delimiter (default: ,) */\n fieldDelimiter?: string;\n /** Record delimiter (default: \\n) */\n recordDelimiter?: string;\n /** Whether first row is header (default: true) */\n fileHeaderInfo?: \"USE\" | \"IGNORE\" | \"NONE\";\n /** Quote character (default: \") */\n quoteCharacter?: string;\n /** Comment character */\n comments?: string;\n };\n /** JSON options */\n json?: {\n /** JSON document type */\n type?: \"DOCUMENT\" | \"LINES\";\n };\n /** Output format (default: JSON) */\n outputFormat?: \"CSV\" | \"JSON\";\n}\n\n/**\n * Select query result\n */\nexport interface SelectResult {\n /** Query results as array of records */\n records: unknown[];\n /** Stats about the query */\n stats?: {\n bytesScanned: number;\n bytesProcessed: number;\n bytesReturned: number;\n };\n}\n\n/**\n * Detect input format from file extension\n */\nfunction detectInputFormat(path: string): InputFormat {\n const ext = path.toLowerCase().split(\".\").pop();\n switch (ext) {\n case \"csv\":\n case \"tsv\":\n return \"CSV\";\n case \"json\":\n case \"jsonl\":\n case \"ndjson\":\n return \"JSON\";\n case \"parquet\":\n return \"Parquet\";\n default:\n return \"JSON\";\n }\n}\n\n/**\n * Run a SQL-like query on an S3 object\n *\n * @param client - S3 client\n * @param bucket - Bucket name\n * @param mountPrefix - Mount prefix from options\n * @param path - Path relative to mount point\n * @param query - SQL query (e.g., \"SELECT * FROM s3object WHERE age > 21\")\n * @param options - Select options\n * @returns Query results\n */\nexport async function selectQuery(\n client: S3Client,\n bucket: string,\n mountPrefix: string,\n path: string,\n query: string,\n options?: SelectOptions,\n): Promise<SelectResult> {\n try {\n const normalizedPath = path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const key = mountPrefix ? `${mountPrefix}/${normalizedPath}` : normalizedPath;\n\n const inputFormat = options?.inputFormat ?? detectInputFormat(path);\n const outputFormat = options?.outputFormat ?? \"JSON\";\n\n // Build input serialization based on format\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const inputSerialization: Record<string, any> = {};\n\n if (inputFormat === \"CSV\") {\n inputSerialization.CSV = {\n FieldDelimiter: options?.csv?.fieldDelimiter ?? \",\",\n RecordDelimiter: options?.csv?.recordDelimiter ?? \"\\n\",\n FileHeaderInfo: options?.csv?.fileHeaderInfo ?? \"USE\",\n QuoteCharacter: options?.csv?.quoteCharacter ?? '\"',\n Comments: options?.csv?.comments,\n };\n } else if (inputFormat === \"JSON\") {\n inputSerialization.JSON = {\n Type: options?.json?.type ?? \"DOCUMENT\",\n };\n } else if (inputFormat === \"Parquet\") {\n inputSerialization.Parquet = {};\n }\n\n // Build output serialization\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const outputSerialization: Record<string, any> = {};\n\n if (outputFormat === \"JSON\") {\n outputSerialization.JSON = {};\n } else {\n outputSerialization.CSV = {};\n }\n\n const command = new SelectObjectContentCommand({\n Bucket: bucket,\n Key: key,\n ExpressionType: \"SQL\",\n Expression: query,\n InputSerialization: inputSerialization,\n OutputSerialization: outputSerialization,\n });\n\n const response = await client.send(command);\n\n // Process the event stream\n const records: unknown[] = [];\n let stats: SelectResult[\"stats\"] | undefined;\n\n if (response.Payload) {\n for await (const event of response.Payload as AsyncIterable<SelectObjectContentEventStream>) {\n if (event.Records?.Payload) {\n // Parse records from payload\n const text = new TextDecoder().decode(event.Records.Payload);\n // Split by newline and parse each line as JSON\n const lines = text.split(\"\\n\").filter((line) => line.trim());\n for (const line of lines) {\n try {\n records.push(JSON.parse(line));\n } catch {\n // If not valid JSON, just push as string\n records.push(line);\n }\n }\n }\n if (event.Stats?.Details) {\n stats = {\n bytesScanned: Number(event.Stats.Details.BytesScanned ?? 0),\n bytesProcessed: Number(event.Stats.Details.BytesProcessed ?? 0),\n bytesReturned: Number(event.Stats.Details.BytesReturned ?? 0),\n };\n }\n }\n }\n\n return { records, stats };\n } catch (error) {\n throw mapS3Error(error);\n }\n}\n","/**\n * S3 Platform Reference\n *\n * Generates AWS Console URLs for S3 objects and directories.\n */\n\n/**\n * Platform reference containing console URL\n */\nexport interface S3PlatformRef {\n consoleUrl: string;\n}\n\n/**\n * Generate platform reference with AWS Console URL\n *\n * @param bucket - S3 bucket name\n * @param region - AWS region (defaults to us-east-1)\n * @param key - S3 object key (without leading slash)\n * @param isDirectory - Whether this is a directory (prefix)\n * @returns Platform reference with console URL\n */\nexport function generatePlatformRef(\n bucket: string,\n region: string | undefined,\n key: string,\n isDirectory: boolean,\n): S3PlatformRef {\n const effectiveRegion = region || \"us-east-1\";\n\n if (isDirectory) {\n // Directory URL: https://s3.console.aws.amazon.com/s3/buckets/{bucket}?region={region}&prefix={prefix}/\n const normalizedPrefix = key.endsWith(\"/\") ? key : key ? `${key}/` : \"\";\n return {\n consoleUrl: `https://s3.console.aws.amazon.com/s3/buckets/${bucket}?region=${effectiveRegion}&prefix=${normalizedPrefix}`,\n };\n }\n\n // File URL: https://s3.console.aws.amazon.com/s3/object/{bucket}?region={region}&prefix={key}\n const encodedKey = encodeURIComponent(key).replace(/%2F/g, \"/\");\n return {\n consoleUrl: `https://s3.console.aws.amazon.com/s3/object/${bucket}?region=${effectiveRegion}&prefix=${encodedKey}`,\n };\n}\n","/**\n * AFS S3 Provider Types\n */\n\nimport { camelize, optionalize } from \"@aigne/afs/utils/zod\";\nimport type { S3Client } from \"@aws-sdk/client-s3\";\nimport { z } from \"zod\";\n\n/**\n * Configuration options for AFSS3\n */\nexport interface AFSS3Options {\n /** Module name (used as mount path segment) */\n name?: string;\n\n /** Module description */\n description?: string;\n\n /** S3 bucket name */\n bucket: string;\n\n /** Key prefix (optional) */\n prefix?: string;\n\n /** AWS region (defaults to environment) */\n region?: string;\n\n /** Access mode */\n accessMode?: \"readonly\" | \"readwrite\";\n\n /** Custom endpoint for S3-compatible services (MinIO, R2, B2) */\n endpoint?: string;\n\n /** Force path-style URLs (needed for some S3-compatible services) */\n forcePathStyle?: boolean;\n\n /** AWS credentials profile name */\n profile?: string;\n\n /** Explicit credentials (for testing or non-AWS environments) */\n credentials?: {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string;\n };\n\n /** Cache TTL in seconds (0 = no cache, Phase 4) */\n cacheTtl?: number;\n\n /**\n * Pre-configured S3 client (for testing or advanced use cases)\n * If provided, endpoint/region/credentials/profile options are ignored.\n */\n client?: S3Client;\n}\n\n/**\n * S3 bucket name validation regex\n * - 3-63 characters\n * - lowercase letters, numbers, hyphens, dots\n * - must start and end with letter or number\n */\nconst BUCKET_NAME_REGEX = /^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/;\n\n/**\n * Zod schema for options validation\n */\nexport const afss3OptionsSchema = camelize(\n z\n .object({\n name: optionalize(z.string()),\n description: optionalize(z.string()),\n bucket: z.string().regex(BUCKET_NAME_REGEX, \"Invalid S3 bucket name\"),\n prefix: optionalize(z.string()),\n region: optionalize(z.string()),\n accessMode: optionalize(z.enum([\"readonly\", \"readwrite\"])),\n endpoint: optionalize(z.string().url()),\n forcePathStyle: optionalize(z.boolean()),\n profile: optionalize(z.string()),\n credentials: optionalize(\n z.object({\n accessKeyId: z.string().meta({\n sensitive: true,\n env: [\"AWS_ACCESS_KEY_ID\"],\n description: \"AWS access key ID\",\n }),\n secretAccessKey: z.string().meta({\n sensitive: true,\n env: [\"AWS_SECRET_ACCESS_KEY\"],\n description: \"AWS secret access key\",\n }),\n sessionToken: optionalize(\n z.string().meta({\n sensitive: true,\n env: [\"AWS_SESSION_TOKEN\"],\n description: \"AWS session token\",\n }),\n ),\n }),\n ),\n cacheTtl: optionalize(z.number().int().min(0)),\n })\n .strict(),\n);\n\n/**\n * Parsed S3 URI\n */\nexport interface ParsedS3Uri {\n bucket: string;\n prefix: string;\n}\n","/**\n * AFS S3 Provider\n *\n * S3 provider using AFSBaseProvider decorator routing pattern.\n * Provides access to AWS S3 and S3-compatible storage (MinIO, R2, B2).\n */\n\nimport {\n Actions,\n type AFSAccessMode,\n AFSBaseProvider,\n type AFSDeleteResult,\n type AFSEntry,\n AFSError,\n type AFSExecResult,\n type AFSExplainResult,\n type AFSListResult,\n type AFSModuleClass,\n type AFSModuleLoadParams,\n AFSNotFoundError,\n type AFSSearchResult,\n type AFSStatResult,\n type AFSWriteEntryPayload,\n type AFSWriteResult,\n type CapabilitiesManifest,\n Delete,\n Explain,\n List,\n Meta,\n type ProviderManifest,\n Read,\n type RouteContext,\n Search,\n Stat,\n Write,\n} from \"@aigne/afs\";\nimport { zodParse } from \"@aigne/afs/utils/zod\";\nimport {\n DeleteObjectCommand,\n GetObjectCommand,\n HeadObjectCommand,\n ListObjectsV2Command,\n ListObjectVersionsCommand,\n PutObjectCommand,\n type S3Client,\n} from \"@aws-sdk/client-s3\";\nimport { getSignedUrl } from \"@aws-sdk/s3-request-presigner\";\nimport { joinURL } from \"ufo\";\nimport { z } from \"zod\";\nimport { createCacheKey, LRUCache } from \"./cache.js\";\nimport { createS3Client } from \"./client.js\";\nimport { mapS3Error } from \"./errors.js\";\nimport { multipartUpload, shouldUseMultipart } from \"./operations/multipart.js\";\nimport { selectQuery } from \"./operations/select.js\";\nimport { generatePlatformRef } from \"./platform-ref.js\";\nimport { type AFSS3Options, afss3OptionsSchema } from \"./types.js\";\n\n/**\n * Default URL expiration time (1 hour)\n */\nconst DEFAULT_EXPIRES_IN = 3600;\n\n/**\n * Maximum expiration time (7 days)\n */\nconst MAX_EXPIRES_IN = 604800;\n\n/**\n * AFSS3 Provider using Base Provider pattern\n *\n * Provides access to AWS S3 and S3-compatible storage through AFS.\n * Uses decorator routing (@List, @Read, @Write, @Delete, @Meta, @Actions).\n *\n * @example\n * ```typescript\n * const s3 = new AFSS3({\n * bucket: \"my-bucket\",\n * prefix: \"data\",\n * region: \"us-east-1\",\n * });\n *\n * // Mount to AFS\n * afs.mount(s3);\n *\n * // List objects\n * const result = await afs.list(\"/modules/my-bucket/data\");\n *\n * // Read object\n * const content = await afs.read(\"/modules/my-bucket/data/file.json\");\n * ```\n */\nexport class AFSS3 extends AFSBaseProvider {\n override readonly name: string;\n override readonly description?: string;\n override readonly accessMode: AFSAccessMode;\n\n private options: Required<Pick<AFSS3Options, \"bucket\">> & AFSS3Options;\n private client: S3Client;\n private listCache?: LRUCache<AFSListResult>;\n private statCache?: LRUCache<AFSEntry>;\n\n constructor(options: AFSS3Options & { uri?: string; token?: string; auth?: unknown }) {\n super();\n\n // Extract client and strip registry-injected keys before strict schema validation\n const { client, uri: _uri, token: _token, auth: _auth, ...restOptions } = options as any;\n\n // Validate options (excluding client and registry keys)\n const parsed = afss3OptionsSchema.parse(restOptions);\n\n this.options = {\n ...parsed,\n bucket: parsed.bucket,\n prefix: parsed.prefix ?? \"\",\n accessMode: parsed.accessMode ?? \"readonly\",\n };\n\n this.name = parsed.name ?? parsed.bucket;\n this.description = parsed.description ?? `S3 bucket: ${parsed.bucket}`;\n this.accessMode = this.options.accessMode ?? \"readonly\";\n\n // Use provided client or create one\n this.client = client ?? createS3Client(this.options);\n\n // Initialize caches if cacheTtl is set\n if (parsed.cacheTtl && parsed.cacheTtl > 0) {\n this.listCache = new LRUCache<AFSListResult>(1000, parsed.cacheTtl);\n this.statCache = new LRUCache<AFSEntry>(5000, parsed.cacheTtl);\n }\n }\n\n /**\n * Schema for configuration validation\n */\n static schema() {\n return afss3OptionsSchema;\n }\n\n /**\n * Provider manifest for URI-based discovery\n */\n static manifest(): ProviderManifest {\n return {\n name: \"s3\",\n description:\n \"AWS S3 and S3-compatible object storage (MinIO, R2, B2).\\n- Browse, read, write, and delete objects; search by key prefix\\n- Exec actions: `presign-download`, `presign-upload`, `multipart-upload`, `select` (SQL on objects)\\n- Path structure: `/{key-prefix}/{object-key}`\",\n uriTemplate: \"s3://{bucket}/{prefix+?}\",\n category: \"cloud-storage\",\n schema: z.object({\n bucket: z.string(),\n prefix: z.string().optional(),\n region: z.string().optional(),\n accessKeyId: z.string().meta({ sensitive: true }).optional(),\n secretAccessKey: z.string().meta({ sensitive: true }).optional(),\n endpoint: z.string().optional(),\n profile: z.string().optional(),\n }),\n tags: [\"aws\", \"s3\", \"cloud\", \"storage\"],\n };\n }\n\n /**\n * Load from configuration file\n */\n static async load({ basePath, config }: AFSModuleLoadParams = {}): Promise<AFSS3> {\n const options = zodParse(afss3OptionsSchema, config, { prefix: basePath });\n return new AFSS3(options);\n }\n\n // ========== Helper Methods ==========\n\n /**\n * Build the full S3 key from a path\n */\n private buildS3Key(path: string): string {\n const normalizedPath = path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n return this.options.prefix ? `${this.options.prefix}/${normalizedPath}` : normalizedPath;\n }\n\n /**\n * Generate a unique ID for an S3 object\n */\n private generateId(key: string): string {\n const cleanKey = key.replace(/^\\/+/, \"\");\n return `s3://${this.options.bucket}/${cleanKey}`;\n }\n\n /**\n * Invalidate caches for a given path\n */\n private invalidateCache(path: string): void {\n if (this.statCache) {\n const statKey = createCacheKey(this.options.bucket, this.options.prefix ?? \"\", path);\n this.statCache.delete(statKey);\n }\n\n if (this.listCache) {\n // Invalidate list cache for parent directory\n const parentPath = path.split(\"/\").slice(0, -1).join(\"/\") || \"/\";\n const listPrefix = createCacheKey(this.options.bucket, this.options.prefix ?? \"\", parentPath);\n this.listCache.deleteByPrefix(listPrefix);\n }\n }\n\n /**\n * Clear all caches\n */\n clearCache(): void {\n this.listCache?.clear();\n this.statCache?.clear();\n }\n\n // ========== List Operations ==========\n\n @List(\"/\")\n @List(\"/:path*\")\n async listHandler(ctx: RouteContext<{ path?: string }>): Promise<AFSListResult> {\n try {\n const path = ctx.params.path ?? \"\";\n const normalizedPath = path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n\n // Build the full S3 prefix\n const fullPrefix = this.options.prefix\n ? normalizedPath\n ? `${this.options.prefix}/${normalizedPath}/`\n : `${this.options.prefix}/`\n : normalizedPath\n ? `${normalizedPath}/`\n : \"\";\n\n const opts = ctx.options as { limit?: number; maxChildren?: number } | undefined;\n const maxChildren = opts?.limit ?? opts?.maxChildren ?? 1000;\n\n // Check cache first\n if (this.listCache) {\n const cacheKey = createCacheKey(\n this.options.bucket,\n this.options.prefix ?? \"\",\n normalizedPath,\n JSON.stringify(ctx.options ?? {}),\n );\n const cached = this.listCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n }\n\n const result = await this.listWithDelimiter(fullPrefix, normalizedPath, maxChildren);\n\n // Cache the result\n if (this.listCache) {\n const cacheKey = createCacheKey(\n this.options.bucket,\n this.options.prefix ?? \"\",\n normalizedPath,\n JSON.stringify(ctx.options ?? {}),\n );\n this.listCache.set(cacheKey, result);\n\n // Also populate stat cache from list results\n if (this.statCache) {\n for (const entry of result.data) {\n const statKey = createCacheKey(\n this.options.bucket,\n this.options.prefix ?? \"\",\n entry.path,\n );\n this.statCache.set(statKey, entry);\n }\n }\n }\n\n return result;\n } catch (error) {\n const normalizedPath = ctx.params.path\n ? ctx.params.path.startsWith(\"/\")\n ? ctx.params.path\n : `/${ctx.params.path}`\n : \"/\";\n throw mapS3Error(error, normalizedPath);\n }\n }\n\n /**\n * List with delimiter (single level)\n */\n private async listWithDelimiter(\n prefix: string,\n basePath: string,\n maxChildren: number,\n ): Promise<AFSListResult> {\n const childEntries: AFSEntry[] = [];\n let continuationToken: string | undefined;\n\n do {\n const command = new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: prefix,\n Delimiter: \"/\",\n MaxKeys: Math.min(maxChildren - childEntries.length, 1000),\n ContinuationToken: continuationToken,\n });\n\n const response = await this.client.send(command);\n\n // Add directories from CommonPrefixes\n if (response.CommonPrefixes) {\n for (const commonPrefix of response.CommonPrefixes) {\n if (!commonPrefix.Prefix) continue;\n\n // Extract directory name\n const dirName = commonPrefix.Prefix.slice(prefix.length).replace(/\\/$/, \"\");\n if (!dirName) continue;\n\n const normalizedPath = joinURL(\"/\", basePath, dirName);\n\n childEntries.push({\n id: this.generateId(commonPrefix.Prefix),\n path: normalizedPath,\n meta: {\n childrenCount: -1,\n platformRef: generatePlatformRef(\n this.options.bucket,\n this.options.region,\n commonPrefix.Prefix,\n true,\n ),\n },\n });\n\n if (childEntries.length >= maxChildren) break;\n }\n }\n\n // Add files from Contents\n if (response.Contents) {\n for (const object of response.Contents) {\n if (!object.Key) continue;\n\n // Skip the prefix itself (if it's a \"directory marker\")\n if (object.Key === prefix) continue;\n\n // Extract file name\n const fileName = object.Key.slice(prefix.length);\n if (!fileName || fileName.includes(\"/\")) continue;\n\n const normalizedPath = joinURL(\"/\", basePath, fileName);\n\n childEntries.push({\n id: this.generateId(object.Key),\n path: normalizedPath,\n updatedAt: object.LastModified,\n meta: {\n size: object.Size,\n lastModified: object.LastModified?.toISOString(),\n etag: object.ETag?.replace(/\"/g, \"\"),\n storageClass: object.StorageClass,\n platformRef: generatePlatformRef(\n this.options.bucket,\n this.options.region,\n object.Key,\n false,\n ),\n },\n });\n\n if (childEntries.length >= maxChildren) break;\n }\n }\n\n continuationToken = response.IsTruncated ? response.NextContinuationToken : undefined;\n } while (continuationToken && childEntries.length < maxChildren);\n\n const selfPath = basePath ? joinURL(\"/\", basePath) : \"/\";\n\n // For non-root paths with no children, check if the path exists\n if (childEntries.length === 0 && basePath) {\n // Check if this path exists as a file or directory marker\n const key = this.options.prefix ? `${this.options.prefix}/${basePath}` : basePath;\n\n try {\n const headCommand = new HeadObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n });\n await this.client.send(headCommand);\n\n // Path exists as an object (file) — files have no children\n return { data: [] };\n } catch (error: any) {\n // Also check for directory marker\n if (error?.name === \"NotFound\" || error?.$metadata?.httpStatusCode === 404) {\n try {\n const dirMarkerCommand = new HeadObjectCommand({\n Bucket: this.options.bucket,\n Key: `${key}/`,\n });\n await this.client.send(dirMarkerCommand);\n // Directory marker exists but is empty\n } catch {\n // Neither file nor directory marker exists\n throw new AFSNotFoundError(selfPath);\n }\n } else {\n throw error;\n }\n }\n }\n\n // Return children only (no self-entry per AFS convention)\n return { data: childEntries };\n }\n\n // ========== Versioning List ==========\n\n @List(\"/:path*/@versions\")\n async listVersionsHandler(ctx: RouteContext<{ path: string }>): Promise<AFSListResult> {\n try {\n const normalizedPath = ctx.params.path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const key = this.options.prefix ? `${this.options.prefix}/${normalizedPath}` : normalizedPath;\n\n const command = new ListObjectVersionsCommand({\n Bucket: this.options.bucket,\n Prefix: key,\n MaxKeys: 1000,\n });\n\n const response = await this.client.send(command);\n const entries: AFSEntry[] = [];\n\n if (response.Versions) {\n for (const version of response.Versions) {\n // Only include exact key matches\n if (version.Key === key && version.VersionId) {\n const versionPath = joinURL(\"/\", normalizedPath, \"@versions\", version.VersionId);\n entries.push({\n id: `${this.generateId(key)}:${version.VersionId}`,\n path: versionPath,\n updatedAt: version.LastModified,\n meta: {\n versionId: version.VersionId,\n isLatest: version.IsLatest ?? false,\n lastModified: version.LastModified?.toISOString(),\n size: version.Size ?? 0,\n etag: version.ETag?.replace(/\"/g, \"\"),\n },\n });\n }\n }\n }\n\n return { data: entries };\n } catch (error) {\n throw mapS3Error(error, `/${ctx.params.path}/@versions`);\n }\n }\n\n // ========== Read Operations ==========\n\n @Read(\"/:path*\")\n async readHandler(ctx: RouteContext<{ path: string }>): Promise<AFSEntry> {\n const normalizedOutputPath = ctx.path.startsWith(\"/\") ? ctx.path : `/${ctx.path}`;\n\n try {\n const key = this.buildS3Key(ctx.params.path);\n\n // Handle root path - return bucket metadata with childrenCount\n if (!key) {\n // Count children in root\n const listCommand = new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: this.options.prefix ? `${this.options.prefix}/` : \"\",\n Delimiter: \"/\",\n MaxKeys: 1000,\n });\n const listResponse = await this.client.send(listCommand);\n const childrenCount =\n (listResponse.CommonPrefixes?.length ?? 0) + (listResponse.Contents?.length ?? 0);\n\n return {\n id: this.generateId(\"/\"),\n path: \"/\",\n content: \"\",\n meta: {\n kind: \"afs:node\",\n childrenCount,\n platformRef: generatePlatformRef(this.options.bucket, this.options.region, \"\", true),\n },\n };\n }\n\n // Try to get the object directly (file case)\n try {\n const command = new GetObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n });\n\n const response = await this.client.send(command);\n\n // Check if this is a directory marker\n if (key.endsWith(\"/\") || response.ContentType === \"application/x-directory\") {\n // This is a directory marker - return directory info\n return {\n id: this.generateId(key),\n path: normalizedOutputPath,\n content: \"\",\n updatedAt: response.LastModified,\n meta: {\n kind: \"afs:node\",\n childrenCount: -1,\n platformRef: generatePlatformRef(this.options.bucket, this.options.region, key, true),\n },\n };\n }\n\n // Read body as string\n let content: string;\n if (response.Body) {\n const bytes = await response.Body.transformToByteArray();\n content = new TextDecoder().decode(bytes);\n } else {\n content = \"\";\n }\n\n return {\n id: this.generateId(key),\n path: normalizedOutputPath,\n content,\n updatedAt: response.LastModified,\n meta: {\n size: response.ContentLength,\n mimeType: response.ContentType,\n contentType: response.ContentType,\n contentLength: response.ContentLength,\n lastModified: response.LastModified?.toISOString(),\n etag: response.ETag?.replace(/\"/g, \"\"),\n contentRange: response.ContentRange,\n },\n };\n } catch (error: any) {\n // If 404, check if it's a directory prefix\n if (error?.name === \"NotFound\" || error?.$metadata?.httpStatusCode === 404) {\n // Check if there are objects with this prefix (it's a directory)\n const listCommand = new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: `${key}/`,\n Delimiter: \"/\",\n MaxKeys: 1000,\n });\n\n const listResponse = await this.client.send(listCommand);\n const hasChildren =\n (listResponse.Contents && listResponse.Contents.length > 0) ||\n (listResponse.CommonPrefixes && listResponse.CommonPrefixes.length > 0);\n\n if (hasChildren) {\n // It's a directory - return directory info\n const childrenCount =\n (listResponse.CommonPrefixes?.length ?? 0) + (listResponse.Contents?.length ?? 0);\n return {\n id: this.generateId(`${key}/`),\n path: normalizedOutputPath,\n content: \"\",\n meta: {\n kind: \"afs:node\",\n childrenCount,\n platformRef: generatePlatformRef(\n this.options.bucket,\n this.options.region,\n key,\n true,\n ),\n },\n };\n }\n\n // Also check for directory marker (key ending with /)\n try {\n const markerCommand = new HeadObjectCommand({\n Bucket: this.options.bucket,\n Key: `${key}/`,\n });\n await this.client.send(markerCommand);\n // Directory marker exists\n return {\n id: this.generateId(`${key}/`),\n path: normalizedOutputPath,\n content: \"\",\n meta: {\n kind: \"afs:node\",\n childrenCount: 0,\n platformRef: generatePlatformRef(\n this.options.bucket,\n this.options.region,\n key,\n true,\n ),\n },\n };\n } catch {\n // Neither file nor directory exists\n throw new AFSNotFoundError(normalizedOutputPath);\n }\n }\n throw error;\n }\n } catch (error) {\n if (error instanceof AFSNotFoundError) {\n throw error;\n }\n throw mapS3Error(error, normalizedOutputPath);\n }\n }\n\n // ========== Version Read ==========\n\n @Read(\"/:path*/@versions/:versionId\")\n async readVersionHandler(\n ctx: RouteContext<{ path: string; versionId: string }>,\n ): Promise<AFSEntry> {\n try {\n const normalizedPath = ctx.params.path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const key = this.options.prefix ? `${this.options.prefix}/${normalizedPath}` : normalizedPath;\n\n const command = new GetObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n VersionId: ctx.params.versionId,\n });\n\n const response = await this.client.send(command);\n\n const content = response.Body ? await response.Body.transformToString() : \"\";\n\n return {\n id: `${this.generateId(key)}:${ctx.params.versionId}`,\n path: ctx.path,\n content,\n updatedAt: response.LastModified,\n meta: {\n size: response.ContentLength,\n contentType: response.ContentType,\n lastModified: response.LastModified?.toISOString(),\n etag: response.ETag?.replace(/\"/g, \"\"),\n versionId: response.VersionId,\n },\n };\n } catch (error) {\n throw mapS3Error(error, `/${ctx.params.path}/@versions/${ctx.params.versionId}`);\n }\n }\n\n // ========== Meta Operations ==========\n\n @Meta(\"/\")\n @Meta(\"/:path*\")\n async metaHandler(ctx: RouteContext<{ path?: string }>): Promise<AFSEntry> {\n try {\n const path = ctx.params.path ?? \"\";\n const normalizedPath = path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const key = this.options.prefix\n ? normalizedPath\n ? `${this.options.prefix}/${normalizedPath}`\n : this.options.prefix\n : normalizedPath;\n\n // Root metadata - count children\n if (!key) {\n const listCommand = new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: this.options.prefix ? `${this.options.prefix}/` : \"\",\n Delimiter: \"/\",\n MaxKeys: 1000,\n });\n const listResponse = await this.client.send(listCommand);\n const childrenCount =\n (listResponse.CommonPrefixes?.length ?? 0) + (listResponse.Contents?.length ?? 0);\n\n return {\n id: this.generateId(\"/\"),\n path: \"/.meta\",\n meta: {\n kind: \"afs:node\",\n childrenCount,\n platformRef: generatePlatformRef(this.options.bucket, this.options.region, \"\", true),\n },\n };\n }\n\n // Check cache first\n if (this.statCache) {\n const cacheKey = createCacheKey(this.options.bucket, this.options.prefix ?? \"\", path);\n const cached = this.statCache.get(cacheKey);\n if (cached) {\n return {\n id: cached.id,\n path: ctx.path,\n meta: cached.meta,\n };\n }\n }\n\n // Try to get the object directly (file case)\n try {\n const command = new HeadObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n });\n\n const response = await this.client.send(command);\n\n // Check if this is a directory marker\n if (key.endsWith(\"/\") || response.ContentType === \"application/x-directory\") {\n return {\n id: this.generateId(key),\n path: ctx.path,\n updatedAt: response.LastModified,\n meta: {\n kind: \"afs:node\",\n childrenCount: -1,\n platformRef: generatePlatformRef(this.options.bucket, this.options.region, key, true),\n },\n };\n }\n\n const result = {\n id: this.generateId(key),\n path: ctx.path,\n updatedAt: response.LastModified,\n meta: {\n kind: \"afs:document\",\n size: response.ContentLength,\n contentType: response.ContentType,\n lastModified: response.LastModified?.toISOString(),\n etag: response.ETag?.replace(/\"/g, \"\"),\n storageClass: response.StorageClass,\n platformRef: generatePlatformRef(this.options.bucket, this.options.region, key, false),\n },\n };\n\n // Cache the result\n if (this.statCache) {\n const cacheKey = createCacheKey(this.options.bucket, this.options.prefix ?? \"\", path);\n this.statCache.set(cacheKey, result);\n }\n\n return result;\n } catch (error: any) {\n // If 404, check if it's a directory prefix\n if (error?.name === \"NotFound\" || error?.$metadata?.httpStatusCode === 404) {\n // Check if there are objects with this prefix\n const listCommand = new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: `${key}/`,\n MaxKeys: 1,\n });\n\n const listResponse = await this.client.send(listCommand);\n\n if (listResponse.Contents && listResponse.Contents.length > 0) {\n // There are objects under this prefix, so it's a directory\n return {\n id: this.generateId(`${key}/`),\n path: ctx.path,\n meta: {\n kind: \"afs:node\",\n childrenCount: -1,\n platformRef: generatePlatformRef(\n this.options.bucket,\n this.options.region,\n key,\n true,\n ),\n },\n };\n }\n\n // Also check for directory marker (key ending with /)\n try {\n const markerCommand = new HeadObjectCommand({\n Bucket: this.options.bucket,\n Key: `${key}/`,\n });\n const markerResponse = await this.client.send(markerCommand);\n\n return {\n id: this.generateId(`${key}/`),\n path: ctx.path,\n updatedAt: markerResponse.LastModified,\n meta: {\n kind: \"afs:node\",\n childrenCount: -1,\n platformRef: generatePlatformRef(\n this.options.bucket,\n this.options.region,\n key,\n true,\n ),\n },\n };\n } catch {\n // Neither file nor directory exists - ensure path has leading slash\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n throw new AFSNotFoundError(normalizedPath);\n }\n }\n\n throw error;\n }\n } catch (error) {\n if (error instanceof AFSNotFoundError) {\n throw error;\n }\n // Ensure path has leading slash for mapS3Error\n const normalizedPath = (ctx.params.path ?? \"\").startsWith(\"/\")\n ? (ctx.params.path ?? \"/\")\n : `/${ctx.params.path ?? \"\"}`;\n throw mapS3Error(error, normalizedPath);\n }\n }\n\n // ========== Stat Operations ==========\n\n @Stat(\"/\")\n @Stat(\"/:path*\")\n async statHandler(ctx: RouteContext<{ path?: string }>): Promise<AFSStatResult> {\n // Delegate to meta handler and convert to stat result\n const metaEntry = await this.metaHandler({\n ...ctx,\n path: ctx.path.endsWith(\"/.meta\") ? ctx.path : `${ctx.path}/.meta`,\n });\n\n // Extract id from path\n const pathSegments = ctx.path.split(\"/\").filter(Boolean);\n const id = pathSegments.length > 0 ? (pathSegments[pathSegments.length - 1] as string) : \"/\";\n\n return {\n data: {\n id,\n path: ctx.path,\n meta: metaEntry.meta as Record<string, unknown>,\n },\n };\n }\n\n // ========== Write Operations ==========\n\n @Write(\"/:path*\")\n async writeHandler(\n ctx: RouteContext<{ path: string }>,\n payload: AFSWriteEntryPayload,\n ): Promise<AFSWriteResult> {\n try {\n const key = this.buildS3Key(ctx.params.path);\n\n // Prepare content\n let body: Buffer;\n if (typeof payload.content === \"string\") {\n body = Buffer.from(payload.content, \"utf-8\");\n } else if (Buffer.isBuffer(payload.content)) {\n body = payload.content;\n } else if (payload.content !== undefined) {\n // JSON content\n body = Buffer.from(JSON.stringify(payload.content), \"utf-8\");\n } else {\n body = Buffer.from(\"\");\n }\n\n // Determine content type from metadata or default\n const contentType =\n (payload.meta?.mimeType as string) ??\n (payload.meta?.contentType as string) ??\n \"application/octet-stream\";\n\n let etag: string | undefined;\n let versionId: string | undefined;\n\n // Use multipart upload for large files\n if (shouldUseMultipart(body.length)) {\n const result = await multipartUpload(this.client, this.options.bucket, key, body, {\n contentType,\n });\n etag = result.etag;\n versionId = result.versionId;\n } else {\n const command = new PutObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n Body: body,\n ContentType: contentType,\n });\n\n const response = await this.client.send(command);\n etag = response.ETag?.replace(/\"/g, \"\");\n versionId = response.VersionId;\n }\n\n const normalizedOutputPath = ctx.path.startsWith(\"/\") ? ctx.path : `/${ctx.path}`;\n\n // Invalidate caches for the written path\n this.invalidateCache(ctx.params.path);\n\n return {\n data: {\n id: this.generateId(key),\n path: normalizedOutputPath,\n content: payload.content,\n meta: {\n size: body.length,\n etag,\n versionId,\n ...payload.meta,\n },\n },\n };\n } catch (error) {\n const normalizedPath = ctx.params.path.startsWith(\"/\")\n ? ctx.params.path\n : `/${ctx.params.path}`;\n throw mapS3Error(error, normalizedPath);\n }\n }\n\n // ========== Delete Operations ==========\n\n @Delete(\"/:path*\")\n async deleteHandler(ctx: RouteContext<{ path: string }>): Promise<AFSDeleteResult> {\n try {\n const key = this.buildS3Key(ctx.params.path);\n\n // Check if the object exists first (S3 DeleteObject is idempotent and doesn't error on missing objects)\n try {\n const headCommand = new HeadObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n });\n await this.client.send(headCommand);\n } catch (error: any) {\n if (error?.name === \"NotFound\" || error?.$metadata?.httpStatusCode === 404) {\n throw new AFSNotFoundError(`/${ctx.params.path}`);\n }\n throw error;\n }\n\n const command = new DeleteObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n });\n\n await this.client.send(command);\n\n // Invalidate caches for the deleted path\n this.invalidateCache(ctx.params.path);\n\n return {\n message: `Successfully deleted: ${ctx.params.path}`,\n };\n } catch (error) {\n if (error instanceof AFSNotFoundError) {\n throw error;\n }\n throw mapS3Error(error, `/${ctx.params.path}`);\n }\n }\n\n // ========== Action System ==========\n\n @Actions(\"/:path*\")\n async listActionsHandler(ctx: RouteContext<{ path: string }>): Promise<AFSListResult> {\n return {\n data: [\n {\n id: \"select\",\n path: joinURL(ctx.path, \".actions\", \"select\"),\n summary: \"Query with S3 Select\",\n meta: {\n kind: \"afs:executable\",\n kinds: [\"afs:executable\", \"afs:node\"],\n inputSchema: {\n type: \"object\",\n properties: {\n expression: { type: \"string\", description: \"SQL expression\" },\n inputFormat: { type: \"string\", description: \"CSV | JSON | Parquet\" },\n outputFormat: { type: \"string\", description: \"CSV | JSON\" },\n },\n required: [\"expression\"],\n },\n },\n },\n {\n id: \"presign-download\",\n path: joinURL(ctx.path, \".actions\", \"presign-download\"),\n summary: \"Generate download URL\",\n meta: {\n kind: \"afs:executable\",\n kinds: [\"afs:executable\", \"afs:node\"],\n inputSchema: {\n type: \"object\",\n properties: {\n expiresIn: {\n type: \"number\",\n description: \"Expiration in seconds (default: 3600, max: 604800)\",\n },\n },\n },\n },\n },\n {\n id: \"presign-upload\",\n path: joinURL(ctx.path, \".actions\", \"presign-upload\"),\n summary: \"Generate upload URL\",\n meta: {\n kind: \"afs:executable\",\n kinds: [\"afs:executable\", \"afs:node\"],\n inputSchema: {\n type: \"object\",\n properties: {\n expiresIn: { type: \"number\", description: \"Expiration in seconds\" },\n contentType: { type: \"string\", description: \"Content-Type for upload\" },\n },\n },\n },\n },\n ],\n };\n }\n\n @Actions.Exec(\"/:path*\", \"select\")\n async selectActionHandler(\n ctx: RouteContext<{ path: string }>,\n args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n const expression = args.expression as string;\n if (!expression) {\n throw new AFSError(\"Missing required argument: expression\", \"AFS_INVALID_ARGUMENT\");\n }\n\n const normalizedPath = ctx.params.path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const key = this.options.prefix ? `${this.options.prefix}/${normalizedPath}` : normalizedPath;\n\n const result = await selectQuery(this.client, this.options.bucket, \"\", key, expression, {\n inputFormat: args.inputFormat as \"CSV\" | \"JSON\" | \"Parquet\" | undefined,\n outputFormat: args.outputFormat as \"CSV\" | \"JSON\" | undefined,\n });\n\n return {\n success: true,\n data: {\n records: result.records,\n stats: result.stats,\n },\n };\n }\n\n @Actions.Exec(\"/:path*\", \"presign-download\")\n async presignDownloadActionHandler(\n ctx: RouteContext<{ path: string }>,\n args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n const normalizedPath = ctx.params.path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const key = this.options.prefix ? `${this.options.prefix}/${normalizedPath}` : normalizedPath;\n\n let expiresIn = (args.expiresIn as number) ?? DEFAULT_EXPIRES_IN;\n if (expiresIn > MAX_EXPIRES_IN) expiresIn = MAX_EXPIRES_IN;\n if (expiresIn < 1) expiresIn = 1;\n\n const command = new GetObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n });\n\n const url = await getSignedUrl(this.client, command, { expiresIn });\n const expiresAt = new Date(Date.now() + expiresIn * 1000).toISOString();\n\n return {\n success: true,\n data: {\n url,\n expiresAt,\n },\n };\n }\n\n @Actions.Exec(\"/:path*\", \"presign-upload\")\n async presignUploadActionHandler(\n ctx: RouteContext<{ path: string }>,\n args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n const normalizedPath = ctx.params.path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const key = this.options.prefix ? `${this.options.prefix}/${normalizedPath}` : normalizedPath;\n\n let expiresIn = (args.expiresIn as number) ?? DEFAULT_EXPIRES_IN;\n if (expiresIn > MAX_EXPIRES_IN) expiresIn = MAX_EXPIRES_IN;\n if (expiresIn < 1) expiresIn = 1;\n\n const command = new PutObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n ContentType: (args.contentType as string) ?? \"application/octet-stream\",\n });\n\n const url = await getSignedUrl(this.client, command, { expiresIn });\n const expiresAt = new Date(Date.now() + expiresIn * 1000).toISOString();\n\n return {\n success: true,\n data: {\n url,\n expiresAt,\n },\n };\n }\n\n // ========== Explain Operations ==========\n\n @Explain(\"/\")\n @Explain(\"/:path*\")\n async explainHandler(ctx: RouteContext<{ path?: string }>): Promise<AFSExplainResult> {\n const normalizedPath = (ctx.params.path ?? \"\").replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n\n // Root explain\n if (!normalizedPath) {\n const response = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: this.options.prefix ? `${this.options.prefix}/` : undefined,\n Delimiter: \"/\",\n MaxKeys: 1000,\n }),\n );\n\n const objectCount = response.Contents?.length ?? 0;\n const prefixCount = response.CommonPrefixes?.length ?? 0;\n\n const lines: string[] = [];\n lines.push(`# ${this.options.bucket}`);\n lines.push(\"\");\n lines.push(`- **Type**: S3 Bucket`);\n lines.push(`- **Bucket**: ${this.options.bucket}`);\n if (this.options.prefix) {\n lines.push(`- **Prefix**: ${this.options.prefix}`);\n }\n if (this.options.region) {\n lines.push(`- **Region**: ${this.options.region}`);\n }\n lines.push(`- **Access Mode**: ${this.accessMode}`);\n lines.push(`- **Top-level Objects**: ${objectCount}`);\n lines.push(`- **Top-level Prefixes**: ${prefixCount}`);\n\n return { format: \"markdown\", content: lines.join(\"\\n\") };\n }\n\n const key = this.buildS3Key(normalizedPath);\n\n // Try as object first (HeadObject)\n try {\n const head = await this.client.send(\n new HeadObjectCommand({ Bucket: this.options.bucket, Key: key }),\n );\n\n const lines: string[] = [];\n lines.push(`# ${normalizedPath}`);\n lines.push(\"\");\n lines.push(`- **Type**: Object`);\n lines.push(`- **Key**: ${key}`);\n lines.push(`- **Size**: ${head.ContentLength ?? 0} bytes`);\n if (head.ContentType) lines.push(`- **Content-Type**: ${head.ContentType}`);\n if (head.StorageClass) lines.push(`- **Storage Class**: ${head.StorageClass}`);\n if (head.LastModified) lines.push(`- **Last Modified**: ${head.LastModified.toISOString()}`);\n if (head.ETag) lines.push(`- **ETag**: ${head.ETag}`);\n\n return { format: \"markdown\", content: lines.join(\"\\n\") };\n } catch {\n // Not an object, try as prefix (directory)\n }\n\n // Try as prefix\n const prefix = key.endsWith(\"/\") ? key : `${key}/`;\n const response = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: prefix,\n Delimiter: \"/\",\n MaxKeys: 1000,\n }),\n );\n\n const objectCount = response.Contents?.length ?? 0;\n const prefixCount = response.CommonPrefixes?.length ?? 0;\n\n if (objectCount === 0 && prefixCount === 0) {\n throw new AFSNotFoundError(`/${normalizedPath}`, `Path not found: ${normalizedPath}`);\n }\n\n const lines: string[] = [];\n lines.push(`# ${normalizedPath}/`);\n lines.push(\"\");\n lines.push(`- **Type**: Prefix (directory)`);\n lines.push(`- **Prefix**: ${prefix}`);\n lines.push(`- **Objects**: ${objectCount}`);\n lines.push(`- **Sub-prefixes**: ${prefixCount}`);\n\n return { format: \"markdown\", content: lines.join(\"\\n\") };\n }\n\n // ========== Search Operations ==========\n\n @Search(\"/\")\n @Search(\"/:path*\")\n async searchHandler(\n ctx: RouteContext<{ path?: string }>,\n query: string,\n ): Promise<AFSSearchResult> {\n const { minimatch } = await import(\"minimatch\");\n const path = ctx.params.path ?? \"\";\n const normalizedPath = path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const prefix = normalizedPath\n ? this.options.prefix\n ? `${this.options.prefix}/${normalizedPath}/`\n : `${normalizedPath}/`\n : this.options.prefix\n ? `${this.options.prefix}/`\n : \"\";\n\n const results: AFSEntry[] = [];\n let continuationToken: string | undefined;\n\n // Paginate through all objects under the prefix\n do {\n const response = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: prefix || undefined,\n ContinuationToken: continuationToken,\n MaxKeys: 1000,\n }),\n );\n\n for (const obj of response.Contents ?? []) {\n if (!obj.Key) continue;\n\n // Get relative name (strip prefix)\n const relativePath = prefix ? obj.Key.slice(prefix.length) : obj.Key;\n if (!relativePath) continue;\n\n // Match against glob pattern\n if (minimatch(relativePath, query)) {\n const displayPath = joinURL(\"/\", normalizedPath, relativePath);\n results.push({\n id: this.generateId(obj.Key),\n path: displayPath,\n meta: {\n size: obj.Size,\n lastModified: obj.LastModified?.toISOString(),\n etag: obj.ETag?.replace(/\"/g, \"\"),\n },\n });\n }\n }\n\n continuationToken = response.IsTruncated ? response.NextContinuationToken : undefined;\n } while (continuationToken);\n\n return { data: results };\n }\n\n // ========== Capabilities ==========\n\n @Read(\"/.meta/.capabilities\")\n async readCapabilities(_ctx: RouteContext): Promise<AFSEntry> {\n const capabilities: CapabilitiesManifest = {\n schemaVersion: 1,\n provider: \"s3\",\n description: `S3 bucket: ${this.options.bucket}`,\n tools: [],\n operations: this.getOperationsDeclaration(),\n actions: [\n {\n description: \"S3 object actions\",\n catalog: [\n {\n name: \"select\",\n description: \"Query object contents using S3 Select (CSV/JSON/Parquet)\",\n inputSchema: {\n type: \"object\",\n properties: {\n expression: { type: \"string\", description: \"SQL expression\" },\n inputFormat: { type: \"string\", description: \"CSV | JSON | Parquet\" },\n outputFormat: { type: \"string\", description: \"CSV | JSON\" },\n },\n required: [\"expression\"],\n },\n },\n {\n name: \"presign-download\",\n description: \"Generate a pre-signed download URL\",\n inputSchema: {\n type: \"object\",\n properties: {\n expiresIn: {\n type: \"number\",\n description: \"Expiration in seconds (default: 3600, max: 604800)\",\n },\n },\n },\n },\n {\n name: \"presign-upload\",\n description: \"Generate a pre-signed upload URL\",\n inputSchema: {\n type: \"object\",\n properties: {\n expiresIn: { type: \"number\", description: \"Expiration in seconds\" },\n contentType: { type: \"string\", description: \"Content-Type for upload\" },\n },\n },\n },\n ],\n discovery: { pathTemplate: \"/{path}/.actions\" },\n },\n ],\n };\n\n return {\n id: \".capabilities\",\n path: \"/.meta/.capabilities\",\n content: JSON.stringify(capabilities, null, 2),\n meta: { kind: \"afs:capabilities\" },\n };\n }\n}\n\n// Type check: AFSS3 should implement AFSModuleClass\nconst _typeCheck: AFSModuleClass<AFSS3, AFSS3Options> = AFSS3;\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aigne/afs-s3",
|
|
3
|
+
"version": "1.11.0-beta.10",
|
|
4
|
+
"description": "AIGNE AFS module for AWS S3 and S3-compatible storage",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"author": "Arcblock <blocklet@arcblock.io> https://github.com/arcblock",
|
|
10
|
+
"homepage": "https://github.com/arcblock/afs",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/arcblock/afs"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/arcblock/afs/issues"
|
|
17
|
+
},
|
|
18
|
+
"type": "module",
|
|
19
|
+
"main": "./dist/index.cjs",
|
|
20
|
+
"module": "./dist/index.mjs",
|
|
21
|
+
"types": "./dist/index.d.cts",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"require": "./dist/index.cjs",
|
|
25
|
+
"import": "./dist/index.mjs"
|
|
26
|
+
},
|
|
27
|
+
"./*": "./*"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"LICENSE",
|
|
32
|
+
"README.md",
|
|
33
|
+
"CHANGELOG.md"
|
|
34
|
+
],
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@aws-sdk/client-s3": "^3.821.0",
|
|
37
|
+
"@aws-sdk/s3-request-presigner": "^3.975.0",
|
|
38
|
+
"minimatch": "^10.1.1",
|
|
39
|
+
"tslib": "^2.8.1",
|
|
40
|
+
"ufo": "^1.6.3",
|
|
41
|
+
"zod": "^4.0.0",
|
|
42
|
+
"@aigne/afs": "^1.11.0-beta.10"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/bun": "^1.3.6",
|
|
46
|
+
"npm-run-all": "^4.1.5",
|
|
47
|
+
"rimraf": "^6.1.2",
|
|
48
|
+
"tsdown": "0.20.0-beta.3",
|
|
49
|
+
"typescript": "5.9.2",
|
|
50
|
+
"@aigne/afs-testing": "1.11.0-beta.10",
|
|
51
|
+
"@aigne/scripts": "0.0.0",
|
|
52
|
+
"@aigne/typescript-config": "0.0.0"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsdown",
|
|
56
|
+
"check-types": "tsc --noEmit",
|
|
57
|
+
"clean": "rimraf dist coverage",
|
|
58
|
+
"test": "bun test",
|
|
59
|
+
"test:coverage": "bun test --coverage --coverage-reporter=lcov --coverage-reporter=text",
|
|
60
|
+
"test-env:start": "docker compose up -d",
|
|
61
|
+
"test-env:stop": "docker compose down"
|
|
62
|
+
}
|
|
63
|
+
}
|