@arke-institute/sdk 3.6.3 → 3.6.6

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/operations/index.ts","../../src/client/errors.ts","../../src/operations/upload/cid.ts","../../src/operations/upload/engine.ts","../../src/operations/upload/scanners.ts","../../src/operations/upload/single.ts","../../src/operations/folders.ts","../../src/operations/batch.ts","../../src/operations/crypto.ts","../../src/operations/cas.ts"],"sourcesContent":["/**\n * High-level operations built on top of the generated API client\n *\n * These provide convenience methods for common multi-step workflows.\n */\n\n// Upload operations (primary folder/file upload functionality)\nexport {\n // Tree uploads\n uploadTree,\n computeCid,\n verifyCid,\n scanFileSystemEntries,\n scanFileList,\n buildUploadTree,\n getMimeType,\n type UploadFile,\n type UploadFolder,\n type UploadTree,\n type UploadTarget,\n type UploadProgress,\n type UploadOptions,\n type UploadResult,\n type CreatedEntity,\n // Single entity uploads\n uploadToEntity,\n type UploadItem,\n type UploadToEntityProgress,\n type UploadToEntityOptions,\n type UploadToEntityResult,\n type UploadContentResult,\n} from './upload/index.js';\n\n// Legacy FolderOperations class (deprecated - use uploadTree instead)\nexport { FolderOperations, type UploadDirectoryOptions, type UploadDirectoryResult } from './folders.js';\n\n// Batch operations (placeholder)\nexport { BatchOperations, type BatchCreateOptions, type BatchResult } from './batch.js';\n\n// Crypto operations (placeholder)\nexport { CryptoOperations, type KeyPair, type SignedPayload } from './crypto.js';\n\n// CAS retry utility\nexport {\n withCasRetry,\n calculateMaxAttempts,\n calculateCasDelay,\n isCasConflictError,\n CasRetryExhaustedError,\n DEFAULT_CAS_RETRY_CONFIG,\n type CasRetryOptions,\n type CasRetryResult,\n type CasRetryCallbacks,\n} from './cas.js';\n","/**\n * SDK error classes\n */\n\n/**\n * Base error class for all Arke SDK errors\n */\nexport class ArkeError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly status?: number,\n public readonly details?: unknown\n ) {\n super(message);\n this.name = 'ArkeError';\n // Maintains proper stack trace for where error was thrown\n Error.captureStackTrace?.(this, this.constructor);\n }\n\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n status: this.status,\n details: this.details,\n };\n }\n}\n\n/**\n * CAS (Compare-And-Swap) conflict - entity was modified by another request\n */\nexport class CASConflictError extends ArkeError {\n constructor(\n public readonly expectedTip?: string,\n public readonly actualTip?: string\n ) {\n super(\n 'Entity was modified by another request. Refresh and retry with the current tip.',\n 'CAS_CONFLICT',\n 409,\n { expectedTip, actualTip }\n );\n this.name = 'CASConflictError';\n }\n}\n\n/**\n * Resource not found\n */\nexport class NotFoundError extends ArkeError {\n constructor(resourceType: string, id: string) {\n super(`${resourceType} not found: ${id}`, 'NOT_FOUND', 404, { resourceType, id });\n this.name = 'NotFoundError';\n }\n}\n\n/**\n * Validation error - invalid request data\n */\nexport class ValidationError extends ArkeError {\n constructor(\n message: string,\n public readonly field?: string,\n details?: unknown\n ) {\n super(message, 'VALIDATION_ERROR', 400, details ?? { field });\n this.name = 'ValidationError';\n }\n}\n\n/**\n * Authentication required or invalid\n */\nexport class AuthenticationError extends ArkeError {\n constructor(message = 'Authentication required') {\n super(message, 'AUTH_REQUIRED', 401);\n this.name = 'AuthenticationError';\n }\n}\n\n/**\n * Permission denied\n */\nexport class ForbiddenError extends ArkeError {\n constructor(action?: string, resource?: string) {\n const msg = action\n ? `Permission denied: ${action}${resource ? ` on ${resource}` : ''}`\n : 'Permission denied';\n super(msg, 'FORBIDDEN', 403, { action, resource });\n this.name = 'ForbiddenError';\n }\n}\n\n/**\n * Parse API error response into appropriate error class\n */\nexport function parseApiError(status: number, body: unknown): ArkeError {\n const errorBody = body as { error?: string; message?: string; details?: unknown } | null;\n const message = errorBody?.error ?? errorBody?.message ?? 'Unknown error';\n\n switch (status) {\n case 400:\n return new ValidationError(message, undefined, errorBody?.details);\n\n case 401:\n return new AuthenticationError(message);\n\n case 403:\n return new ForbiddenError(message);\n\n case 404:\n return new NotFoundError('Resource', 'unknown');\n\n case 409: {\n // Parse CAS conflict details if available\n const details = errorBody?.details as { expected?: string; actual?: string } | undefined;\n return new CASConflictError(details?.expected, details?.actual);\n }\n\n default:\n return new ArkeError(message, 'API_ERROR', status, errorBody?.details);\n }\n}\n","/**\n * CID Computation Utility\n *\n * Computes IPFS CIDv1 (base32) for file content.\n * Uses raw codec (0x55) and SHA-256 hash.\n *\n * Note: This module is not used internally by the upload engine (the server\n * computes CIDs from uploaded content). It is exported for convenience in case\n * you want to verify entity CIDs, detect duplicates before upload, or perform\n * other content-addressed operations.\n */\n\nimport { CID } from 'multiformats/cid';\nimport { sha256 } from 'multiformats/hashes/sha2';\nimport * as raw from 'multiformats/codecs/raw';\n\n/**\n * Compute CIDv1 for binary content.\n * Returns base32 encoded string (bafk... prefix for raw codec).\n *\n * @param data - Binary content as ArrayBuffer, Uint8Array, or Blob\n * @returns CIDv1 string in base32 encoding\n *\n * @example\n * ```typescript\n * const cid = await computeCid(new TextEncoder().encode('hello world'));\n * // Returns: \"bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e\"\n * ```\n */\nexport async function computeCid(data: ArrayBuffer | Uint8Array | Blob): Promise<string> {\n // Convert to Uint8Array\n let bytes: Uint8Array;\n\n if (data instanceof Blob) {\n const buffer = await data.arrayBuffer();\n bytes = new Uint8Array(buffer);\n } else if (data instanceof ArrayBuffer) {\n bytes = new Uint8Array(data);\n } else {\n bytes = data;\n }\n\n // Compute SHA-256 hash\n const hash = await sha256.digest(bytes);\n\n // Create CIDv1 with raw codec\n const cid = CID.create(1, raw.code, hash);\n\n // Return base32 encoded string\n return cid.toString();\n}\n\n/**\n * Verify a CID matches the content.\n *\n * @param data - Binary content\n * @param expectedCid - CID to verify against\n * @returns true if CID matches\n */\nexport async function verifyCid(\n data: ArrayBuffer | Uint8Array | Blob,\n expectedCid: string\n): Promise<boolean> {\n const computed = await computeCid(data);\n return computed === expectedCid;\n}\n","/**\n * Upload Engine\n *\n * Core upload implementation optimized for fast tree visibility:\n * 1. Create folders by depth (with unidirectional 'in' → parent)\n * 2. Create file entities (metadata only, high parallelism)\n * 3. Backlink parents with 'contains' relationships\n * → Tree is now browsable! Users can explore structure immediately.\n * 4. Upload file content via POST /files/{id}/content (byte-based pool, ~200MB in flight)\n * - Direct upload to API endpoint\n * - API streams to R2, computes CID, updates entity atomically\n *\n * Byte-based pool (for uploads):\n * - Maintains ~200MB of data in flight at all times\n * - When a file completes, next file starts immediately (no gaps)\n * - Small files: Many upload in parallel\n * - Large files: Fewer upload in parallel (bandwidth-limited)\n * - Single file > 200MB: Uploads alone when pool is empty\n *\n * This minimizes time-to-browse by:\n * - Creating all entities before uploading content\n * - Using unidirectional 'in' relationship on entity creation\n * - Adding all 'contains' relationships in a single PUT per parent\n */\n\nimport type { ArkeClient } from '../../client/ArkeClient.js';\nimport { getAuthorizationHeader } from '../../client/ArkeClient.js';\nimport { ForbiddenError } from '../../client/errors.js';\nimport type { components } from '../../generated/types.js';\nimport { computeCid } from './cid.js';\nimport type {\n UploadTree,\n UploadOptions,\n UploadResult,\n UploadProgress,\n CreatedFolder,\n CreatedFile,\n CreatedEntity,\n UploadFolder,\n} from './types.js';\n\ntype CreateCollectionRequest = components['schemas']['CreateCollectionRequest'];\ntype CreateEntityRequest = components['schemas']['CreateEntityRequest'];\ntype UpdateEntityRequest = components['schemas']['UpdateEntityRequest'];\ntype UpdateCollectionRequest = components['schemas']['UpdateCollectionRequest'];\n\n// Phase constants\nconst PHASE_COUNT = 3; // creating, backlinking, uploading (excluding complete/error)\nconst PHASE_INDEX: Record<string, number> = {\n creating: 0,\n backlinking: 1,\n uploading: 2,\n complete: 3,\n error: -1,\n};\n\n// Batch creation constants\nconst BATCH_SIZE = 100; // Entities per batch request\nconst BATCH_CONCURRENCY = 25; // Concurrent batch requests\nconst BACKLINK_CONCURRENCY = 100; // Concurrent backlink PUTs\nconst MAX_UPLOAD_CONCURRENCY = 500; // Max concurrent upload requests\n\n// =============================================================================\n// Concurrency Utilities\n// =============================================================================\n\n/**\n * Simple concurrency limiter for parallel operations.\n */\nasync function parallelLimit<T, R>(\n items: T[],\n concurrency: number,\n fn: (item: T, index: number) => Promise<R>\n): Promise<R[]> {\n const results: R[] = [];\n let index = 0;\n\n async function worker(): Promise<void> {\n while (index < items.length) {\n const currentIndex = index++;\n const item = items[currentIndex]!;\n results[currentIndex] = await fn(item, currentIndex);\n }\n }\n\n const workers = Array.from({ length: Math.min(concurrency, items.length) }, () => worker());\n await Promise.all(workers);\n\n return results;\n}\n\n/**\n * Split array into chunks of specified size.\n */\nfunction chunk<T>(array: T[], size: number): T[][] {\n const chunks: T[][] = [];\n for (let i = 0; i < array.length; i += size) {\n chunks.push(array.slice(i, i + size));\n }\n return chunks;\n}\n\n// =============================================================================\n// Byte-Based Pool\n// =============================================================================\n\n/** Target bytes in flight (~200MB) */\nconst TARGET_BYTES_IN_FLIGHT = 200 * 1024 * 1024;\n\n/** Threshold for using presigned URLs (5MB) - larger files bypass API worker */\nconst PRESIGNED_URL_THRESHOLD = 5 * 1024 * 1024;\n\n/**\n * Pool that maintains a target number of bytes in flight AND limits concurrent requests.\n *\n * When a file completes, its bytes are released and the next\n * waiting file can start immediately. This keeps the pipeline full.\n *\n * - Small files: Many run in parallel (up to ~200MB worth or maxConcurrent)\n * - Large files: Fewer run in parallel\n * - Single file > 200MB: Runs alone (allowed when pool is empty)\n */\nclass BytePool {\n private bytesInFlight = 0;\n private activeCount = 0;\n private waitQueue: Array<() => void> = [];\n\n constructor(\n private targetBytes: number = TARGET_BYTES_IN_FLIGHT,\n private maxConcurrent: number = MAX_UPLOAD_CONCURRENCY\n ) {}\n\n async run<T>(size: number, fn: () => Promise<T>): Promise<T> {\n // Wait until we have room for BOTH bytes AND request count\n // Exception: if pool is empty, always allow (handles files > targetBytes)\n while (\n (this.bytesInFlight > 0 && this.bytesInFlight + size > this.targetBytes) ||\n this.activeCount >= this.maxConcurrent\n ) {\n await new Promise<void>((resolve) => this.waitQueue.push(resolve));\n }\n\n this.bytesInFlight += size;\n this.activeCount++;\n try {\n return await fn();\n } finally {\n this.bytesInFlight -= size;\n this.activeCount--;\n // Wake ALL waiting tasks so they can re-evaluate the condition.\n // wake-one is incorrect here: if a large file finishes, multiple\n // smaller files may now fit but only the first waiter would check.\n const queue = this.waitQueue.splice(0);\n for (const resolve of queue) resolve();\n }\n }\n}\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Parse folder path to get parent path.\n * e.g., \"docs/images/photos\" -> \"docs/images\"\n */\nfunction getParentPath(relativePath: string): string | null {\n const lastSlash = relativePath.lastIndexOf('/');\n if (lastSlash === -1) return null;\n return relativePath.slice(0, lastSlash);\n}\n\n/**\n * Group folders by depth level.\n */\nfunction groupFoldersByDepth(folders: UploadFolder[]): Map<number, UploadFolder[]> {\n const byDepth = new Map<number, UploadFolder[]>();\n\n for (const folder of folders) {\n const depth = folder.relativePath.split('/').length - 1;\n if (!byDepth.has(depth)) byDepth.set(depth, []);\n byDepth.get(depth)!.push(folder);\n }\n\n return byDepth;\n}\n\n// =============================================================================\n// Main Upload Function\n// =============================================================================\n\n/**\n * Main upload function.\n * Orchestrates the entire upload process with optimized relationship strategy.\n */\nexport async function uploadTree(\n client: ArkeClient,\n tree: UploadTree,\n options: UploadOptions\n): Promise<UploadResult> {\n const { target, onProgress, concurrency = 10, continueOnError = false, maxBytesInFlight, note } = options;\n\n const errors: Array<{ path: string; error: string }> = [];\n const createdFolders: CreatedFolder[] = [];\n const createdFiles: CreatedFile[] = [];\n\n // Maps for tracking (include label for peer_label in relationships)\n const foldersByPath = new Map<string, { id: string; cid: string; label: string }>();\n\n // Calculate totals\n const totalEntities = tree.files.length + tree.folders.length;\n const totalBytes = tree.files.reduce((sum, f) => sum + f.size, 0);\n let completedEntities = 0;\n let bytesUploaded = 0;\n\n // Helper to report progress\n const reportProgress = (progress: Partial<UploadProgress>) => {\n if (onProgress) {\n const phase = progress.phase || 'creating';\n const phaseIndex = PHASE_INDEX[phase] ?? -1;\n\n // Calculate phase percent based on current phase\n let phasePercent = 0;\n if (phase === 'creating') {\n // Creating phase: progress is based on entities created\n const done = progress.completedEntities ?? completedEntities;\n phasePercent = totalEntities > 0 ? Math.round((done / totalEntities) * 100) : 100;\n } else if (phase === 'backlinking') {\n // Backlinking phase: progress is based on parents updated\n const done = progress.completedParents ?? 0;\n const total = progress.totalParents ?? 0;\n phasePercent = total > 0 ? Math.round((done / total) * 100) : 100;\n } else if (phase === 'uploading') {\n // Uploading phase: progress is based on bytes uploaded\n const done = progress.bytesUploaded ?? bytesUploaded;\n phasePercent = totalBytes > 0 ? Math.round((done / totalBytes) * 100) : 100;\n } else if (phase === 'complete') {\n phasePercent = 100;\n }\n\n onProgress({\n phase,\n phaseIndex,\n phaseCount: PHASE_COUNT,\n phasePercent,\n totalEntities,\n completedEntities,\n totalParents: 0,\n completedParents: 0,\n totalBytes,\n bytesUploaded,\n ...progress,\n } as UploadProgress);\n }\n };\n\n try {\n // ─────────────────────────────────────────────────────────────────────────\n // SETUP: Resolve or create collection\n // ─────────────────────────────────────────────────────────────────────────\n let collectionId: string;\n let collectionCid: string;\n let collectionLabel: string;\n let collectionCreated = false;\n\n if (target.createCollection) {\n const collectionBody: CreateCollectionRequest = {\n label: target.createCollection.label,\n description: target.createCollection.description,\n roles: target.createCollection.roles,\n note,\n };\n\n const { data, error } = await client.api.POST('/collections', {\n body: collectionBody,\n });\n\n if (error || !data) {\n throw new Error(`Failed to create collection: ${JSON.stringify(error)}`);\n }\n\n collectionId = data.id;\n collectionCid = data.cid;\n collectionLabel = target.createCollection.label;\n collectionCreated = true;\n } else if (target.collectionId) {\n collectionId = target.collectionId;\n\n const { data, error } = await client.api.GET('/collections/{id}', {\n params: { path: { id: collectionId } },\n });\n\n if (error || !data) {\n throw new Error(`Failed to fetch collection: ${JSON.stringify(error)}`);\n }\n\n collectionCid = data.cid;\n collectionLabel = (data.properties?.label as string) ?? collectionId;\n } else {\n throw new Error('Must provide either collectionId or createCollection in target');\n }\n\n // Determine the parent for root-level items\n const rootParentId = target.parentId ?? collectionId;\n let rootParentLabel = collectionLabel;\n let rootParentType: 'collection' | 'folder' = 'collection';\n\n // If a specific parent folder is provided, fetch its label\n if (target.parentId && target.parentId !== collectionId) {\n const { data: parentData, error: parentError } = await client.api.GET('/entities/{id}', {\n params: { path: { id: target.parentId } },\n });\n if (parentError || !parentData) {\n throw new Error(`Failed to fetch parent folder: ${JSON.stringify(parentError)}`);\n }\n rootParentLabel = (parentData.properties?.label as string) ?? target.parentId;\n rootParentType = 'folder';\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // PRE-FLIGHT PERMISSION CHECK\n // Verify we have the required permissions before starting the upload.\n // This fails fast with a clear error instead of failing mid-upload.\n // ─────────────────────────────────────────────────────────────────────────\n\n // Only check permissions if we didn't just create the collection (we'd have all permissions)\n if (!collectionCreated) {\n // Check entity:create permission on the collection\n const { data: collPerms, error: collPermsError } = await client.api.GET(\n '/entities/{id}/permissions',\n { params: { path: { id: collectionId } } }\n );\n\n if (collPermsError || !collPerms) {\n throw new Error(`Failed to check collection permissions: ${JSON.stringify(collPermsError)}`);\n }\n\n if (!collPerms.allowed_actions.includes('entity:create')) {\n throw new ForbiddenError('entity:create', `collection ${collectionId}`);\n }\n\n // If a parent folder is specified (not the collection), check entity:update permission\n // This is required for backlinking (adding 'contains' relationships to the parent)\n if (target.parentId && target.parentId !== collectionId) {\n const { data: parentPerms, error: parentPermsError } = await client.api.GET(\n '/entities/{id}/permissions',\n { params: { path: { id: target.parentId } } }\n );\n\n if (parentPermsError || !parentPerms) {\n throw new Error(`Failed to check parent folder permissions: ${JSON.stringify(parentPermsError)}`);\n }\n\n if (!parentPerms.allowed_actions.includes('entity:update')) {\n throw new ForbiddenError('entity:update', `parent folder ${target.parentId}`);\n }\n }\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // PHASE 1: Create entities (folders by depth, then files)\n // ─────────────────────────────────────────────────────────────────────────\n reportProgress({ phase: 'creating', completedEntities: 0 });\n\n // Group folders by depth\n const foldersByDepth = groupFoldersByDepth(tree.folders);\n const sortedDepths = [...foldersByDepth.keys()].sort((a, b) => a - b);\n\n // Create folders depth by depth (parents before children) using batch endpoint\n for (const depth of sortedDepths) {\n const foldersAtDepth = foldersByDepth.get(depth)!;\n\n // Build batch items for all folders at this depth\n const batchItems = foldersAtDepth.map((folder) => {\n const parentPath = getParentPath(folder.relativePath);\n const parentInfo = parentPath ? foldersByPath.get(parentPath)! : null;\n const parentId = parentInfo ? parentInfo.id : rootParentId;\n const parentType = parentInfo ? 'folder' : rootParentType;\n const parentLabel = parentInfo ? parentInfo.label : rootParentLabel;\n\n return {\n folder, // Track for result processing\n entity: {\n type: 'folder',\n properties: { label: folder.name },\n note,\n relationships: [{ predicate: 'in', peer: parentId, peer_type: parentType, peer_label: parentLabel }],\n },\n };\n });\n\n // Chunk into batches of BATCH_SIZE and create in parallel\n const batches = chunk(batchItems, BATCH_SIZE);\n\n await parallelLimit(batches, BATCH_CONCURRENCY, async (batch) => {\n const { data, error } = await client.api.POST('/entities/batch', {\n params: { query: { validate_relationships: 'false' } },\n body: {\n default_collection: collectionId,\n entities: batch.map((item) => item.entity),\n },\n });\n\n if (error || !data) {\n throw new Error(`Batch folder creation failed: ${JSON.stringify(error)}`);\n }\n\n // Process results - map back to folders by index\n for (const result of data.results) {\n const batchItem = batch[result.index];\n if (!batchItem) continue;\n const folder = batchItem.folder;\n\n if (result.success) {\n // Track folder (include label for peer_label in child relationships)\n foldersByPath.set(folder.relativePath, {\n id: result.id,\n cid: result.cid,\n label: folder.name,\n });\n createdFolders.push({\n name: folder.name,\n relativePath: folder.relativePath,\n id: result.id,\n entityCid: result.cid,\n });\n completedEntities++;\n } else {\n // BatchCreateFailure\n const errorMsg = result.error;\n if (continueOnError) {\n errors.push({ path: folder.relativePath, error: `Folder creation failed: ${errorMsg}` });\n completedEntities++;\n } else {\n throw new Error(`Failed to create folder ${folder.relativePath}: ${errorMsg}`);\n }\n }\n }\n\n reportProgress({ phase: 'creating', completedEntities });\n });\n }\n\n // Create file entities (metadata only, no content upload yet) using batch endpoint\n // Build batch items for all files\n const fileBatchItems = tree.files.map((file) => {\n const parentPath = getParentPath(file.relativePath);\n const parentInfo = parentPath ? foldersByPath.get(parentPath)! : null;\n const parentId = parentInfo ? parentInfo.id : rootParentId;\n const parentType = parentInfo ? 'folder' : rootParentType;\n const parentLabel = parentInfo ? parentInfo.label : rootParentLabel;\n\n return {\n file, // Track for result processing\n entity: {\n type: 'file',\n properties: {\n label: file.name,\n filename: file.name,\n content_type: file.mimeType,\n size: file.size,\n },\n note,\n relationships: [{ predicate: 'in', peer: parentId, peer_type: parentType, peer_label: parentLabel }],\n },\n };\n });\n\n // Chunk into batches of BATCH_SIZE and create in parallel\n const fileBatches = chunk(fileBatchItems, BATCH_SIZE);\n\n await parallelLimit(fileBatches, BATCH_CONCURRENCY, async (batch) => {\n const { data, error } = await client.api.POST('/entities/batch', {\n params: { query: { validate_relationships: 'false' } },\n body: {\n default_collection: collectionId,\n entities: batch.map((item) => item.entity),\n },\n });\n\n if (error || !data) {\n throw new Error(`Batch file creation failed: ${JSON.stringify(error)}`);\n }\n\n // Process results - map back to files by index\n for (const result of data.results) {\n const batchItem = batch[result.index];\n if (!batchItem) continue;\n const file = batchItem.file;\n\n if (result.success) {\n // Track file for later upload\n createdFiles.push({\n ...file,\n id: result.id,\n entityCid: result.cid,\n });\n completedEntities++;\n } else {\n // BatchCreateFailure\n const errorMsg = result.error;\n if (continueOnError) {\n errors.push({ path: file.relativePath, error: errorMsg });\n completedEntities++;\n } else {\n throw new Error(`Failed to create file ${file.relativePath}: ${errorMsg}`);\n }\n }\n }\n\n reportProgress({ phase: 'creating', completedEntities });\n });\n\n // ─────────────────────────────────────────────────────────────────────────\n // PHASE 2: Backlink - Update each parent with 'contains' relationships\n // Uses cached CIDs to avoid GETs, with retry on 409 conflict\n // ─────────────────────────────────────────────────────────────────────────\n\n // Build parent -> children map (include label for peer_label)\n const childrenByParent = new Map<string, Array<{ id: string; type: 'file' | 'folder'; label: string }>>();\n\n // Add folders as children of their parents\n for (const folder of createdFolders) {\n const parentPath = getParentPath(folder.relativePath);\n const parentId = parentPath ? foldersByPath.get(parentPath)!.id : rootParentId;\n\n if (!childrenByParent.has(parentId)) childrenByParent.set(parentId, []);\n childrenByParent.get(parentId)!.push({ id: folder.id, type: 'folder', label: folder.name });\n }\n\n // Add files as children of their parents\n for (const file of createdFiles) {\n const parentPath = getParentPath(file.relativePath);\n const parentId = parentPath ? foldersByPath.get(parentPath)!.id : rootParentId;\n\n if (!childrenByParent.has(parentId)) childrenByParent.set(parentId, []);\n childrenByParent.get(parentId)!.push({ id: file.id, type: 'file', label: file.name });\n }\n\n const totalParents = childrenByParent.size;\n let completedParents = 0;\n\n reportProgress({ phase: 'backlinking', totalParents, completedParents: 0 });\n\n // Update all parents in parallel - each parent gets one PUT with all its children\n // Use cached CIDs from creation phase, only fetch fresh on 409 conflict\n const parentEntries = [...childrenByParent.entries()];\n\n await parallelLimit(parentEntries, BACKLINK_CONCURRENCY, async ([parentId, children]) => {\n try {\n const isCollection = parentId === collectionId;\n\n // Build relationships_add array with all children (include peer_label for display)\n const relationshipsAdd = children.map((child) => ({\n predicate: 'contains' as const,\n peer: child.id,\n peer_type: child.type,\n peer_label: child.label,\n }));\n\n // Get cached CID - no GET required for entities we created\n let expectTip: string;\n if (isCollection) {\n expectTip = collectionCid; // From setup phase\n } else {\n // Check if it's a folder we created (have cached CID)\n const folderInfo = [...foldersByPath.values()].find((f) => f.id === parentId);\n if (folderInfo) {\n expectTip = folderInfo.cid;\n } else {\n // Root parent provided by user - need to fetch tip\n const { data: tipData, error: tipError } = await client.api.GET('/entities/{id}/tip', {\n params: { path: { id: parentId } },\n });\n if (tipError || !tipData) {\n throw new Error(`Failed to get tip: ${JSON.stringify(tipError)}`);\n }\n expectTip = tipData.cid;\n }\n }\n\n // Attempt PUT with cached CID, retry once on 409 conflict\n // Skip relationship validation - we just created these entities\n const attemptPut = async (tip: string, isRetry: boolean): Promise<void> => {\n if (isCollection) {\n const { error, response } = await client.api.PUT('/collections/{id}', {\n params: { path: { id: parentId }, query: { validate_relationships: 'false' } },\n body: {\n expect_tip: tip,\n relationships_add: relationshipsAdd,\n note: note ? `${note} (backlink${isRetry ? ' retry' : ''})` : `Upload backlink${isRetry ? ' retry' : ''}`,\n },\n });\n\n if (error) {\n // Check for CAS conflict (409) - retry with fresh tip\n if (response?.status === 409 && !isRetry) {\n const { data: freshData } = await client.api.GET('/collections/{id}', {\n params: { path: { id: parentId } },\n });\n if (!freshData) throw new Error('Failed to get fresh collection tip');\n return attemptPut(freshData.cid, true);\n }\n throw new Error(JSON.stringify(error));\n }\n } else {\n const { error, response } = await client.api.PUT('/entities/{id}', {\n params: { path: { id: parentId }, query: { validate_relationships: 'false' } },\n body: {\n expect_tip: tip,\n relationships_add: relationshipsAdd,\n note: note ? `${note} (backlink${isRetry ? ' retry' : ''})` : `Upload backlink${isRetry ? ' retry' : ''}`,\n },\n });\n\n if (error) {\n // Check for CAS conflict (409) - retry with fresh tip via fast /tip endpoint\n if (response?.status === 409 && !isRetry) {\n const { data: freshTip, error: tipError } = await client.api.GET('/entities/{id}/tip', {\n params: { path: { id: parentId } },\n });\n if (tipError || !freshTip) throw new Error(`Failed to get fresh tip: ${JSON.stringify(tipError)}`);\n return attemptPut(freshTip.cid, true);\n }\n throw new Error(JSON.stringify(error));\n }\n }\n };\n\n await attemptPut(expectTip, false);\n\n completedParents++;\n reportProgress({\n phase: 'backlinking',\n totalParents,\n completedParents,\n currentItem: `parent:${parentId}`,\n });\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n if (continueOnError) {\n errors.push({ path: `parent:${parentId}`, error: `Backlink failed: ${errorMsg}` });\n completedParents++;\n } else {\n throw new Error(`Failed to backlink parent ${parentId}: ${errorMsg}`);\n }\n }\n });\n\n // ─────────────────────────────────────────────────────────────────────────\n // PHASE 3: Upload file content\n // Tree is now browsable! Users can explore while content uploads.\n //\n // Two paths based on file size:\n // - Small files (<5MB): Direct upload through API (simple, CID computed server-side)\n // - Large files (>=5MB): Presigned URL to R2 (fast, CID computed client-side)\n // ─────────────────────────────────────────────────────────────────────────\n reportProgress({ phase: 'uploading', bytesUploaded: 0 });\n\n // Use byte-based pool to maintain target bytes in flight\n const pool = new BytePool(maxBytesInFlight);\n\n await Promise.all(\n createdFiles.map(async (file) => {\n await pool.run(file.size, async () => {\n try {\n // Get file data\n const fileData = await file.getData();\n\n let body: Blob;\n if (fileData instanceof Blob) {\n body = fileData;\n } else if (fileData instanceof Uint8Array) {\n const arrayBuffer = new ArrayBuffer(fileData.byteLength);\n new Uint8Array(arrayBuffer).set(fileData);\n body = new Blob([arrayBuffer], { type: file.mimeType });\n } else {\n body = new Blob([fileData], { type: file.mimeType });\n }\n\n if (file.size >= PRESIGNED_URL_THRESHOLD) {\n // ─────────────────────────────────────────────────────────────\n // LARGE FILE: Presigned URL flow (bypasses API worker)\n // ─────────────────────────────────────────────────────────────\n\n // 1. Compute CID client-side\n const fileCid = await computeCid(fileData);\n\n // 2. Get presigned URL from API\n const { data: urlData, error: urlError } = await client.api.POST(\n '/entities/{id}/content/upload-url',\n {\n params: { path: { id: file.id } },\n body: {\n cid: fileCid,\n content_type: file.mimeType,\n size: file.size,\n },\n }\n );\n\n if (urlError || !urlData) {\n throw new Error(`Failed to get presigned URL: ${JSON.stringify(urlError)}`);\n }\n\n // 3. PUT directly to R2 (fast!)\n const r2Response = await fetch(urlData.upload_url, {\n method: 'PUT',\n headers: { 'Content-Type': file.mimeType },\n body: body,\n });\n\n if (!r2Response.ok) {\n const errorText = await r2Response.text();\n throw new Error(`R2 upload failed: ${r2Response.status} ${errorText}`);\n }\n\n // 4. Complete upload by updating entity metadata\n const { error: completeError } = await client.api.POST(\n '/entities/{id}/content/complete',\n {\n params: { path: { id: file.id } },\n body: {\n key: 'v1',\n cid: fileCid,\n size: file.size,\n content_type: file.mimeType,\n filename: file.name,\n expect_tip: file.entityCid,\n },\n }\n );\n\n if (completeError) {\n throw new Error(`Failed to complete upload: ${JSON.stringify(completeError)}`);\n }\n } else {\n // ─────────────────────────────────────────────────────────────\n // SMALL FILE: Direct upload through API\n // ─────────────────────────────────────────────────────────────\n\n const { error: uploadError } = await client.api.POST('/entities/{id}/content', {\n params: { path: { id: file.id }, query: { key: 'v1', filename: file.name } },\n body: body as unknown as Record<string, never>,\n bodySerializer: (b: unknown) => b as BodyInit,\n headers: { 'Content-Type': file.mimeType },\n } as Parameters<typeof client.api.POST>[1]);\n\n if (uploadError) {\n throw new Error(`Upload failed: ${JSON.stringify(uploadError)}`);\n }\n }\n\n bytesUploaded += file.size;\n reportProgress({\n phase: 'uploading',\n bytesUploaded,\n currentItem: file.relativePath,\n });\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n if (continueOnError) {\n errors.push({ path: file.relativePath, error: `Upload failed: ${errorMsg}` });\n } else {\n throw new Error(`Failed to upload ${file.relativePath}: ${errorMsg}`);\n }\n }\n });\n })\n );\n\n // ─────────────────────────────────────────────────────────────────────────\n // Complete!\n // ─────────────────────────────────────────────────────────────────────────\n reportProgress({ phase: 'complete', totalParents, completedParents, bytesUploaded });\n\n const resultFolders: CreatedEntity[] = createdFolders.map((f) => ({\n id: f.id,\n cid: f.entityCid,\n type: 'folder' as const,\n relativePath: f.relativePath,\n }));\n\n const resultFiles: CreatedEntity[] = createdFiles.map((f) => ({\n id: f.id,\n cid: f.entityCid,\n type: 'file' as const,\n relativePath: f.relativePath,\n }));\n\n return {\n success: errors.length === 0,\n collection: {\n id: collectionId,\n cid: collectionCid,\n created: collectionCreated,\n },\n folders: resultFolders,\n files: resultFiles,\n errors,\n };\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n\n reportProgress({\n phase: 'error',\n error: errorMsg,\n });\n\n return {\n success: false,\n collection: {\n id: target.collectionId ?? '',\n cid: '',\n created: false,\n },\n folders: createdFolders.map((f) => ({\n id: f.id,\n cid: f.entityCid,\n type: 'folder' as const,\n relativePath: f.relativePath,\n })),\n files: createdFiles.map((f) => ({\n id: f.id,\n cid: f.entityCid,\n type: 'file' as const,\n relativePath: f.relativePath,\n })),\n errors: [...errors, { path: '', error: errorMsg }],\n };\n }\n}\n","/**\n * Platform-specific Scanners\n *\n * Helpers to build UploadTree from different input sources.\n * These are optional utilities - users can also build UploadTree manually.\n */\n\n/// <reference lib=\"dom\" />\n\nimport type { UploadTree, UploadFile, UploadFolder } from './types.js';\n\n/**\n * Detect MIME type from filename.\n * Falls back to 'application/octet-stream' if unknown.\n */\nexport function getMimeType(filename: string): string {\n const ext = filename.toLowerCase().split('.').pop() || '';\n\n const mimeTypes: Record<string, string> = {\n // Images\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n gif: 'image/gif',\n webp: 'image/webp',\n svg: 'image/svg+xml',\n ico: 'image/x-icon',\n bmp: 'image/bmp',\n tiff: 'image/tiff',\n tif: 'image/tiff',\n\n // Documents\n pdf: 'application/pdf',\n doc: 'application/msword',\n docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n xls: 'application/vnd.ms-excel',\n xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n ppt: 'application/vnd.ms-powerpoint',\n pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n odt: 'application/vnd.oasis.opendocument.text',\n ods: 'application/vnd.oasis.opendocument.spreadsheet',\n odp: 'application/vnd.oasis.opendocument.presentation',\n\n // Text\n txt: 'text/plain',\n md: 'text/markdown',\n csv: 'text/csv',\n html: 'text/html',\n htm: 'text/html',\n css: 'text/css',\n xml: 'text/xml',\n rtf: 'application/rtf',\n\n // Code\n js: 'text/javascript',\n mjs: 'text/javascript',\n ts: 'text/typescript',\n jsx: 'text/javascript',\n tsx: 'text/typescript',\n json: 'application/json',\n yaml: 'text/yaml',\n yml: 'text/yaml',\n\n // Archives\n zip: 'application/zip',\n tar: 'application/x-tar',\n gz: 'application/gzip',\n rar: 'application/vnd.rar',\n '7z': 'application/x-7z-compressed',\n\n // Audio\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n ogg: 'audio/ogg',\n m4a: 'audio/mp4',\n flac: 'audio/flac',\n\n // Video\n mp4: 'video/mp4',\n webm: 'video/webm',\n avi: 'video/x-msvideo',\n mov: 'video/quicktime',\n mkv: 'video/x-matroska',\n\n // Fonts\n woff: 'font/woff',\n woff2: 'font/woff2',\n ttf: 'font/ttf',\n otf: 'font/otf',\n\n // Other\n wasm: 'application/wasm',\n };\n\n return mimeTypes[ext] || 'application/octet-stream';\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Browser Scanners\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Scan browser FileSystemEntry objects (from drag-drop or showDirectoryPicker).\n *\n * **Browser only** - uses FileSystemEntry API.\n *\n * @param entries - FileSystemEntry array from DataTransferItem.webkitGetAsEntry()\n * @returns UploadTree ready for upload\n *\n * @example\n * ```typescript\n * // In a drop handler\n * dropzone.ondrop = async (e) => {\n * const entries = Array.from(e.dataTransfer.items)\n * .map(item => item.webkitGetAsEntry())\n * .filter(Boolean);\n *\n * const tree = await scanFileSystemEntries(entries);\n * const result = await uploadTree(client, tree, { target: { collectionId: '...' } });\n * };\n * ```\n */\nexport async function scanFileSystemEntries(\n entries: FileSystemEntry[],\n options: {\n /** Patterns to ignore */\n ignore?: string[];\n } = {}\n): Promise<UploadTree> {\n const { ignore = ['node_modules', '.git', '.DS_Store'] } = options;\n\n const files: UploadFile[] = [];\n const folders: UploadFolder[] = [];\n\n async function processEntry(entry: FileSystemEntry, parentPath: string): Promise<void> {\n const name = entry.name;\n\n // Skip ignored patterns\n if (ignore.some((pattern) => name === pattern)) {\n return;\n }\n\n const relativePath = parentPath ? `${parentPath}/${name}` : name;\n\n if (entry.isFile) {\n const fileEntry = entry as FileSystemFileEntry;\n\n // Get File object from FileSystemFileEntry\n const file = await new Promise<File>((resolve, reject) => {\n fileEntry.file(resolve, reject);\n });\n\n files.push({\n name,\n relativePath,\n size: file.size,\n mimeType: file.type || getMimeType(name),\n getData: async () => file.arrayBuffer(),\n });\n } else if (entry.isDirectory) {\n const dirEntry = entry as FileSystemDirectoryEntry;\n\n folders.push({\n name,\n relativePath,\n });\n\n // Read directory contents\n const reader = dirEntry.createReader();\n const childEntries = await new Promise<FileSystemEntry[]>((resolve, reject) => {\n const allEntries: FileSystemEntry[] = [];\n\n function readEntries() {\n reader.readEntries((entries) => {\n if (entries.length === 0) {\n resolve(allEntries);\n } else {\n allEntries.push(...entries);\n readEntries(); // Continue reading (readEntries returns max 100 at a time)\n }\n }, reject);\n }\n\n readEntries();\n });\n\n // Process children\n for (const childEntry of childEntries) {\n await processEntry(childEntry, relativePath);\n }\n }\n }\n\n // Process all root entries\n for (const entry of entries) {\n if (entry) {\n await processEntry(entry, '');\n }\n }\n\n // Sort folders by depth\n folders.sort((a, b) => a.relativePath.split('/').length - b.relativePath.split('/').length);\n\n return { files, folders };\n}\n\n/**\n * Build UploadTree from a FileList (from <input type=\"file\"> with webkitdirectory).\n *\n * **Browser only** - uses File API.\n *\n * @param fileList - FileList from input element\n * @returns UploadTree ready for upload\n *\n * @example\n * ```typescript\n * <input type=\"file\" webkitdirectory multiple onChange={async (e) => {\n * const tree = await scanFileList(e.target.files);\n * const result = await uploadTree(client, tree, { target: { collectionId: '...' } });\n * }} />\n * ```\n */\nexport async function scanFileList(\n fileList: FileList,\n options: {\n /** Patterns to ignore */\n ignore?: string[];\n } = {}\n): Promise<UploadTree> {\n const { ignore = ['node_modules', '.git', '.DS_Store'] } = options;\n\n const files: UploadFile[] = [];\n const folderPaths = new Set<string>();\n\n for (let i = 0; i < fileList.length; i++) {\n const file = fileList[i];\n if (!file) continue;\n\n // webkitRelativePath gives us the path including the root folder\n const relativePath = file.webkitRelativePath || file.name;\n const name = file.name;\n\n // Skip ignored patterns (check each path segment)\n const pathSegments = relativePath.split('/');\n if (pathSegments.some((segment: string) => ignore.includes(segment))) {\n continue;\n }\n\n // Extract folder paths\n const pathParts = relativePath.split('/');\n for (let j = 1; j < pathParts.length; j++) {\n const folderPath = pathParts.slice(0, j).join('/');\n folderPaths.add(folderPath);\n }\n\n // Capture file reference for closure\n const fileRef = file;\n files.push({\n name,\n relativePath,\n size: fileRef.size,\n mimeType: fileRef.type || getMimeType(name),\n getData: async () => fileRef.arrayBuffer(),\n });\n }\n\n // Convert folder paths to UploadFolder objects\n const folders: UploadFolder[] = Array.from(folderPaths)\n .map((path) => ({\n name: path.split('/').pop()!,\n relativePath: path,\n }))\n .sort((a, b) => a.relativePath.split('/').length - b.relativePath.split('/').length);\n\n return { files, folders };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Utility: Build tree from flat file list\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Build an UploadTree from a flat array of files with paths.\n *\n * Useful for programmatically constructing uploads without filesystem access.\n *\n * @param items - Array of file items with path, data, and optional metadata\n * @returns UploadTree ready for upload\n *\n * @example\n * ```typescript\n * const tree = buildUploadTree([\n * { path: 'docs/readme.md', data: new Blob(['# Hello']) },\n * { path: 'docs/images/logo.png', data: logoBlob },\n * ]);\n * ```\n */\nexport function buildUploadTree(\n items: Array<{\n /** Relative path (e.g., \"docs/readme.md\") */\n path: string;\n /** File data */\n data: Blob | ArrayBuffer | Uint8Array;\n /** MIME type (auto-detected if not provided) */\n mimeType?: string;\n }>\n): UploadTree {\n const files: UploadFile[] = [];\n const folderPaths = new Set<string>();\n\n for (const item of items) {\n const pathParts = item.path.split('/');\n const name = pathParts.pop()!;\n\n // Extract folder paths\n for (let i = 1; i <= pathParts.length; i++) {\n folderPaths.add(pathParts.slice(0, i).join('/'));\n }\n\n // Determine size\n let size: number;\n if (item.data instanceof Blob) {\n size = item.data.size;\n } else if (item.data instanceof ArrayBuffer) {\n size = item.data.byteLength;\n } else {\n size = item.data.length;\n }\n\n files.push({\n name,\n relativePath: item.path,\n size,\n mimeType: item.mimeType || getMimeType(name),\n getData: async () => item.data,\n });\n }\n\n const folders: UploadFolder[] = Array.from(folderPaths)\n .map((path) => ({\n name: path.split('/').pop()!,\n relativePath: path,\n }))\n .sort((a, b) => a.relativePath.split('/').length - b.relativePath.split('/').length);\n\n return { files, folders };\n}\n","/**\n * Upload to Entity Helper\n *\n * Upload one or more files to an existing entity.\n * Handles presigned URL flow with parallel uploads and sequential CAS completion.\n */\n\nimport type { ArkeClient } from '../../client/ArkeClient.js';\nimport { computeCid } from './cid.js';\nimport { getMimeType } from './scanners.js';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A single file to upload to an entity.\n */\nexport interface UploadItem {\n /** Content key. Defaults to filename without extension, or \"file_{cid_hash}\" if no filename. */\n key?: string;\n /** File data */\n data: File | Blob | ArrayBuffer | Uint8Array;\n /** MIME type (auto-detected from filename if not provided) */\n contentType?: string;\n /** Original filename for Content-Disposition on download */\n filename?: string;\n}\n\n/**\n * Progress information during upload to entity.\n */\nexport interface UploadToEntityProgress {\n /** Current phase */\n phase: 'preparing' | 'uploading' | 'completing';\n /** Total bytes across all files */\n totalBytes: number;\n /** Bytes uploaded so far */\n uploadedBytes: number;\n /** Number of files completed */\n completedFiles: number;\n /** Total number of files */\n totalFiles: number;\n}\n\n/**\n * Options for uploading to an entity.\n */\nexport interface UploadToEntityOptions {\n /** Progress callback */\n onProgress?: (progress: UploadToEntityProgress) => void;\n}\n\n/**\n * Result for a single uploaded file.\n */\nexport interface UploadContentResult {\n /** Content version key */\n key: string;\n /** Content CID */\n cid: string;\n /** File size in bytes */\n size: number;\n /** MIME type */\n contentType: string;\n /** Original filename */\n filename?: string;\n}\n\n/**\n * Result of uploading to an entity.\n */\nexport interface UploadToEntityResult {\n /** Entity ID */\n id: string;\n /** Final entity CID after all uploads */\n cid: string;\n /** Entity CID before first upload */\n prevCid: string;\n /** Results for each uploaded file */\n contents: UploadContentResult[];\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Internal Types\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface PreparedItem {\n key: string;\n bytes: ArrayBuffer;\n size: number;\n contentType: string;\n filename?: string;\n cid: string;\n}\n\ninterface UploadedItem extends PreparedItem {\n uploadUrl: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Constants\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst MAX_CAS_RETRIES = 3;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Main Function\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Upload one or more files to an existing entity.\n *\n * Files are uploaded in parallel for speed, then metadata is committed\n * sequentially with CAS protection to ensure consistency.\n *\n * @example\n * ```typescript\n * // Single file\n * const result = await uploadToEntity(client, entityId, [\n * { key: 'v1', data: file }\n * ]);\n *\n * // Multiple files (e.g., original + thumbnail)\n * const result = await uploadToEntity(client, entityId, [\n * { key: 'original', data: originalFile },\n * { key: 'thumbnail', data: thumbnailBlob },\n * { key: 'preview', data: previewBlob },\n * ]);\n *\n * // With progress tracking\n * const result = await uploadToEntity(client, entityId, items, {\n * onProgress: (p) => console.log(`${p.phase}: ${p.uploadedBytes}/${p.totalBytes}`),\n * });\n * ```\n */\nexport async function uploadToEntity(\n client: ArkeClient,\n entityId: string,\n items: UploadItem[],\n options: UploadToEntityOptions = {}\n): Promise<UploadToEntityResult> {\n if (items.length === 0) {\n throw new Error('At least one upload item is required');\n }\n\n const { onProgress } = options;\n\n // Progress state\n let progressState: UploadToEntityProgress = {\n phase: 'preparing',\n totalBytes: 0,\n uploadedBytes: 0,\n completedFiles: 0,\n totalFiles: items.length,\n };\n\n const reportProgress = (update: Partial<UploadToEntityProgress>) => {\n progressState = { ...progressState, ...update };\n onProgress?.(progressState);\n };\n\n // ─────────────────────────────────────────────────────────────────────────\n // PHASE 1: Prepare all files (parallel)\n // - Convert to ArrayBuffer\n // - Determine size, contentType, filename\n // - Compute CID\n // ─────────────────────────────────────────────────────────────────────────\n reportProgress({ phase: 'preparing' });\n\n const prepared: PreparedItem[] = await Promise.all(\n items.map((item) => prepareItem(item))\n );\n\n // Check for duplicate keys\n const keys = prepared.map((p) => p.key);\n const duplicates = keys.filter((key, i) => keys.indexOf(key) !== i);\n if (duplicates.length > 0) {\n const uniqueDupes = [...new Set(duplicates)];\n throw new Error(\n `Duplicate content keys detected: ${uniqueDupes.map((k) => `\"${k}\"`).join(', ')}. ` +\n `Each file must have a unique key. Provide explicit keys to resolve.`\n );\n }\n\n const totalBytes = prepared.reduce((sum, p) => sum + p.size, 0);\n reportProgress({ totalBytes });\n\n // ─────────────────────────────────────────────────────────────────────────\n // PHASE 2: Get presigned URLs (parallel, no tip needed)\n // ─────────────────────────────────────────────────────────────────────────\n const uploadInfos: UploadedItem[] = await Promise.all(\n prepared.map(async (item) => {\n const { data: presigned, error } = await client.api.POST(\n '/entities/{id}/content/upload-url',\n {\n params: { path: { id: entityId } },\n body: {\n cid: item.cid,\n content_type: item.contentType,\n size: item.size,\n },\n }\n );\n\n if (error || !presigned) {\n throw new Error(\n `Failed to get upload URL for ${item.key}: ${JSON.stringify(error)}`\n );\n }\n\n return {\n ...item,\n uploadUrl: presigned.upload_url,\n };\n })\n );\n\n // ─────────────────────────────────────────────────────────────────────────\n // PHASE 3: Upload to R2 (parallel - the slow part)\n // ─────────────────────────────────────────────────────────────────────────\n reportProgress({ phase: 'uploading', uploadedBytes: 0 });\n\n let uploadedBytes = 0;\n await Promise.all(\n uploadInfos.map(async (item) => {\n const response = await fetch(item.uploadUrl, {\n method: 'PUT',\n headers: { 'Content-Type': item.contentType },\n body: item.bytes,\n });\n\n if (!response.ok) {\n throw new Error(\n `Upload to R2 failed for ${item.key}: ${response.statusText}`\n );\n }\n\n uploadedBytes += item.size;\n reportProgress({ uploadedBytes });\n })\n );\n\n // ─────────────────────────────────────────────────────────────────────────\n // PHASE 4: Get current tip\n // ─────────────────────────────────────────────────────────────────────────\n const { data: tipData, error: tipError } = await client.api.GET(\n '/entities/{id}/tip',\n { params: { path: { id: entityId } } }\n );\n\n if (tipError || !tipData) {\n throw new Error(`Failed to get entity tip: ${JSON.stringify(tipError)}`);\n }\n\n const prevCid = tipData.cid;\n let currentTip = prevCid;\n\n // ─────────────────────────────────────────────────────────────────────────\n // PHASE 5: Complete uploads (sequential with CAS retry)\n // ─────────────────────────────────────────────────────────────────────────\n reportProgress({ phase: 'completing', completedFiles: 0 });\n\n const contents: UploadContentResult[] = [];\n let finalCid = currentTip;\n\n for (let i = 0; i < uploadInfos.length; i++) {\n const item = uploadInfos[i]!;\n const result = await completeWithRetry(client, entityId, item, currentTip);\n\n currentTip = result.cid; // Use new tip for next complete\n finalCid = result.cid;\n\n contents.push({\n key: item.key,\n cid: result.contentCid,\n size: item.size,\n contentType: item.contentType,\n filename: item.filename,\n });\n\n reportProgress({ completedFiles: i + 1 });\n }\n\n return {\n id: entityId,\n cid: finalCid,\n prevCid,\n contents,\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Prepare an upload item: convert to bytes, detect metadata, compute CID.\n */\nasync function prepareItem(item: UploadItem): Promise<PreparedItem> {\n const { data } = item;\n\n // Convert to ArrayBuffer\n let bytes: ArrayBuffer;\n if (data instanceof Blob) {\n bytes = await data.arrayBuffer();\n } else if (data instanceof ArrayBuffer) {\n bytes = data;\n } else {\n // Uint8Array - copy to handle SharedArrayBuffer\n const buffer = new ArrayBuffer(data.byteLength);\n new Uint8Array(buffer).set(data);\n bytes = buffer;\n }\n\n // Determine properties\n const size = bytes.byteLength;\n let filename = item.filename;\n let contentType = item.contentType;\n\n if (data instanceof File) {\n filename = filename ?? data.name;\n contentType = contentType ?? (data.type || getMimeType(data.name));\n }\n\n contentType = contentType ?? 'application/octet-stream';\n if (contentType === 'application/octet-stream' && filename) {\n contentType = getMimeType(filename);\n }\n\n // Compute CID\n const cid = await computeCid(new Uint8Array(bytes));\n\n // Determine key: explicit > filename without extension > cid-based\n let key = item.key;\n if (!key && filename) {\n // Strip extension: \"document.pdf\" -> \"document\"\n const lastDot = filename.lastIndexOf('.');\n key = lastDot > 0 ? filename.substring(0, lastDot) : filename;\n }\n if (!key) {\n // Use last 8 chars of CID for uniqueness\n key = `file_${cid.slice(-8)}`;\n }\n\n return { key, bytes, size, contentType, filename, cid };\n}\n\n/**\n * Complete an upload with CAS retry on conflict.\n */\nasync function completeWithRetry(\n client: ArkeClient,\n entityId: string,\n item: UploadedItem,\n expectTip: string\n): Promise<{ cid: string; contentCid: string }> {\n let tip = expectTip;\n\n for (let attempt = 0; attempt < MAX_CAS_RETRIES; attempt++) {\n const { data, error, response } = await client.api.POST(\n '/entities/{id}/content/complete',\n {\n params: { path: { id: entityId } },\n body: {\n key: item.key,\n cid: item.cid,\n size: item.size,\n content_type: item.contentType,\n filename: item.filename,\n expect_tip: tip,\n },\n }\n );\n\n if (data) {\n return { cid: data.cid, contentCid: data.content.cid };\n }\n\n // Check for CAS conflict (409)\n if (response?.status === 409) {\n // Refresh tip and retry\n const { data: freshTip, error: tipError } = await client.api.GET(\n '/entities/{id}/tip',\n { params: { path: { id: entityId } } }\n );\n\n if (tipError || !freshTip) {\n throw new Error(`Failed to refresh tip: ${JSON.stringify(tipError)}`);\n }\n\n tip = freshTip.cid;\n continue;\n }\n\n // Other error - throw immediately\n throw new Error(\n `Failed to complete upload for ${item.key}: ${JSON.stringify(error)}`\n );\n }\n\n throw new Error(`Max CAS retries exceeded for ${item.key}`);\n}\n","/**\n * Folder Operations (Legacy)\n *\n * @deprecated Use the new upload module instead:\n * ```typescript\n * import { uploadTree, buildUploadTree } from '@arke-institute/sdk/operations';\n *\n * const tree = buildUploadTree([\n * { path: 'docs/readme.md', data: readmeBuffer },\n * { path: 'images/logo.png', data: logoBlob },\n * ]);\n * const result = await uploadTree(client, tree, {\n * target: { collectionId: '...' },\n * });\n * ```\n */\n\nimport type { ArkeClient } from '../client/ArkeClient.js';\n\n/**\n * @deprecated Use UploadProgress from upload module\n */\nexport interface UploadProgress {\n phase: 'scanning' | 'creating-folders' | 'uploading-files' | 'linking' | 'complete';\n totalFiles: number;\n completedFiles: number;\n totalFolders: number;\n completedFolders: number;\n currentFile?: string;\n}\n\n/**\n * @deprecated Use UploadOptions from upload module\n */\nexport interface UploadDirectoryOptions {\n /** Collection to upload into */\n collectionId: string;\n /** Parent folder ID (optional - creates at root if not provided) */\n parentFolderId?: string;\n /** Progress callback */\n onProgress?: (progress: UploadProgress) => void;\n /** Max concurrent uploads */\n concurrency?: number;\n}\n\n/**\n * @deprecated Use UploadResult from upload module\n */\nexport interface UploadDirectoryResult {\n /** Root folder entity */\n rootFolder: unknown;\n /** All created folder entities */\n folders: unknown[];\n /** All created file entities */\n files: unknown[];\n}\n\n/**\n * Folder operations helper\n *\n * @deprecated Use uploadTree and buildUploadTree functions instead:\n * ```typescript\n * import { uploadTree, buildUploadTree } from '@arke-institute/sdk/operations';\n *\n * const tree = buildUploadTree([\n * { path: 'docs/readme.md', data: readmeBuffer },\n * ]);\n * const result = await uploadTree(client, tree, {\n * target: { collectionId: '...' },\n * });\n * ```\n */\nexport class FolderOperations {\n constructor(private client: ArkeClient) {\n void client; // Suppress unused warning\n }\n\n /**\n * Upload a local directory to Arke\n *\n * @deprecated This method has been removed. Use uploadTree and buildUploadTree instead.\n */\n async uploadDirectory(\n _localPath: string,\n _options: UploadDirectoryOptions\n ): Promise<UploadDirectoryResult> {\n throw new Error(\n 'FolderOperations.uploadDirectory has been removed. ' +\n 'Use uploadTree() with buildUploadTree() instead. ' +\n 'See: https://github.com/arke-institute/arke-sdk#upload-module'\n );\n }\n}\n","/**\n * Batch Operations\n *\n * High-level operations for bulk entity and relationship management.\n *\n * TODO: Implement batch operations\n * - createEntities: Create multiple entities in parallel\n * - updateEntities: Update multiple entities in parallel\n * - createRelationships: Create multiple relationships in parallel\n */\n\nimport type { ArkeClient } from '../client/ArkeClient.js';\n\nexport interface BatchCreateOptions {\n /** Max concurrent operations */\n concurrency?: number;\n /** Continue on individual failures */\n continueOnError?: boolean;\n /** Progress callback */\n onProgress?: (completed: number, total: number) => void;\n}\n\nexport interface BatchResult<T> {\n /** Successfully completed operations */\n succeeded: T[];\n /** Failed operations with errors */\n failed: Array<{ input: unknown; error: Error }>;\n}\n\n/**\n * Batch operations helper\n *\n * @example\n * ```typescript\n * const batch = new BatchOperations(arkeClient);\n * const result = await batch.createEntities([\n * { type: 'document', properties: { title: 'Doc 1' } },\n * { type: 'document', properties: { title: 'Doc 2' } },\n * ], { concurrency: 5 });\n * ```\n */\nexport class BatchOperations {\n constructor(private client: ArkeClient) {}\n\n /**\n * Create multiple entities in parallel\n *\n * TODO: Implement this method\n */\n async createEntities(\n _entities: Array<{\n collectionId: string;\n type: string;\n properties?: Record<string, unknown>;\n }>,\n _options?: BatchCreateOptions\n ): Promise<BatchResult<unknown>> {\n throw new Error('BatchOperations.createEntities is not yet implemented');\n }\n\n /**\n * Create multiple relationships in parallel\n *\n * TODO: Implement this method\n */\n async createRelationships(\n _relationships: Array<{\n sourceId: string;\n targetId: string;\n predicate: string;\n bidirectional?: boolean;\n properties?: Record<string, unknown>;\n }>,\n _options?: BatchCreateOptions\n ): Promise<BatchResult<unknown>> {\n throw new Error('BatchOperations.createRelationships is not yet implemented');\n }\n}\n","/**\n * Crypto Operations\n *\n * Cryptographic utilities for agents and content addressing.\n *\n * TODO: Implement crypto operations\n * - generateKeyPair: Generate Ed25519 key pair for agent authentication\n * - signPayload: Sign a payload with agent private key\n * - computeCID: Compute IPFS CID for content\n */\n\n/**\n * Ed25519 key pair for agent authentication\n */\nexport interface KeyPair {\n /** Public key in base64 */\n publicKey: string;\n /** Private key in base64 (keep secret!) */\n privateKey: string;\n}\n\n/**\n * Signed payload with signature\n */\nexport interface SignedPayload {\n /** Original payload */\n payload: string;\n /** Ed25519 signature in base64 */\n signature: string;\n /** Timestamp of signature */\n timestamp: number;\n}\n\n/**\n * Crypto operations helper\n *\n * @example\n * ```typescript\n * // Generate key pair for a new agent\n * const { publicKey, privateKey } = await CryptoOperations.generateKeyPair();\n *\n * // Sign a payload\n * const signed = await CryptoOperations.signPayload(privateKey, payload);\n * ```\n */\nexport class CryptoOperations {\n /**\n * Generate an Ed25519 key pair for agent authentication\n *\n * TODO: Implement using Node.js crypto or Web Crypto API\n */\n static async generateKeyPair(): Promise<KeyPair> {\n throw new Error('CryptoOperations.generateKeyPair is not yet implemented');\n }\n\n /**\n * Sign a payload with an Ed25519 private key\n *\n * TODO: Implement signature generation\n */\n static async signPayload(_privateKey: string, _payload: string): Promise<SignedPayload> {\n throw new Error('CryptoOperations.signPayload is not yet implemented');\n }\n\n /**\n * Verify an Ed25519 signature\n *\n * TODO: Implement signature verification\n */\n static async verifySignature(\n _publicKey: string,\n _payload: string,\n _signature: string\n ): Promise<boolean> {\n throw new Error('CryptoOperations.verifySignature is not yet implemented');\n }\n\n /**\n * Compute IPFS CID for content\n *\n * TODO: Implement using multiformats library\n */\n static async computeCID(_content: Uint8Array): Promise<string> {\n throw new Error('CryptoOperations.computeCID is not yet implemented');\n }\n}\n","/**\n * CAS (Compare-And-Swap) retry utility for concurrent updates\n *\n * Use this for additive operations where your update doesn't depend on\n * current state - you just need the tip for CAS validation.\n */\n\nimport { CASConflictError } from '../client/errors.js';\n\n/**\n * Configuration for CAS retry behavior\n */\nexport interface CasRetryOptions {\n /**\n * Expected number of concurrent writers - affects retry timing.\n * Higher concurrency = more initial spread and more retry headroom.\n * @default 10\n */\n concurrency?: number;\n\n /**\n * Override: maximum retry attempts.\n * If not specified, calculated from concurrency.\n */\n maxAttempts?: number;\n\n /**\n * Base delay between retries in ms.\n * Actual delay grows exponentially with jitter.\n * @default 50\n */\n baseDelayMs?: number;\n\n /**\n * Maximum delay between retries in ms (caps exponential growth).\n * @default 10000\n */\n maxDelayMs?: number;\n\n /**\n * Add initial random delay before first attempt to spread concurrent requests.\n * Delay range: 0 to (concurrency * 100)ms. E.g., 500 concurrent = 0-50s spread.\n * This dramatically reduces collisions by staggering first attempts.\n * @default true\n */\n spreadInitial?: boolean;\n\n /**\n * Called before each retry attempt.\n * Useful for logging or monitoring.\n */\n onRetry?: (attempt: number, error: CASConflictError, delayMs: number) => void;\n}\n\n/**\n * Result of a successful CAS retry operation\n */\nexport interface CasRetryResult<T> {\n /** The successful response data */\n data: T;\n /** Number of attempts made (1 = succeeded first try) */\n attempts: number;\n}\n\n/**\n * Callbacks for CAS retry operation\n */\nexport interface CasRetryCallbacks<T> {\n /** Get the current tip/CID - called before each attempt */\n getTip: () => Promise<string>;\n /** Perform the update with given tip - return {data, error} like openapi-fetch */\n update: (tip: string) => Promise<{ data?: T; error?: unknown }>;\n}\n\n/**\n * Default configuration values\n */\nexport const DEFAULT_CAS_RETRY_CONFIG = {\n concurrency: 10,\n baseDelayMs: 50,\n maxDelayMs: 10000, // 10 seconds - allows more spread at high concurrency\n} as const;\n\n/**\n * Calculate max attempts from concurrency level.\n * Formula: max(5, ceil(log2(concurrency) * 4) + floor(concurrency / 100))\n *\n * The logarithmic component handles the base scaling, while the linear\n * component adds extra headroom at very high concurrency levels.\n *\n * Examples:\n * - concurrency 10 → 14 attempts\n * - concurrency 100 → 28 attempts\n * - concurrency 500 → 41 attempts\n * - concurrency 1000 → 50 attempts\n */\nexport function calculateMaxAttempts(concurrency: number): number {\n const logComponent = Math.ceil(Math.log2(Math.max(2, concurrency)) * 4);\n const linearComponent = Math.floor(concurrency / 100);\n return Math.max(5, logComponent + linearComponent);\n}\n\n/**\n * Calculate delay with exponential backoff and heavy jitter.\n * Jitter is 0-100% of the base delay to spread concurrent retries.\n *\n * Formula: min(baseDelay * 1.5^attempt + random(0, baseDelay * 1.5^attempt), maxDelay)\n */\nexport function calculateCasDelay(\n attempt: number,\n baseDelayMs: number,\n maxDelayMs: number\n): number {\n // Exponential backoff: baseDelay * 1.5^attempt\n const exponentialDelay = baseDelayMs * Math.pow(1.5, attempt);\n\n // Cap at maxDelay before adding jitter\n const cappedDelay = Math.min(exponentialDelay, maxDelayMs);\n\n // Heavy jitter: 0-100% of the delay (critical for spreading concurrent retries)\n const jitter = Math.random() * cappedDelay;\n\n return Math.floor(cappedDelay + jitter);\n}\n\n/**\n * Check if an error is a CAS conflict.\n * Handles multiple error shapes:\n * - openapi-fetch: { status: 409, ... }\n * - API error body: { error: \"CAS failure: ...\" }\n */\nexport function isCasConflictError(error: unknown): boolean {\n if (error === null || error === undefined) {\n return false;\n }\n\n if (typeof error !== 'object') {\n return false;\n }\n\n // Check for status 409 (openapi-fetch error shape)\n if ('status' in error && (error as { status: unknown }).status === 409) {\n return true;\n }\n\n // Check for error message pattern (API returns CAS failures this way)\n if ('error' in error) {\n const errorMsg = (error as { error: unknown }).error;\n if (typeof errorMsg === 'string' && errorMsg.startsWith('CAS failure:')) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Sleep for a specified duration\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Error thrown when CAS retries are exhausted\n */\nexport class CasRetryExhaustedError extends Error {\n constructor(\n public readonly attempts: number,\n public readonly lastError: CASConflictError\n ) {\n super(\n `CAS update failed after ${attempts} attempts. ` +\n `Expected tip: ${lastError.expectedTip}, actual: ${lastError.actualTip}`\n );\n this.name = 'CasRetryExhaustedError';\n Error.captureStackTrace?.(this, this.constructor);\n }\n}\n\n/**\n * Wrap a CAS update operation with automatic retry on conflicts.\n *\n * Use this for additive operations where your update doesn't depend on\n * current state - you just need the tip for CAS validation.\n *\n * @example\n * ```typescript\n * const { data, attempts } = await withCasRetry({\n * getTip: async () => {\n * const { data } = await client.api.GET('/entities/{id}/tip', {\n * params: { path: { id: entityId } }\n * });\n * return data!.cid;\n * },\n * update: async (tip) => {\n * return client.api.PUT('/entities/{id}', {\n * params: { path: { id: entityId } },\n * body: {\n * expect_tip: tip,\n * relationships_add: [{ predicate: 'contains', peer: childId }]\n * }\n * });\n * }\n * }, { concurrency: 100 });\n * ```\n *\n * @param callbacks.getTip - Function to fetch the current tip/CID\n * @param callbacks.update - Function to perform the update with the tip\n * @param options - Retry configuration\n * @returns The successful result with attempt count\n * @throws {CasRetryExhaustedError} When all retries are exhausted\n * @throws {Error} For non-CAS errors (not retried)\n */\nexport async function withCasRetry<T>(\n callbacks: CasRetryCallbacks<T>,\n options?: CasRetryOptions\n): Promise<CasRetryResult<T>> {\n const {\n concurrency = DEFAULT_CAS_RETRY_CONFIG.concurrency,\n maxAttempts: maxAttemptsOverride,\n baseDelayMs = DEFAULT_CAS_RETRY_CONFIG.baseDelayMs,\n maxDelayMs = DEFAULT_CAS_RETRY_CONFIG.maxDelayMs,\n spreadInitial = true,\n onRetry,\n } = options ?? {};\n\n const maxAttempts = maxAttemptsOverride ?? calculateMaxAttempts(concurrency);\n\n // Initial spread: random delay before first attempt to reduce initial collisions\n // Range: 0 to (concurrency * 100)ms - e.g., 500 concurrent = 0-50s spread\n // This spreads first attempts over time, dramatically reducing collisions\n if (spreadInitial && concurrency > 1) {\n const initialSpread = Math.floor(Math.random() * concurrency * 100);\n await sleep(initialSpread);\n }\n\n let lastCasError: CASConflictError | undefined;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n // Get fresh tip before each attempt\n const tip = await callbacks.getTip();\n\n // Attempt the update\n const { data, error } = await callbacks.update(tip);\n\n // Success!\n if (!error && data !== undefined) {\n return { data, attempts: attempt };\n }\n\n // Check if it's a CAS conflict\n if (isCasConflictError(error)) {\n // Parse the error details from various formats\n let expectedTip: string | undefined;\n let actualTip: string | undefined;\n\n const errorObj = error as Record<string, unknown>;\n\n // Format 1: { details: { expected, actual } }\n if (errorObj.details && typeof errorObj.details === 'object') {\n const details = errorObj.details as { expected?: string; actual?: string };\n expectedTip = details.expected;\n actualTip = details.actual;\n }\n\n // Format 2: { error: \"CAS failure: expected tip X, got Y\" }\n if (!expectedTip && typeof errorObj.error === 'string') {\n const match = errorObj.error.match(/expected tip (\\S+), got (\\S+)/);\n if (match) {\n expectedTip = match[1];\n actualTip = match[2];\n }\n }\n\n lastCasError = new CASConflictError(expectedTip, actualTip);\n\n // If we have more attempts, wait and retry\n if (attempt < maxAttempts) {\n const delay = calculateCasDelay(attempt - 1, baseDelayMs, maxDelayMs);\n onRetry?.(attempt, lastCasError, delay);\n await sleep(delay);\n continue;\n }\n } else {\n // Non-CAS error - throw immediately, don't retry\n throw new Error(\n `Update failed with non-CAS error: ${JSON.stringify(error)}`\n );\n }\n }\n\n // Exhausted all attempts\n throw new CasRetryExhaustedError(\n maxAttempts,\n lastCasError ?? new CASConflictError()\n );\n}\n"],"mappings":"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","names":["code","entries"]}
1
+ {"version":3,"sources":["../../src/operations/index.ts","../../src/client/errors.ts","../../src/operations/upload/cid.ts","../../src/operations/upload/engine.ts","../../src/operations/upload/scanners.ts","../../src/operations/upload/single.ts","../../src/operations/folders.ts","../../src/operations/batch.ts","../../src/operations/crypto.ts","../../src/operations/cas.ts"],"sourcesContent":["/**\n * High-level operations built on top of the generated API client\n *\n * These provide convenience methods for common multi-step workflows.\n */\n\n// Upload operations (primary folder/file upload functionality)\nexport {\n // Tree uploads\n uploadTree,\n computeCid,\n verifyCid,\n scanFileSystemEntries,\n scanFileList,\n buildUploadTree,\n getMimeType,\n type UploadFile,\n type UploadFolder,\n type UploadTree,\n type UploadTarget,\n type UploadProgress,\n type UploadOptions,\n type UploadResult,\n type CreatedEntity,\n // Single entity uploads\n uploadToEntity,\n type UploadItem,\n type UploadToEntityProgress,\n type UploadToEntityOptions,\n type UploadToEntityResult,\n type UploadContentResult,\n} from './upload/index.js';\n\n// Legacy FolderOperations class (deprecated - use uploadTree instead)\nexport { FolderOperations, type UploadDirectoryOptions, type UploadDirectoryResult } from './folders.js';\n\n// Batch operations (placeholder)\nexport { BatchOperations, type BatchCreateOptions, type BatchResult } from './batch.js';\n\n// Crypto operations (placeholder)\nexport { CryptoOperations, type KeyPair, type SignedPayload } from './crypto.js';\n\n// CAS retry utility\nexport {\n withCasRetry,\n calculateMaxAttempts,\n calculateCasDelay,\n isCasConflictError,\n CasRetryExhaustedError,\n DEFAULT_CAS_RETRY_CONFIG,\n type CasRetryOptions,\n type CasRetryResult,\n type CasRetryCallbacks,\n} from './cas.js';\n","/**\n * SDK error classes\n */\n\n/**\n * Base error class for all Arke SDK errors\n */\nexport class ArkeError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly status?: number,\n public readonly details?: unknown\n ) {\n super(message);\n this.name = 'ArkeError';\n // Maintains proper stack trace for where error was thrown\n Error.captureStackTrace?.(this, this.constructor);\n }\n\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n status: this.status,\n details: this.details,\n };\n }\n}\n\n/**\n * CAS (Compare-And-Swap) conflict - entity was modified by another request\n */\nexport class CASConflictError extends ArkeError {\n constructor(\n public readonly expectedTip?: string,\n public readonly actualTip?: string\n ) {\n super(\n 'Entity was modified by another request. Refresh and retry with the current tip.',\n 'CAS_CONFLICT',\n 409,\n { expectedTip, actualTip }\n );\n this.name = 'CASConflictError';\n }\n}\n\n/**\n * Resource not found\n */\nexport class NotFoundError extends ArkeError {\n constructor(resourceType: string, id: string) {\n super(`${resourceType} not found: ${id}`, 'NOT_FOUND', 404, { resourceType, id });\n this.name = 'NotFoundError';\n }\n}\n\n/**\n * Validation error - invalid request data\n */\nexport class ValidationError extends ArkeError {\n constructor(\n message: string,\n public readonly field?: string,\n details?: unknown\n ) {\n super(message, 'VALIDATION_ERROR', 400, details ?? { field });\n this.name = 'ValidationError';\n }\n}\n\n/**\n * Authentication required or invalid\n */\nexport class AuthenticationError extends ArkeError {\n constructor(message = 'Authentication required') {\n super(message, 'AUTH_REQUIRED', 401);\n this.name = 'AuthenticationError';\n }\n}\n\n/**\n * Permission denied\n */\nexport class ForbiddenError extends ArkeError {\n constructor(action?: string, resource?: string) {\n const msg = action\n ? `Permission denied: ${action}${resource ? ` on ${resource}` : ''}`\n : 'Permission denied';\n super(msg, 'FORBIDDEN', 403, { action, resource });\n this.name = 'ForbiddenError';\n }\n}\n\n/**\n * Parse API error response into appropriate error class\n */\nexport function parseApiError(status: number, body: unknown): ArkeError {\n const errorBody = body as { error?: string; message?: string; details?: unknown } | null;\n const message = errorBody?.error ?? errorBody?.message ?? 'Unknown error';\n\n switch (status) {\n case 400:\n return new ValidationError(message, undefined, errorBody?.details);\n\n case 401:\n return new AuthenticationError(message);\n\n case 403:\n return new ForbiddenError(message);\n\n case 404:\n return new NotFoundError('Resource', 'unknown');\n\n case 409: {\n // Parse CAS conflict details if available\n const details = errorBody?.details as { expected?: string; actual?: string } | undefined;\n return new CASConflictError(details?.expected, details?.actual);\n }\n\n default:\n return new ArkeError(message, 'API_ERROR', status, errorBody?.details);\n }\n}\n","/**\n * CID Computation Utility\n *\n * Computes IPFS CIDv1 (base32) for file content.\n * Uses raw codec (0x55) and SHA-256 hash.\n *\n * Note: This module is not used internally by the upload engine (the server\n * computes CIDs from uploaded content). It is exported for convenience in case\n * you want to verify entity CIDs, detect duplicates before upload, or perform\n * other content-addressed operations.\n */\n\nimport { CID } from 'multiformats/cid';\nimport { sha256 } from 'multiformats/hashes/sha2';\nimport * as raw from 'multiformats/codecs/raw';\n\n/**\n * Compute CIDv1 for binary content.\n * Returns base32 encoded string (bafk... prefix for raw codec).\n *\n * @param data - Binary content as ArrayBuffer, Uint8Array, or Blob\n * @returns CIDv1 string in base32 encoding\n *\n * @example\n * ```typescript\n * const cid = await computeCid(new TextEncoder().encode('hello world'));\n * // Returns: \"bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e\"\n * ```\n */\nexport async function computeCid(data: ArrayBuffer | Uint8Array | Blob): Promise<string> {\n // Convert to Uint8Array\n let bytes: Uint8Array;\n\n if (data instanceof Blob) {\n const buffer = await data.arrayBuffer();\n bytes = new Uint8Array(buffer);\n } else if (data instanceof ArrayBuffer) {\n bytes = new Uint8Array(data);\n } else {\n bytes = data;\n }\n\n // Compute SHA-256 hash\n const hash = await sha256.digest(bytes);\n\n // Create CIDv1 with raw codec\n const cid = CID.create(1, raw.code, hash);\n\n // Return base32 encoded string\n return cid.toString();\n}\n\n/**\n * Verify a CID matches the content.\n *\n * @param data - Binary content\n * @param expectedCid - CID to verify against\n * @returns true if CID matches\n */\nexport async function verifyCid(\n data: ArrayBuffer | Uint8Array | Blob,\n expectedCid: string\n): Promise<boolean> {\n const computed = await computeCid(data);\n return computed === expectedCid;\n}\n","/**\n * Upload Engine\n *\n * Core upload implementation optimized for fast tree visibility:\n * 1. Create folders by depth (with unidirectional 'in' → parent)\n * 2. Create file entities (metadata only, high parallelism)\n * 3. Backlink parents with 'contains' relationships\n * → Tree is now browsable! Users can explore structure immediately.\n * 4. Upload file content via POST /files/{id}/content (byte-based pool, ~200MB in flight)\n * - Direct upload to API endpoint\n * - API streams to R2, computes CID, updates entity atomically\n *\n * Byte-based pool (for uploads):\n * - Maintains ~200MB of data in flight at all times\n * - When a file completes, next file starts immediately (no gaps)\n * - Small files: Many upload in parallel\n * - Large files: Fewer upload in parallel (bandwidth-limited)\n * - Single file > 200MB: Uploads alone when pool is empty\n *\n * This minimizes time-to-browse by:\n * - Creating all entities before uploading content\n * - Using unidirectional 'in' relationship on entity creation\n * - Adding all 'contains' relationships in a single PUT per parent\n */\n\nimport type { ArkeClient } from '../../client/ArkeClient.js';\nimport { getAuthorizationHeader } from '../../client/ArkeClient.js';\nimport { ForbiddenError } from '../../client/errors.js';\nimport type { components } from '../../generated/types.js';\nimport { computeCid } from './cid.js';\nimport type {\n UploadTree,\n UploadOptions,\n UploadResult,\n UploadProgress,\n CreatedFolder,\n CreatedFile,\n CreatedEntity,\n UploadFolder,\n} from './types.js';\n\ntype CreateCollectionRequest = components['schemas']['CreateCollectionRequest'];\ntype CreateEntityRequest = components['schemas']['CreateEntityRequest'];\ntype UpdateEntityRequest = components['schemas']['UpdateEntityRequest'];\ntype UpdateCollectionRequest = components['schemas']['UpdateCollectionRequest'];\n\n// Phase constants\nconst PHASE_COUNT = 3; // creating, backlinking, uploading (excluding complete/error)\nconst PHASE_INDEX: Record<string, number> = {\n creating: 0,\n backlinking: 1,\n uploading: 2,\n complete: 3,\n error: -1,\n};\n\n// Batch creation constants\nconst BATCH_SIZE = 100; // Entities per batch request\nconst BATCH_CONCURRENCY = 25; // Concurrent batch requests\nconst BACKLINK_CONCURRENCY = 100; // Concurrent backlink PUTs\nconst MAX_UPLOAD_CONCURRENCY = 500; // Max concurrent upload requests\n\n// =============================================================================\n// Concurrency Utilities\n// =============================================================================\n\n/**\n * Simple concurrency limiter for parallel operations.\n */\nasync function parallelLimit<T, R>(\n items: T[],\n concurrency: number,\n fn: (item: T, index: number) => Promise<R>\n): Promise<R[]> {\n const results: R[] = [];\n let index = 0;\n\n async function worker(): Promise<void> {\n while (index < items.length) {\n const currentIndex = index++;\n const item = items[currentIndex]!;\n results[currentIndex] = await fn(item, currentIndex);\n }\n }\n\n const workers = Array.from({ length: Math.min(concurrency, items.length) }, () => worker());\n await Promise.all(workers);\n\n return results;\n}\n\n/**\n * Split array into chunks of specified size.\n */\nfunction chunk<T>(array: T[], size: number): T[][] {\n const chunks: T[][] = [];\n for (let i = 0; i < array.length; i += size) {\n chunks.push(array.slice(i, i + size));\n }\n return chunks;\n}\n\n// =============================================================================\n// Byte-Based Pool\n// =============================================================================\n\n/** Target bytes in flight (~200MB) */\nconst TARGET_BYTES_IN_FLIGHT = 200 * 1024 * 1024;\n\n/** Threshold for using presigned URLs (5MB) - larger files bypass API worker */\nconst PRESIGNED_URL_THRESHOLD = 5 * 1024 * 1024;\n\n/**\n * Pool that maintains a target number of bytes in flight AND limits concurrent requests.\n *\n * When a file completes, its bytes are released and the next\n * waiting file can start immediately. This keeps the pipeline full.\n *\n * - Small files: Many run in parallel (up to ~200MB worth or maxConcurrent)\n * - Large files: Fewer run in parallel\n * - Single file > 200MB: Runs alone (allowed when pool is empty)\n */\nclass BytePool {\n private bytesInFlight = 0;\n private activeCount = 0;\n private waitQueue: Array<() => void> = [];\n\n constructor(\n private targetBytes: number = TARGET_BYTES_IN_FLIGHT,\n private maxConcurrent: number = MAX_UPLOAD_CONCURRENCY\n ) {}\n\n async run<T>(size: number, fn: () => Promise<T>): Promise<T> {\n // Wait until we have room for BOTH bytes AND request count\n // Exception: if pool is empty, always allow (handles files > targetBytes)\n while (\n (this.bytesInFlight > 0 && this.bytesInFlight + size > this.targetBytes) ||\n this.activeCount >= this.maxConcurrent\n ) {\n await new Promise<void>((resolve) => this.waitQueue.push(resolve));\n }\n\n this.bytesInFlight += size;\n this.activeCount++;\n try {\n return await fn();\n } finally {\n this.bytesInFlight -= size;\n this.activeCount--;\n // Wake ALL waiting tasks so they can re-evaluate the condition.\n // wake-one is incorrect here: if a large file finishes, multiple\n // smaller files may now fit but only the first waiter would check.\n const queue = this.waitQueue.splice(0);\n for (const resolve of queue) resolve();\n }\n }\n}\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Parse folder path to get parent path.\n * e.g., \"docs/images/photos\" -> \"docs/images\"\n */\nfunction getParentPath(relativePath: string): string | null {\n const lastSlash = relativePath.lastIndexOf('/');\n if (lastSlash === -1) return null;\n return relativePath.slice(0, lastSlash);\n}\n\n/**\n * Group folders by depth level.\n */\nfunction groupFoldersByDepth(folders: UploadFolder[]): Map<number, UploadFolder[]> {\n const byDepth = new Map<number, UploadFolder[]>();\n\n for (const folder of folders) {\n const depth = folder.relativePath.split('/').length - 1;\n if (!byDepth.has(depth)) byDepth.set(depth, []);\n byDepth.get(depth)!.push(folder);\n }\n\n return byDepth;\n}\n\n// =============================================================================\n// Main Upload Function\n// =============================================================================\n\n/**\n * Main upload function.\n * Orchestrates the entire upload process with optimized relationship strategy.\n */\nexport async function uploadTree(\n client: ArkeClient,\n tree: UploadTree,\n options: UploadOptions\n): Promise<UploadResult> {\n const { target, onProgress, concurrency = 10, continueOnError = false, maxBytesInFlight, note } = options;\n\n const errors: Array<{ path: string; error: string }> = [];\n const createdFolders: CreatedFolder[] = [];\n const createdFiles: CreatedFile[] = [];\n\n // Maps for tracking (include label for peer_label in relationships)\n const foldersByPath = new Map<string, { id: string; cid: string; label: string }>();\n\n // Calculate totals\n const totalEntities = tree.files.length + tree.folders.length;\n const totalBytes = tree.files.reduce((sum, f) => sum + f.size, 0);\n let completedEntities = 0;\n let bytesUploaded = 0;\n\n // Helper to report progress\n const reportProgress = (progress: Partial<UploadProgress>) => {\n if (onProgress) {\n const phase = progress.phase || 'creating';\n const phaseIndex = PHASE_INDEX[phase] ?? -1;\n\n // Calculate phase percent based on current phase\n let phasePercent = 0;\n if (phase === 'creating') {\n // Creating phase: progress is based on entities created\n const done = progress.completedEntities ?? completedEntities;\n phasePercent = totalEntities > 0 ? Math.round((done / totalEntities) * 100) : 100;\n } else if (phase === 'backlinking') {\n // Backlinking phase: progress is based on parents updated\n const done = progress.completedParents ?? 0;\n const total = progress.totalParents ?? 0;\n phasePercent = total > 0 ? Math.round((done / total) * 100) : 100;\n } else if (phase === 'uploading') {\n // Uploading phase: progress is based on bytes uploaded\n const done = progress.bytesUploaded ?? bytesUploaded;\n phasePercent = totalBytes > 0 ? Math.round((done / totalBytes) * 100) : 100;\n } else if (phase === 'complete') {\n phasePercent = 100;\n }\n\n onProgress({\n phase,\n phaseIndex,\n phaseCount: PHASE_COUNT,\n phasePercent,\n totalEntities,\n completedEntities,\n totalParents: 0,\n completedParents: 0,\n totalBytes,\n bytesUploaded,\n ...progress,\n } as UploadProgress);\n }\n };\n\n try {\n // ─────────────────────────────────────────────────────────────────────────\n // SETUP: Resolve or create collection\n // ─────────────────────────────────────────────────────────────────────────\n let collectionId: string;\n let collectionCid: string;\n let collectionLabel: string;\n let collectionCreated = false;\n\n if (target.createCollection) {\n const collectionBody: CreateCollectionRequest = {\n label: target.createCollection.label,\n description: target.createCollection.description,\n roles: target.createCollection.roles,\n use_roles_default: true,\n note,\n };\n\n const { data, error } = await client.api.POST('/collections', {\n body: collectionBody,\n });\n\n if (error || !data) {\n throw new Error(`Failed to create collection: ${JSON.stringify(error)}`);\n }\n\n collectionId = data.id;\n collectionCid = data.cid;\n collectionLabel = target.createCollection.label;\n collectionCreated = true;\n } else if (target.collectionId) {\n collectionId = target.collectionId;\n\n const { data, error } = await client.api.GET('/collections/{id}', {\n params: { path: { id: collectionId } },\n });\n\n if (error || !data) {\n throw new Error(`Failed to fetch collection: ${JSON.stringify(error)}`);\n }\n\n collectionCid = data.cid;\n collectionLabel = (data.properties?.label as string) ?? collectionId;\n } else {\n throw new Error('Must provide either collectionId or createCollection in target');\n }\n\n // Determine the parent for root-level items\n const rootParentId = target.parentId ?? collectionId;\n let rootParentLabel = collectionLabel;\n let rootParentType: 'collection' | 'folder' = 'collection';\n\n // If a specific parent folder is provided, fetch its label\n if (target.parentId && target.parentId !== collectionId) {\n const { data: parentData, error: parentError } = await client.api.GET('/entities/{id}', {\n params: { path: { id: target.parentId } },\n });\n if (parentError || !parentData) {\n throw new Error(`Failed to fetch parent folder: ${JSON.stringify(parentError)}`);\n }\n rootParentLabel = (parentData.properties?.label as string) ?? target.parentId;\n rootParentType = 'folder';\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // PRE-FLIGHT PERMISSION CHECK\n // Verify we have the required permissions before starting the upload.\n // This fails fast with a clear error instead of failing mid-upload.\n // ─────────────────────────────────────────────────────────────────────────\n\n // Only check permissions if we didn't just create the collection (we'd have all permissions)\n if (!collectionCreated) {\n // Check entity:create permission on the collection\n const { data: collPerms, error: collPermsError } = await client.api.GET(\n '/entities/{id}/permissions',\n { params: { path: { id: collectionId } } }\n );\n\n if (collPermsError || !collPerms) {\n throw new Error(`Failed to check collection permissions: ${JSON.stringify(collPermsError)}`);\n }\n\n if (!collPerms.allowed_actions.includes('entity:create')) {\n throw new ForbiddenError('entity:create', `collection ${collectionId}`);\n }\n\n // If a parent folder is specified (not the collection), check entity:update permission\n // This is required for backlinking (adding 'contains' relationships to the parent)\n if (target.parentId && target.parentId !== collectionId) {\n const { data: parentPerms, error: parentPermsError } = await client.api.GET(\n '/entities/{id}/permissions',\n { params: { path: { id: target.parentId } } }\n );\n\n if (parentPermsError || !parentPerms) {\n throw new Error(`Failed to check parent folder permissions: ${JSON.stringify(parentPermsError)}`);\n }\n\n if (!parentPerms.allowed_actions.includes('entity:update')) {\n throw new ForbiddenError('entity:update', `parent folder ${target.parentId}`);\n }\n }\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // PHASE 1: Create entities (folders by depth, then files)\n // ─────────────────────────────────────────────────────────────────────────\n reportProgress({ phase: 'creating', completedEntities: 0 });\n\n // Group folders by depth\n const foldersByDepth = groupFoldersByDepth(tree.folders);\n const sortedDepths = [...foldersByDepth.keys()].sort((a, b) => a - b);\n\n // Create folders depth by depth (parents before children) using batch endpoint\n for (const depth of sortedDepths) {\n const foldersAtDepth = foldersByDepth.get(depth)!;\n\n // Build batch items for all folders at this depth\n const batchItems = foldersAtDepth.map((folder) => {\n const parentPath = getParentPath(folder.relativePath);\n const parentInfo = parentPath ? foldersByPath.get(parentPath)! : null;\n const parentId = parentInfo ? parentInfo.id : rootParentId;\n const parentType = parentInfo ? 'folder' : rootParentType;\n const parentLabel = parentInfo ? parentInfo.label : rootParentLabel;\n\n return {\n folder, // Track for result processing\n entity: {\n type: 'folder',\n properties: { label: folder.name },\n note,\n relationships: [{ predicate: 'in', peer: parentId, peer_type: parentType, peer_label: parentLabel }],\n },\n };\n });\n\n // Chunk into batches of BATCH_SIZE and create in parallel\n const batches = chunk(batchItems, BATCH_SIZE);\n\n await parallelLimit(batches, BATCH_CONCURRENCY, async (batch) => {\n const { data, error } = await client.api.POST('/entities/batch', {\n params: { query: { validate_relationships: 'false' } },\n body: {\n default_collection: collectionId,\n entities: batch.map((item) => item.entity),\n },\n });\n\n if (error || !data) {\n throw new Error(`Batch folder creation failed: ${JSON.stringify(error)}`);\n }\n\n // Process results - map back to folders by index\n for (const result of data.results) {\n const batchItem = batch[result.index];\n if (!batchItem) continue;\n const folder = batchItem.folder;\n\n if (result.success) {\n // Track folder (include label for peer_label in child relationships)\n foldersByPath.set(folder.relativePath, {\n id: result.id,\n cid: result.cid,\n label: folder.name,\n });\n createdFolders.push({\n name: folder.name,\n relativePath: folder.relativePath,\n id: result.id,\n entityCid: result.cid,\n });\n completedEntities++;\n } else {\n // BatchCreateFailure\n const errorMsg = result.error;\n if (continueOnError) {\n errors.push({ path: folder.relativePath, error: `Folder creation failed: ${errorMsg}` });\n completedEntities++;\n } else {\n throw new Error(`Failed to create folder ${folder.relativePath}: ${errorMsg}`);\n }\n }\n }\n\n reportProgress({ phase: 'creating', completedEntities });\n });\n }\n\n // Create file entities (metadata only, no content upload yet) using batch endpoint\n // Build batch items for all files\n const fileBatchItems = tree.files.map((file) => {\n const parentPath = getParentPath(file.relativePath);\n const parentInfo = parentPath ? foldersByPath.get(parentPath)! : null;\n const parentId = parentInfo ? parentInfo.id : rootParentId;\n const parentType = parentInfo ? 'folder' : rootParentType;\n const parentLabel = parentInfo ? parentInfo.label : rootParentLabel;\n\n return {\n file, // Track for result processing\n entity: {\n type: 'file',\n properties: {\n label: file.name,\n filename: file.name,\n content_type: file.mimeType,\n size: file.size,\n },\n note,\n relationships: [{ predicate: 'in', peer: parentId, peer_type: parentType, peer_label: parentLabel }],\n },\n };\n });\n\n // Chunk into batches of BATCH_SIZE and create in parallel\n const fileBatches = chunk(fileBatchItems, BATCH_SIZE);\n\n await parallelLimit(fileBatches, BATCH_CONCURRENCY, async (batch) => {\n const { data, error } = await client.api.POST('/entities/batch', {\n params: { query: { validate_relationships: 'false' } },\n body: {\n default_collection: collectionId,\n entities: batch.map((item) => item.entity),\n },\n });\n\n if (error || !data) {\n throw new Error(`Batch file creation failed: ${JSON.stringify(error)}`);\n }\n\n // Process results - map back to files by index\n for (const result of data.results) {\n const batchItem = batch[result.index];\n if (!batchItem) continue;\n const file = batchItem.file;\n\n if (result.success) {\n // Track file for later upload\n createdFiles.push({\n ...file,\n id: result.id,\n entityCid: result.cid,\n });\n completedEntities++;\n } else {\n // BatchCreateFailure\n const errorMsg = result.error;\n if (continueOnError) {\n errors.push({ path: file.relativePath, error: errorMsg });\n completedEntities++;\n } else {\n throw new Error(`Failed to create file ${file.relativePath}: ${errorMsg}`);\n }\n }\n }\n\n reportProgress({ phase: 'creating', completedEntities });\n });\n\n // ─────────────────────────────────────────────────────────────────────────\n // PHASE 2: Backlink - Update each parent with 'contains' relationships\n // Uses cached CIDs to avoid GETs, with retry on 409 conflict\n // ─────────────────────────────────────────────────────────────────────────\n\n // Build parent -> children map (include label for peer_label)\n const childrenByParent = new Map<string, Array<{ id: string; type: 'file' | 'folder'; label: string }>>();\n\n // Add folders as children of their parents\n for (const folder of createdFolders) {\n const parentPath = getParentPath(folder.relativePath);\n const parentId = parentPath ? foldersByPath.get(parentPath)!.id : rootParentId;\n\n if (!childrenByParent.has(parentId)) childrenByParent.set(parentId, []);\n childrenByParent.get(parentId)!.push({ id: folder.id, type: 'folder', label: folder.name });\n }\n\n // Add files as children of their parents\n for (const file of createdFiles) {\n const parentPath = getParentPath(file.relativePath);\n const parentId = parentPath ? foldersByPath.get(parentPath)!.id : rootParentId;\n\n if (!childrenByParent.has(parentId)) childrenByParent.set(parentId, []);\n childrenByParent.get(parentId)!.push({ id: file.id, type: 'file', label: file.name });\n }\n\n const totalParents = childrenByParent.size;\n let completedParents = 0;\n\n reportProgress({ phase: 'backlinking', totalParents, completedParents: 0 });\n\n // Update all parents in parallel - each parent gets one PUT with all its children\n // Use cached CIDs from creation phase, only fetch fresh on 409 conflict\n const parentEntries = [...childrenByParent.entries()];\n\n await parallelLimit(parentEntries, BACKLINK_CONCURRENCY, async ([parentId, children]) => {\n try {\n const isCollection = parentId === collectionId;\n\n // Build relationships_add array with all children (include peer_label for display)\n const relationshipsAdd = children.map((child) => ({\n predicate: 'contains' as const,\n peer: child.id,\n peer_type: child.type,\n peer_label: child.label,\n }));\n\n // Get cached CID - no GET required for entities we created\n let expectTip: string;\n if (isCollection) {\n expectTip = collectionCid; // From setup phase\n } else {\n // Check if it's a folder we created (have cached CID)\n const folderInfo = [...foldersByPath.values()].find((f) => f.id === parentId);\n if (folderInfo) {\n expectTip = folderInfo.cid;\n } else {\n // Root parent provided by user - need to fetch tip\n const { data: tipData, error: tipError } = await client.api.GET('/entities/{id}/tip', {\n params: { path: { id: parentId } },\n });\n if (tipError || !tipData) {\n throw new Error(`Failed to get tip: ${JSON.stringify(tipError)}`);\n }\n expectTip = tipData.cid;\n }\n }\n\n // Attempt PUT with cached CID, retry once on 409 conflict\n // Skip relationship validation - we just created these entities\n const attemptPut = async (tip: string, isRetry: boolean): Promise<void> => {\n if (isCollection) {\n const { error, response } = await client.api.PUT('/collections/{id}', {\n params: { path: { id: parentId }, query: { validate_relationships: 'false' } },\n body: {\n expect_tip: tip,\n relationships_add: relationshipsAdd,\n note: note ? `${note} (backlink${isRetry ? ' retry' : ''})` : `Upload backlink${isRetry ? ' retry' : ''}`,\n },\n });\n\n if (error) {\n // Check for CAS conflict (409) - retry with fresh tip\n if (response?.status === 409 && !isRetry) {\n const { data: freshData } = await client.api.GET('/collections/{id}', {\n params: { path: { id: parentId } },\n });\n if (!freshData) throw new Error('Failed to get fresh collection tip');\n return attemptPut(freshData.cid, true);\n }\n throw new Error(JSON.stringify(error));\n }\n } else {\n const { error, response } = await client.api.PUT('/entities/{id}', {\n params: { path: { id: parentId }, query: { validate_relationships: 'false' } },\n body: {\n expect_tip: tip,\n relationships_add: relationshipsAdd,\n note: note ? `${note} (backlink${isRetry ? ' retry' : ''})` : `Upload backlink${isRetry ? ' retry' : ''}`,\n },\n });\n\n if (error) {\n // Check for CAS conflict (409) - retry with fresh tip via fast /tip endpoint\n if (response?.status === 409 && !isRetry) {\n const { data: freshTip, error: tipError } = await client.api.GET('/entities/{id}/tip', {\n params: { path: { id: parentId } },\n });\n if (tipError || !freshTip) throw new Error(`Failed to get fresh tip: ${JSON.stringify(tipError)}`);\n return attemptPut(freshTip.cid, true);\n }\n throw new Error(JSON.stringify(error));\n }\n }\n };\n\n await attemptPut(expectTip, false);\n\n completedParents++;\n reportProgress({\n phase: 'backlinking',\n totalParents,\n completedParents,\n currentItem: `parent:${parentId}`,\n });\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n if (continueOnError) {\n errors.push({ path: `parent:${parentId}`, error: `Backlink failed: ${errorMsg}` });\n completedParents++;\n } else {\n throw new Error(`Failed to backlink parent ${parentId}: ${errorMsg}`);\n }\n }\n });\n\n // ─────────────────────────────────────────────────────────────────────────\n // PHASE 3: Upload file content\n // Tree is now browsable! Users can explore while content uploads.\n //\n // Two paths based on file size:\n // - Small files (<5MB): Direct upload through API (simple, CID computed server-side)\n // - Large files (>=5MB): Presigned URL to R2 (fast, CID computed client-side)\n // ─────────────────────────────────────────────────────────────────────────\n reportProgress({ phase: 'uploading', bytesUploaded: 0 });\n\n // Use byte-based pool to maintain target bytes in flight\n const pool = new BytePool(maxBytesInFlight);\n\n await Promise.all(\n createdFiles.map(async (file) => {\n await pool.run(file.size, async () => {\n try {\n // Get file data\n const fileData = await file.getData();\n\n let body: Blob;\n if (fileData instanceof Blob) {\n body = fileData;\n } else if (fileData instanceof Uint8Array) {\n const arrayBuffer = new ArrayBuffer(fileData.byteLength);\n new Uint8Array(arrayBuffer).set(fileData);\n body = new Blob([arrayBuffer], { type: file.mimeType });\n } else {\n body = new Blob([fileData], { type: file.mimeType });\n }\n\n if (file.size >= PRESIGNED_URL_THRESHOLD) {\n // ─────────────────────────────────────────────────────────────\n // LARGE FILE: Presigned URL flow (bypasses API worker)\n // ─────────────────────────────────────────────────────────────\n\n // 1. Compute CID client-side\n const fileCid = await computeCid(fileData);\n\n // 2. Get presigned URL from API\n const { data: urlData, error: urlError } = await client.api.POST(\n '/entities/{id}/content/upload-url',\n {\n params: { path: { id: file.id } },\n body: {\n cid: fileCid,\n content_type: file.mimeType,\n size: file.size,\n },\n }\n );\n\n if (urlError || !urlData) {\n throw new Error(`Failed to get presigned URL: ${JSON.stringify(urlError)}`);\n }\n\n // 3. PUT directly to R2 (fast!)\n const r2Response = await fetch(urlData.upload_url, {\n method: 'PUT',\n headers: { 'Content-Type': file.mimeType },\n body: body,\n });\n\n if (!r2Response.ok) {\n const errorText = await r2Response.text();\n throw new Error(`R2 upload failed: ${r2Response.status} ${errorText}`);\n }\n\n // 4. Complete upload by updating entity metadata\n const { error: completeError } = await client.api.POST(\n '/entities/{id}/content/complete',\n {\n params: { path: { id: file.id } },\n body: {\n key: 'v1',\n cid: fileCid,\n size: file.size,\n content_type: file.mimeType,\n filename: file.name,\n expect_tip: file.entityCid,\n },\n }\n );\n\n if (completeError) {\n throw new Error(`Failed to complete upload: ${JSON.stringify(completeError)}`);\n }\n } else {\n // ─────────────────────────────────────────────────────────────\n // SMALL FILE: Direct upload through API\n // ─────────────────────────────────────────────────────────────\n\n const { error: uploadError } = await client.api.POST('/entities/{id}/content', {\n params: { path: { id: file.id }, query: { key: 'v1', filename: file.name } },\n body: body as unknown as Record<string, never>,\n bodySerializer: (b: unknown) => b as BodyInit,\n headers: { 'Content-Type': file.mimeType },\n } as Parameters<typeof client.api.POST>[1]);\n\n if (uploadError) {\n throw new Error(`Upload failed: ${JSON.stringify(uploadError)}`);\n }\n }\n\n bytesUploaded += file.size;\n reportProgress({\n phase: 'uploading',\n bytesUploaded,\n currentItem: file.relativePath,\n });\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n if (continueOnError) {\n errors.push({ path: file.relativePath, error: `Upload failed: ${errorMsg}` });\n } else {\n throw new Error(`Failed to upload ${file.relativePath}: ${errorMsg}`);\n }\n }\n });\n })\n );\n\n // ─────────────────────────────────────────────────────────────────────────\n // Complete!\n // ─────────────────────────────────────────────────────────────────────────\n reportProgress({ phase: 'complete', totalParents, completedParents, bytesUploaded });\n\n const resultFolders: CreatedEntity[] = createdFolders.map((f) => ({\n id: f.id,\n cid: f.entityCid,\n type: 'folder' as const,\n relativePath: f.relativePath,\n }));\n\n const resultFiles: CreatedEntity[] = createdFiles.map((f) => ({\n id: f.id,\n cid: f.entityCid,\n type: 'file' as const,\n relativePath: f.relativePath,\n }));\n\n return {\n success: errors.length === 0,\n collection: {\n id: collectionId,\n cid: collectionCid,\n created: collectionCreated,\n },\n folders: resultFolders,\n files: resultFiles,\n errors,\n };\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n\n reportProgress({\n phase: 'error',\n error: errorMsg,\n });\n\n return {\n success: false,\n collection: {\n id: target.collectionId ?? '',\n cid: '',\n created: false,\n },\n folders: createdFolders.map((f) => ({\n id: f.id,\n cid: f.entityCid,\n type: 'folder' as const,\n relativePath: f.relativePath,\n })),\n files: createdFiles.map((f) => ({\n id: f.id,\n cid: f.entityCid,\n type: 'file' as const,\n relativePath: f.relativePath,\n })),\n errors: [...errors, { path: '', error: errorMsg }],\n };\n }\n}\n","/**\n * Platform-specific Scanners\n *\n * Helpers to build UploadTree from different input sources.\n * These are optional utilities - users can also build UploadTree manually.\n */\n\n/// <reference lib=\"dom\" />\n\nimport type { UploadTree, UploadFile, UploadFolder } from './types.js';\n\n/**\n * Detect MIME type from filename.\n * Falls back to 'application/octet-stream' if unknown.\n */\nexport function getMimeType(filename: string): string {\n const ext = filename.toLowerCase().split('.').pop() || '';\n\n const mimeTypes: Record<string, string> = {\n // Images\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n gif: 'image/gif',\n webp: 'image/webp',\n svg: 'image/svg+xml',\n ico: 'image/x-icon',\n bmp: 'image/bmp',\n tiff: 'image/tiff',\n tif: 'image/tiff',\n\n // Documents\n pdf: 'application/pdf',\n doc: 'application/msword',\n docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n xls: 'application/vnd.ms-excel',\n xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n ppt: 'application/vnd.ms-powerpoint',\n pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n odt: 'application/vnd.oasis.opendocument.text',\n ods: 'application/vnd.oasis.opendocument.spreadsheet',\n odp: 'application/vnd.oasis.opendocument.presentation',\n\n // Text\n txt: 'text/plain',\n md: 'text/markdown',\n csv: 'text/csv',\n html: 'text/html',\n htm: 'text/html',\n css: 'text/css',\n xml: 'text/xml',\n rtf: 'application/rtf',\n\n // Code\n js: 'text/javascript',\n mjs: 'text/javascript',\n ts: 'text/typescript',\n jsx: 'text/javascript',\n tsx: 'text/typescript',\n json: 'application/json',\n yaml: 'text/yaml',\n yml: 'text/yaml',\n\n // Archives\n zip: 'application/zip',\n tar: 'application/x-tar',\n gz: 'application/gzip',\n rar: 'application/vnd.rar',\n '7z': 'application/x-7z-compressed',\n\n // Audio\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n ogg: 'audio/ogg',\n m4a: 'audio/mp4',\n flac: 'audio/flac',\n\n // Video\n mp4: 'video/mp4',\n webm: 'video/webm',\n avi: 'video/x-msvideo',\n mov: 'video/quicktime',\n mkv: 'video/x-matroska',\n\n // Fonts\n woff: 'font/woff',\n woff2: 'font/woff2',\n ttf: 'font/ttf',\n otf: 'font/otf',\n\n // Other\n wasm: 'application/wasm',\n };\n\n return mimeTypes[ext] || 'application/octet-stream';\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Browser Scanners\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Scan browser FileSystemEntry objects (from drag-drop or showDirectoryPicker).\n *\n * **Browser only** - uses FileSystemEntry API.\n *\n * @param entries - FileSystemEntry array from DataTransferItem.webkitGetAsEntry()\n * @returns UploadTree ready for upload\n *\n * @example\n * ```typescript\n * // In a drop handler\n * dropzone.ondrop = async (e) => {\n * const entries = Array.from(e.dataTransfer.items)\n * .map(item => item.webkitGetAsEntry())\n * .filter(Boolean);\n *\n * const tree = await scanFileSystemEntries(entries);\n * const result = await uploadTree(client, tree, { target: { collectionId: '...' } });\n * };\n * ```\n */\nexport async function scanFileSystemEntries(\n entries: FileSystemEntry[],\n options: {\n /** Patterns to ignore */\n ignore?: string[];\n } = {}\n): Promise<UploadTree> {\n const { ignore = ['node_modules', '.git', '.DS_Store'] } = options;\n\n const files: UploadFile[] = [];\n const folders: UploadFolder[] = [];\n\n async function processEntry(entry: FileSystemEntry, parentPath: string): Promise<void> {\n const name = entry.name;\n\n // Skip ignored patterns\n if (ignore.some((pattern) => name === pattern)) {\n return;\n }\n\n const relativePath = parentPath ? `${parentPath}/${name}` : name;\n\n if (entry.isFile) {\n const fileEntry = entry as FileSystemFileEntry;\n\n // Get File object from FileSystemFileEntry\n const file = await new Promise<File>((resolve, reject) => {\n fileEntry.file(resolve, reject);\n });\n\n files.push({\n name,\n relativePath,\n size: file.size,\n mimeType: file.type || getMimeType(name),\n getData: async () => file.arrayBuffer(),\n });\n } else if (entry.isDirectory) {\n const dirEntry = entry as FileSystemDirectoryEntry;\n\n folders.push({\n name,\n relativePath,\n });\n\n // Read directory contents\n const reader = dirEntry.createReader();\n const childEntries = await new Promise<FileSystemEntry[]>((resolve, reject) => {\n const allEntries: FileSystemEntry[] = [];\n\n function readEntries() {\n reader.readEntries((entries) => {\n if (entries.length === 0) {\n resolve(allEntries);\n } else {\n allEntries.push(...entries);\n readEntries(); // Continue reading (readEntries returns max 100 at a time)\n }\n }, reject);\n }\n\n readEntries();\n });\n\n // Process children\n for (const childEntry of childEntries) {\n await processEntry(childEntry, relativePath);\n }\n }\n }\n\n // Process all root entries\n for (const entry of entries) {\n if (entry) {\n await processEntry(entry, '');\n }\n }\n\n // Sort folders by depth\n folders.sort((a, b) => a.relativePath.split('/').length - b.relativePath.split('/').length);\n\n return { files, folders };\n}\n\n/**\n * Build UploadTree from a FileList (from <input type=\"file\"> with webkitdirectory).\n *\n * **Browser only** - uses File API.\n *\n * @param fileList - FileList from input element\n * @returns UploadTree ready for upload\n *\n * @example\n * ```typescript\n * <input type=\"file\" webkitdirectory multiple onChange={async (e) => {\n * const tree = await scanFileList(e.target.files);\n * const result = await uploadTree(client, tree, { target: { collectionId: '...' } });\n * }} />\n * ```\n */\nexport async function scanFileList(\n fileList: FileList,\n options: {\n /** Patterns to ignore */\n ignore?: string[];\n } = {}\n): Promise<UploadTree> {\n const { ignore = ['node_modules', '.git', '.DS_Store'] } = options;\n\n const files: UploadFile[] = [];\n const folderPaths = new Set<string>();\n\n for (let i = 0; i < fileList.length; i++) {\n const file = fileList[i];\n if (!file) continue;\n\n // webkitRelativePath gives us the path including the root folder\n const relativePath = file.webkitRelativePath || file.name;\n const name = file.name;\n\n // Skip ignored patterns (check each path segment)\n const pathSegments = relativePath.split('/');\n if (pathSegments.some((segment: string) => ignore.includes(segment))) {\n continue;\n }\n\n // Extract folder paths\n const pathParts = relativePath.split('/');\n for (let j = 1; j < pathParts.length; j++) {\n const folderPath = pathParts.slice(0, j).join('/');\n folderPaths.add(folderPath);\n }\n\n // Capture file reference for closure\n const fileRef = file;\n files.push({\n name,\n relativePath,\n size: fileRef.size,\n mimeType: fileRef.type || getMimeType(name),\n getData: async () => fileRef.arrayBuffer(),\n });\n }\n\n // Convert folder paths to UploadFolder objects\n const folders: UploadFolder[] = Array.from(folderPaths)\n .map((path) => ({\n name: path.split('/').pop()!,\n relativePath: path,\n }))\n .sort((a, b) => a.relativePath.split('/').length - b.relativePath.split('/').length);\n\n return { files, folders };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Utility: Build tree from flat file list\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Build an UploadTree from a flat array of files with paths.\n *\n * Useful for programmatically constructing uploads without filesystem access.\n *\n * @param items - Array of file items with path, data, and optional metadata\n * @returns UploadTree ready for upload\n *\n * @example\n * ```typescript\n * const tree = buildUploadTree([\n * { path: 'docs/readme.md', data: new Blob(['# Hello']) },\n * { path: 'docs/images/logo.png', data: logoBlob },\n * ]);\n * ```\n */\nexport function buildUploadTree(\n items: Array<{\n /** Relative path (e.g., \"docs/readme.md\") */\n path: string;\n /** File data */\n data: Blob | ArrayBuffer | Uint8Array;\n /** MIME type (auto-detected if not provided) */\n mimeType?: string;\n }>\n): UploadTree {\n const files: UploadFile[] = [];\n const folderPaths = new Set<string>();\n\n for (const item of items) {\n const pathParts = item.path.split('/');\n const name = pathParts.pop()!;\n\n // Extract folder paths\n for (let i = 1; i <= pathParts.length; i++) {\n folderPaths.add(pathParts.slice(0, i).join('/'));\n }\n\n // Determine size\n let size: number;\n if (item.data instanceof Blob) {\n size = item.data.size;\n } else if (item.data instanceof ArrayBuffer) {\n size = item.data.byteLength;\n } else {\n size = item.data.length;\n }\n\n files.push({\n name,\n relativePath: item.path,\n size,\n mimeType: item.mimeType || getMimeType(name),\n getData: async () => item.data,\n });\n }\n\n const folders: UploadFolder[] = Array.from(folderPaths)\n .map((path) => ({\n name: path.split('/').pop()!,\n relativePath: path,\n }))\n .sort((a, b) => a.relativePath.split('/').length - b.relativePath.split('/').length);\n\n return { files, folders };\n}\n","/**\n * Upload to Entity Helper\n *\n * Upload one or more files to an existing entity.\n * Handles presigned URL flow with parallel uploads and sequential CAS completion.\n */\n\nimport type { ArkeClient } from '../../client/ArkeClient.js';\nimport { computeCid } from './cid.js';\nimport { getMimeType } from './scanners.js';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A single file to upload to an entity.\n */\nexport interface UploadItem {\n /** Content key. Defaults to filename without extension, or \"file_{cid_hash}\" if no filename. */\n key?: string;\n /** File data */\n data: File | Blob | ArrayBuffer | Uint8Array;\n /** MIME type (auto-detected from filename if not provided) */\n contentType?: string;\n /** Original filename for Content-Disposition on download */\n filename?: string;\n}\n\n/**\n * Progress information during upload to entity.\n */\nexport interface UploadToEntityProgress {\n /** Current phase */\n phase: 'preparing' | 'uploading' | 'completing';\n /** Total bytes across all files */\n totalBytes: number;\n /** Bytes uploaded so far */\n uploadedBytes: number;\n /** Number of files completed */\n completedFiles: number;\n /** Total number of files */\n totalFiles: number;\n}\n\n/**\n * Options for uploading to an entity.\n */\nexport interface UploadToEntityOptions {\n /** Progress callback */\n onProgress?: (progress: UploadToEntityProgress) => void;\n}\n\n/**\n * Result for a single uploaded file.\n */\nexport interface UploadContentResult {\n /** Content version key */\n key: string;\n /** Content CID */\n cid: string;\n /** File size in bytes */\n size: number;\n /** MIME type */\n contentType: string;\n /** Original filename */\n filename?: string;\n}\n\n/**\n * Result of uploading to an entity.\n */\nexport interface UploadToEntityResult {\n /** Entity ID */\n id: string;\n /** Final entity CID after all uploads */\n cid: string;\n /** Entity CID before first upload */\n prevCid: string;\n /** Results for each uploaded file */\n contents: UploadContentResult[];\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Internal Types\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface PreparedItem {\n key: string;\n bytes: ArrayBuffer;\n size: number;\n contentType: string;\n filename?: string;\n cid: string;\n}\n\ninterface UploadedItem extends PreparedItem {\n uploadUrl: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Constants\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst MAX_CAS_RETRIES = 3;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Main Function\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Upload one or more files to an existing entity.\n *\n * Files are uploaded in parallel for speed, then metadata is committed\n * sequentially with CAS protection to ensure consistency.\n *\n * @example\n * ```typescript\n * // Single file\n * const result = await uploadToEntity(client, entityId, [\n * { key: 'v1', data: file }\n * ]);\n *\n * // Multiple files (e.g., original + thumbnail)\n * const result = await uploadToEntity(client, entityId, [\n * { key: 'original', data: originalFile },\n * { key: 'thumbnail', data: thumbnailBlob },\n * { key: 'preview', data: previewBlob },\n * ]);\n *\n * // With progress tracking\n * const result = await uploadToEntity(client, entityId, items, {\n * onProgress: (p) => console.log(`${p.phase}: ${p.uploadedBytes}/${p.totalBytes}`),\n * });\n * ```\n */\nexport async function uploadToEntity(\n client: ArkeClient,\n entityId: string,\n items: UploadItem[],\n options: UploadToEntityOptions = {}\n): Promise<UploadToEntityResult> {\n if (items.length === 0) {\n throw new Error('At least one upload item is required');\n }\n\n const { onProgress } = options;\n\n // Progress state\n let progressState: UploadToEntityProgress = {\n phase: 'preparing',\n totalBytes: 0,\n uploadedBytes: 0,\n completedFiles: 0,\n totalFiles: items.length,\n };\n\n const reportProgress = (update: Partial<UploadToEntityProgress>) => {\n progressState = { ...progressState, ...update };\n onProgress?.(progressState);\n };\n\n // ─────────────────────────────────────────────────────────────────────────\n // PHASE 1: Prepare all files (parallel)\n // - Convert to ArrayBuffer\n // - Determine size, contentType, filename\n // - Compute CID\n // ─────────────────────────────────────────────────────────────────────────\n reportProgress({ phase: 'preparing' });\n\n const prepared: PreparedItem[] = await Promise.all(\n items.map((item) => prepareItem(item))\n );\n\n // Check for duplicate keys\n const keys = prepared.map((p) => p.key);\n const duplicates = keys.filter((key, i) => keys.indexOf(key) !== i);\n if (duplicates.length > 0) {\n const uniqueDupes = [...new Set(duplicates)];\n throw new Error(\n `Duplicate content keys detected: ${uniqueDupes.map((k) => `\"${k}\"`).join(', ')}. ` +\n `Each file must have a unique key. Provide explicit keys to resolve.`\n );\n }\n\n const totalBytes = prepared.reduce((sum, p) => sum + p.size, 0);\n reportProgress({ totalBytes });\n\n // ─────────────────────────────────────────────────────────────────────────\n // PHASE 2: Get presigned URLs (parallel, no tip needed)\n // ─────────────────────────────────────────────────────────────────────────\n const uploadInfos: UploadedItem[] = await Promise.all(\n prepared.map(async (item) => {\n const { data: presigned, error } = await client.api.POST(\n '/entities/{id}/content/upload-url',\n {\n params: { path: { id: entityId } },\n body: {\n cid: item.cid,\n content_type: item.contentType,\n size: item.size,\n },\n }\n );\n\n if (error || !presigned) {\n throw new Error(\n `Failed to get upload URL for ${item.key}: ${JSON.stringify(error)}`\n );\n }\n\n return {\n ...item,\n uploadUrl: presigned.upload_url,\n };\n })\n );\n\n // ─────────────────────────────────────────────────────────────────────────\n // PHASE 3: Upload to R2 (parallel - the slow part)\n // ─────────────────────────────────────────────────────────────────────────\n reportProgress({ phase: 'uploading', uploadedBytes: 0 });\n\n let uploadedBytes = 0;\n await Promise.all(\n uploadInfos.map(async (item) => {\n const response = await fetch(item.uploadUrl, {\n method: 'PUT',\n headers: { 'Content-Type': item.contentType },\n body: item.bytes,\n });\n\n if (!response.ok) {\n throw new Error(\n `Upload to R2 failed for ${item.key}: ${response.statusText}`\n );\n }\n\n uploadedBytes += item.size;\n reportProgress({ uploadedBytes });\n })\n );\n\n // ─────────────────────────────────────────────────────────────────────────\n // PHASE 4: Get current tip\n // ─────────────────────────────────────────────────────────────────────────\n const { data: tipData, error: tipError } = await client.api.GET(\n '/entities/{id}/tip',\n { params: { path: { id: entityId } } }\n );\n\n if (tipError || !tipData) {\n throw new Error(`Failed to get entity tip: ${JSON.stringify(tipError)}`);\n }\n\n const prevCid = tipData.cid;\n let currentTip = prevCid;\n\n // ─────────────────────────────────────────────────────────────────────────\n // PHASE 5: Complete uploads (sequential with CAS retry)\n // ─────────────────────────────────────────────────────────────────────────\n reportProgress({ phase: 'completing', completedFiles: 0 });\n\n const contents: UploadContentResult[] = [];\n let finalCid = currentTip;\n\n for (let i = 0; i < uploadInfos.length; i++) {\n const item = uploadInfos[i]!;\n const result = await completeWithRetry(client, entityId, item, currentTip);\n\n currentTip = result.cid; // Use new tip for next complete\n finalCid = result.cid;\n\n contents.push({\n key: item.key,\n cid: result.contentCid,\n size: item.size,\n contentType: item.contentType,\n filename: item.filename,\n });\n\n reportProgress({ completedFiles: i + 1 });\n }\n\n return {\n id: entityId,\n cid: finalCid,\n prevCid,\n contents,\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Prepare an upload item: convert to bytes, detect metadata, compute CID.\n */\nasync function prepareItem(item: UploadItem): Promise<PreparedItem> {\n const { data } = item;\n\n // Convert to ArrayBuffer\n let bytes: ArrayBuffer;\n if (data instanceof Blob) {\n bytes = await data.arrayBuffer();\n } else if (data instanceof ArrayBuffer) {\n bytes = data;\n } else {\n // Uint8Array - copy to handle SharedArrayBuffer\n const buffer = new ArrayBuffer(data.byteLength);\n new Uint8Array(buffer).set(data);\n bytes = buffer;\n }\n\n // Determine properties\n const size = bytes.byteLength;\n let filename = item.filename;\n let contentType = item.contentType;\n\n if (data instanceof File) {\n filename = filename ?? data.name;\n contentType = contentType ?? (data.type || getMimeType(data.name));\n }\n\n contentType = contentType ?? 'application/octet-stream';\n if (contentType === 'application/octet-stream' && filename) {\n contentType = getMimeType(filename);\n }\n\n // Compute CID\n const cid = await computeCid(new Uint8Array(bytes));\n\n // Determine key: explicit > filename without extension > cid-based\n let key = item.key;\n if (!key && filename) {\n // Strip extension: \"document.pdf\" -> \"document\"\n const lastDot = filename.lastIndexOf('.');\n key = lastDot > 0 ? filename.substring(0, lastDot) : filename;\n }\n if (!key) {\n // Use last 8 chars of CID for uniqueness\n key = `file_${cid.slice(-8)}`;\n }\n\n return { key, bytes, size, contentType, filename, cid };\n}\n\n/**\n * Complete an upload with CAS retry on conflict.\n */\nasync function completeWithRetry(\n client: ArkeClient,\n entityId: string,\n item: UploadedItem,\n expectTip: string\n): Promise<{ cid: string; contentCid: string }> {\n let tip = expectTip;\n\n for (let attempt = 0; attempt < MAX_CAS_RETRIES; attempt++) {\n const { data, error, response } = await client.api.POST(\n '/entities/{id}/content/complete',\n {\n params: { path: { id: entityId } },\n body: {\n key: item.key,\n cid: item.cid,\n size: item.size,\n content_type: item.contentType,\n filename: item.filename,\n expect_tip: tip,\n },\n }\n );\n\n if (data) {\n return { cid: data.cid, contentCid: data.content.cid };\n }\n\n // Check for CAS conflict (409)\n if (response?.status === 409) {\n // Refresh tip and retry\n const { data: freshTip, error: tipError } = await client.api.GET(\n '/entities/{id}/tip',\n { params: { path: { id: entityId } } }\n );\n\n if (tipError || !freshTip) {\n throw new Error(`Failed to refresh tip: ${JSON.stringify(tipError)}`);\n }\n\n tip = freshTip.cid;\n continue;\n }\n\n // Other error - throw immediately\n throw new Error(\n `Failed to complete upload for ${item.key}: ${JSON.stringify(error)}`\n );\n }\n\n throw new Error(`Max CAS retries exceeded for ${item.key}`);\n}\n","/**\n * Folder Operations (Legacy)\n *\n * @deprecated Use the new upload module instead:\n * ```typescript\n * import { uploadTree, buildUploadTree } from '@arke-institute/sdk/operations';\n *\n * const tree = buildUploadTree([\n * { path: 'docs/readme.md', data: readmeBuffer },\n * { path: 'images/logo.png', data: logoBlob },\n * ]);\n * const result = await uploadTree(client, tree, {\n * target: { collectionId: '...' },\n * });\n * ```\n */\n\nimport type { ArkeClient } from '../client/ArkeClient.js';\n\n/**\n * @deprecated Use UploadProgress from upload module\n */\nexport interface UploadProgress {\n phase: 'scanning' | 'creating-folders' | 'uploading-files' | 'linking' | 'complete';\n totalFiles: number;\n completedFiles: number;\n totalFolders: number;\n completedFolders: number;\n currentFile?: string;\n}\n\n/**\n * @deprecated Use UploadOptions from upload module\n */\nexport interface UploadDirectoryOptions {\n /** Collection to upload into */\n collectionId: string;\n /** Parent folder ID (optional - creates at root if not provided) */\n parentFolderId?: string;\n /** Progress callback */\n onProgress?: (progress: UploadProgress) => void;\n /** Max concurrent uploads */\n concurrency?: number;\n}\n\n/**\n * @deprecated Use UploadResult from upload module\n */\nexport interface UploadDirectoryResult {\n /** Root folder entity */\n rootFolder: unknown;\n /** All created folder entities */\n folders: unknown[];\n /** All created file entities */\n files: unknown[];\n}\n\n/**\n * Folder operations helper\n *\n * @deprecated Use uploadTree and buildUploadTree functions instead:\n * ```typescript\n * import { uploadTree, buildUploadTree } from '@arke-institute/sdk/operations';\n *\n * const tree = buildUploadTree([\n * { path: 'docs/readme.md', data: readmeBuffer },\n * ]);\n * const result = await uploadTree(client, tree, {\n * target: { collectionId: '...' },\n * });\n * ```\n */\nexport class FolderOperations {\n constructor(private client: ArkeClient) {\n void client; // Suppress unused warning\n }\n\n /**\n * Upload a local directory to Arke\n *\n * @deprecated This method has been removed. Use uploadTree and buildUploadTree instead.\n */\n async uploadDirectory(\n _localPath: string,\n _options: UploadDirectoryOptions\n ): Promise<UploadDirectoryResult> {\n throw new Error(\n 'FolderOperations.uploadDirectory has been removed. ' +\n 'Use uploadTree() with buildUploadTree() instead. ' +\n 'See: https://github.com/arke-institute/arke-sdk#upload-module'\n );\n }\n}\n","/**\n * Batch Operations\n *\n * High-level operations for bulk entity and relationship management.\n *\n * TODO: Implement batch operations\n * - createEntities: Create multiple entities in parallel\n * - updateEntities: Update multiple entities in parallel\n * - createRelationships: Create multiple relationships in parallel\n */\n\nimport type { ArkeClient } from '../client/ArkeClient.js';\n\nexport interface BatchCreateOptions {\n /** Max concurrent operations */\n concurrency?: number;\n /** Continue on individual failures */\n continueOnError?: boolean;\n /** Progress callback */\n onProgress?: (completed: number, total: number) => void;\n}\n\nexport interface BatchResult<T> {\n /** Successfully completed operations */\n succeeded: T[];\n /** Failed operations with errors */\n failed: Array<{ input: unknown; error: Error }>;\n}\n\n/**\n * Batch operations helper\n *\n * @example\n * ```typescript\n * const batch = new BatchOperations(arkeClient);\n * const result = await batch.createEntities([\n * { type: 'document', properties: { title: 'Doc 1' } },\n * { type: 'document', properties: { title: 'Doc 2' } },\n * ], { concurrency: 5 });\n * ```\n */\nexport class BatchOperations {\n constructor(private client: ArkeClient) {}\n\n /**\n * Create multiple entities in parallel\n *\n * TODO: Implement this method\n */\n async createEntities(\n _entities: Array<{\n collectionId: string;\n type: string;\n properties?: Record<string, unknown>;\n }>,\n _options?: BatchCreateOptions\n ): Promise<BatchResult<unknown>> {\n throw new Error('BatchOperations.createEntities is not yet implemented');\n }\n\n /**\n * Create multiple relationships in parallel\n *\n * TODO: Implement this method\n */\n async createRelationships(\n _relationships: Array<{\n sourceId: string;\n targetId: string;\n predicate: string;\n bidirectional?: boolean;\n properties?: Record<string, unknown>;\n }>,\n _options?: BatchCreateOptions\n ): Promise<BatchResult<unknown>> {\n throw new Error('BatchOperations.createRelationships is not yet implemented');\n }\n}\n","/**\n * Crypto Operations\n *\n * Cryptographic utilities for agents and content addressing.\n *\n * TODO: Implement crypto operations\n * - generateKeyPair: Generate Ed25519 key pair for agent authentication\n * - signPayload: Sign a payload with agent private key\n * - computeCID: Compute IPFS CID for content\n */\n\n/**\n * Ed25519 key pair for agent authentication\n */\nexport interface KeyPair {\n /** Public key in base64 */\n publicKey: string;\n /** Private key in base64 (keep secret!) */\n privateKey: string;\n}\n\n/**\n * Signed payload with signature\n */\nexport interface SignedPayload {\n /** Original payload */\n payload: string;\n /** Ed25519 signature in base64 */\n signature: string;\n /** Timestamp of signature */\n timestamp: number;\n}\n\n/**\n * Crypto operations helper\n *\n * @example\n * ```typescript\n * // Generate key pair for a new agent\n * const { publicKey, privateKey } = await CryptoOperations.generateKeyPair();\n *\n * // Sign a payload\n * const signed = await CryptoOperations.signPayload(privateKey, payload);\n * ```\n */\nexport class CryptoOperations {\n /**\n * Generate an Ed25519 key pair for agent authentication\n *\n * TODO: Implement using Node.js crypto or Web Crypto API\n */\n static async generateKeyPair(): Promise<KeyPair> {\n throw new Error('CryptoOperations.generateKeyPair is not yet implemented');\n }\n\n /**\n * Sign a payload with an Ed25519 private key\n *\n * TODO: Implement signature generation\n */\n static async signPayload(_privateKey: string, _payload: string): Promise<SignedPayload> {\n throw new Error('CryptoOperations.signPayload is not yet implemented');\n }\n\n /**\n * Verify an Ed25519 signature\n *\n * TODO: Implement signature verification\n */\n static async verifySignature(\n _publicKey: string,\n _payload: string,\n _signature: string\n ): Promise<boolean> {\n throw new Error('CryptoOperations.verifySignature is not yet implemented');\n }\n\n /**\n * Compute IPFS CID for content\n *\n * TODO: Implement using multiformats library\n */\n static async computeCID(_content: Uint8Array): Promise<string> {\n throw new Error('CryptoOperations.computeCID is not yet implemented');\n }\n}\n","/**\n * CAS (Compare-And-Swap) retry utility for concurrent updates\n *\n * Use this for additive operations where your update doesn't depend on\n * current state - you just need the tip for CAS validation.\n */\n\nimport { CASConflictError } from '../client/errors.js';\n\n/**\n * Configuration for CAS retry behavior\n */\nexport interface CasRetryOptions {\n /**\n * Expected number of concurrent writers - affects retry timing.\n * Higher concurrency = more initial spread and more retry headroom.\n * @default 10\n */\n concurrency?: number;\n\n /**\n * Override: maximum retry attempts.\n * If not specified, calculated from concurrency.\n */\n maxAttempts?: number;\n\n /**\n * Base delay between retries in ms.\n * Actual delay grows exponentially with jitter.\n * @default 50\n */\n baseDelayMs?: number;\n\n /**\n * Maximum delay between retries in ms (caps exponential growth).\n * @default 10000\n */\n maxDelayMs?: number;\n\n /**\n * Add initial random delay before first attempt to spread concurrent requests.\n * Delay range: 0 to (concurrency * 100)ms. E.g., 500 concurrent = 0-50s spread.\n * This dramatically reduces collisions by staggering first attempts.\n * @default true\n */\n spreadInitial?: boolean;\n\n /**\n * Called before each retry attempt.\n * Useful for logging or monitoring.\n */\n onRetry?: (attempt: number, error: CASConflictError, delayMs: number) => void;\n}\n\n/**\n * Result of a successful CAS retry operation\n */\nexport interface CasRetryResult<T> {\n /** The successful response data */\n data: T;\n /** Number of attempts made (1 = succeeded first try) */\n attempts: number;\n}\n\n/**\n * Callbacks for CAS retry operation\n */\nexport interface CasRetryCallbacks<T> {\n /** Get the current tip/CID - called before each attempt */\n getTip: () => Promise<string>;\n /** Perform the update with given tip - return {data, error} like openapi-fetch */\n update: (tip: string) => Promise<{ data?: T; error?: unknown }>;\n}\n\n/**\n * Default configuration values\n */\nexport const DEFAULT_CAS_RETRY_CONFIG = {\n concurrency: 10,\n baseDelayMs: 50,\n maxDelayMs: 10000, // 10 seconds - allows more spread at high concurrency\n} as const;\n\n/**\n * Calculate max attempts from concurrency level.\n * Formula: max(5, ceil(log2(concurrency) * 4) + floor(concurrency / 100))\n *\n * The logarithmic component handles the base scaling, while the linear\n * component adds extra headroom at very high concurrency levels.\n *\n * Examples:\n * - concurrency 10 → 14 attempts\n * - concurrency 100 → 28 attempts\n * - concurrency 500 → 41 attempts\n * - concurrency 1000 → 50 attempts\n */\nexport function calculateMaxAttempts(concurrency: number): number {\n const logComponent = Math.ceil(Math.log2(Math.max(2, concurrency)) * 4);\n const linearComponent = Math.floor(concurrency / 100);\n return Math.max(5, logComponent + linearComponent);\n}\n\n/**\n * Calculate delay with exponential backoff and heavy jitter.\n * Jitter is 0-100% of the base delay to spread concurrent retries.\n *\n * Formula: min(baseDelay * 1.5^attempt + random(0, baseDelay * 1.5^attempt), maxDelay)\n */\nexport function calculateCasDelay(\n attempt: number,\n baseDelayMs: number,\n maxDelayMs: number\n): number {\n // Exponential backoff: baseDelay * 1.5^attempt\n const exponentialDelay = baseDelayMs * Math.pow(1.5, attempt);\n\n // Cap at maxDelay before adding jitter\n const cappedDelay = Math.min(exponentialDelay, maxDelayMs);\n\n // Heavy jitter: 0-100% of the delay (critical for spreading concurrent retries)\n const jitter = Math.random() * cappedDelay;\n\n return Math.floor(cappedDelay + jitter);\n}\n\n/**\n * Check if an error is a CAS conflict.\n * Handles multiple error shapes:\n * - openapi-fetch: { status: 409, ... }\n * - API error body: { error: \"CAS failure: ...\" }\n */\nexport function isCasConflictError(error: unknown): boolean {\n if (error === null || error === undefined) {\n return false;\n }\n\n if (typeof error !== 'object') {\n return false;\n }\n\n // Check for status 409 (openapi-fetch error shape)\n if ('status' in error && (error as { status: unknown }).status === 409) {\n return true;\n }\n\n // Check for error message pattern (API returns CAS failures this way)\n if ('error' in error) {\n const errorMsg = (error as { error: unknown }).error;\n if (typeof errorMsg === 'string' && errorMsg.startsWith('CAS failure:')) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Sleep for a specified duration\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Error thrown when CAS retries are exhausted\n */\nexport class CasRetryExhaustedError extends Error {\n constructor(\n public readonly attempts: number,\n public readonly lastError: CASConflictError\n ) {\n super(\n `CAS update failed after ${attempts} attempts. ` +\n `Expected tip: ${lastError.expectedTip}, actual: ${lastError.actualTip}`\n );\n this.name = 'CasRetryExhaustedError';\n Error.captureStackTrace?.(this, this.constructor);\n }\n}\n\n/**\n * Wrap a CAS update operation with automatic retry on conflicts.\n *\n * Use this for additive operations where your update doesn't depend on\n * current state - you just need the tip for CAS validation.\n *\n * @example\n * ```typescript\n * const { data, attempts } = await withCasRetry({\n * getTip: async () => {\n * const { data } = await client.api.GET('/entities/{id}/tip', {\n * params: { path: { id: entityId } }\n * });\n * return data!.cid;\n * },\n * update: async (tip) => {\n * return client.api.PUT('/entities/{id}', {\n * params: { path: { id: entityId } },\n * body: {\n * expect_tip: tip,\n * relationships_add: [{ predicate: 'contains', peer: childId }]\n * }\n * });\n * }\n * }, { concurrency: 100 });\n * ```\n *\n * @param callbacks.getTip - Function to fetch the current tip/CID\n * @param callbacks.update - Function to perform the update with the tip\n * @param options - Retry configuration\n * @returns The successful result with attempt count\n * @throws {CasRetryExhaustedError} When all retries are exhausted\n * @throws {Error} For non-CAS errors (not retried)\n */\nexport async function withCasRetry<T>(\n callbacks: CasRetryCallbacks<T>,\n options?: CasRetryOptions\n): Promise<CasRetryResult<T>> {\n const {\n concurrency = DEFAULT_CAS_RETRY_CONFIG.concurrency,\n maxAttempts: maxAttemptsOverride,\n baseDelayMs = DEFAULT_CAS_RETRY_CONFIG.baseDelayMs,\n maxDelayMs = DEFAULT_CAS_RETRY_CONFIG.maxDelayMs,\n spreadInitial = true,\n onRetry,\n } = options ?? {};\n\n const maxAttempts = maxAttemptsOverride ?? calculateMaxAttempts(concurrency);\n\n // Initial spread: random delay before first attempt to reduce initial collisions\n // Range: 0 to (concurrency * 100)ms - e.g., 500 concurrent = 0-50s spread\n // This spreads first attempts over time, dramatically reducing collisions\n if (spreadInitial && concurrency > 1) {\n const initialSpread = Math.floor(Math.random() * concurrency * 100);\n await sleep(initialSpread);\n }\n\n let lastCasError: CASConflictError | undefined;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n // Get fresh tip before each attempt\n const tip = await callbacks.getTip();\n\n // Attempt the update\n const { data, error } = await callbacks.update(tip);\n\n // Success!\n if (!error && data !== undefined) {\n return { data, attempts: attempt };\n }\n\n // Check if it's a CAS conflict\n if (isCasConflictError(error)) {\n // Parse the error details from various formats\n let expectedTip: string | undefined;\n let actualTip: string | undefined;\n\n const errorObj = error as Record<string, unknown>;\n\n // Format 1: { details: { expected, actual } }\n if (errorObj.details && typeof errorObj.details === 'object') {\n const details = errorObj.details as { expected?: string; actual?: string };\n expectedTip = details.expected;\n actualTip = details.actual;\n }\n\n // Format 2: { error: \"CAS failure: expected tip X, got Y\" }\n if (!expectedTip && typeof errorObj.error === 'string') {\n const match = errorObj.error.match(/expected tip (\\S+), got (\\S+)/);\n if (match) {\n expectedTip = match[1];\n actualTip = match[2];\n }\n }\n\n lastCasError = new CASConflictError(expectedTip, actualTip);\n\n // If we have more attempts, wait and retry\n if (attempt < maxAttempts) {\n const delay = calculateCasDelay(attempt - 1, baseDelayMs, maxDelayMs);\n onRetry?.(attempt, lastCasError, delay);\n await sleep(delay);\n continue;\n }\n } else {\n // Non-CAS error - throw immediately, don't retry\n throw new Error(\n `Update failed with non-CAS error: ${JSON.stringify(error)}`\n );\n }\n }\n\n // Exhausted all attempts\n throw new CasRetryExhaustedError(\n maxAttempts,\n lastCasError ?? new CASConflictError()\n );\n}\n"],"mappings":"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","names":["code","entries"]}
@@ -189,6 +189,7 @@ async function uploadTree(client, tree, options) {
189
189
  label: target.createCollection.label,
190
190
  description: target.createCollection.description,
191
191
  roles: target.createCollection.roles,
192
+ use_roles_default: true,
192
193
  note
193
194
  };
194
195
  const { data, error } = await client.api.POST("/collections", {