@arke-institute/sdk 2.2.0 → 2.3.1

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,5 +1,5 @@
1
- import { A as ArkeClient, j as UploadTree, k as UploadOptions, l as UploadResult } from '../crypto-7c990p-j.js';
2
- export { f as BatchCreateOptions, B as BatchOperations, h as BatchResult, p as CreatedEntity, C as CryptoOperations, F as FolderOperations, K as KeyPair, S as SignedPayload, d as UploadDirectoryOptions, e as UploadDirectoryResult, m as UploadFile, n as UploadFolder, U as UploadProgress, o as UploadTarget } from '../crypto-7c990p-j.js';
1
+ import { A as ArkeClient, j as UploadTree, k as UploadOptions, l as UploadResult } from '../crypto-Dz2ktRy4.js';
2
+ export { f as BatchCreateOptions, B as BatchOperations, h as BatchResult, p as CreatedEntity, C as CryptoOperations, F as FolderOperations, K as KeyPair, S as SignedPayload, d as UploadDirectoryOptions, e as UploadDirectoryResult, m as UploadFile, n as UploadFolder, U as UploadProgress, o as UploadTarget } from '../crypto-Dz2ktRy4.js';
3
3
  import 'openapi-fetch';
4
4
  import '../generated/index.js';
5
5
 
@@ -7,17 +7,15 @@ import '../generated/index.js';
7
7
  * Upload Engine
8
8
  *
9
9
  * Core upload implementation optimized for fast tree visibility:
10
- * 1. Compute CIDs for all files (high parallelism)
11
- * 2. Create folders by depth (with unidirectional 'in' → parent)
12
- * 3. Create file entities (metadata only, high parallelism)
13
- * 4. Backlink parents with 'contains' relationships
10
+ * 1. Create folders by depth (with unidirectional 'in' → parent)
11
+ * 2. Create file entities (metadata only, high parallelism)
12
+ * 3. Backlink parents with 'contains' relationships
14
13
  * → Tree is now browsable! Users can explore structure immediately.
15
- * 5. Upload file content to S3 + confirm (byte-based pool, ~200MB in flight)
16
- * - PUT to presigned S3 URL
17
- * - POST /files/{id}/confirm-upload to set uploaded: true
18
- * - Auto-retry on CAS conflict (entity may have changed during upload)
14
+ * 4. Upload file content via POST /files/{id}/content (byte-based pool, ~200MB in flight)
15
+ * - Direct upload to API endpoint
16
+ * - API streams to R2, computes CID, updates entity atomically
19
17
  *
20
- * Byte-based pool (for S3 uploads):
18
+ * Byte-based pool (for uploads):
21
19
  * - Maintains ~200MB of data in flight at all times
22
20
  * - When a file completes, next file starts immediately (no gaps)
23
21
  * - Small files: Many upload in parallel
@@ -41,6 +39,11 @@ declare function uploadTree(client: ArkeClient, tree: UploadTree, options: Uploa
41
39
  *
42
40
  * Computes IPFS CIDv1 (base32) for file content.
43
41
  * Uses raw codec (0x55) and SHA-256 hash.
42
+ *
43
+ * Note: This module is not used internally by the upload engine (the server
44
+ * computes CIDs from uploaded content). It is exported for convenience in case
45
+ * you want to verify entity CIDs, detect duplicates before upload, or perform
46
+ * other content-addressed operations.
44
47
  */
45
48
  /**
46
49
  * Compute CIDv1 for binary content.
@@ -1,35 +1,11 @@
1
- // src/operations/upload/cid.ts
2
- import { CID } from "multiformats/cid";
3
- import { sha256 } from "multiformats/hashes/sha2";
4
- import * as raw from "multiformats/codecs/raw";
5
- async function computeCid(data) {
6
- let bytes;
7
- if (data instanceof Blob) {
8
- const buffer = await data.arrayBuffer();
9
- bytes = new Uint8Array(buffer);
10
- } else if (data instanceof ArrayBuffer) {
11
- bytes = new Uint8Array(data);
12
- } else {
13
- bytes = data;
14
- }
15
- const hash = await sha256.digest(bytes);
16
- const cid = CID.create(1, raw.code, hash);
17
- return cid.toString();
18
- }
19
- async function verifyCid(data, expectedCid) {
20
- const computed = await computeCid(data);
21
- return computed === expectedCid;
22
- }
23
-
24
1
  // src/operations/upload/engine.ts
25
- var PHASE_COUNT = 4;
2
+ var PHASE_COUNT = 3;
26
3
  var PHASE_INDEX = {
27
- "computing-cids": 0,
28
- "creating": 1,
29
- "backlinking": 2,
30
- "uploading": 3,
31
- "complete": 4,
32
- "error": -1
4
+ creating: 0,
5
+ backlinking: 1,
6
+ uploading: 2,
7
+ complete: 3,
8
+ error: -1
33
9
  };
34
10
  async function parallelLimit(items, concurrency, fn) {
35
11
  const results = [];
@@ -92,13 +68,10 @@ async function uploadTree(client, tree, options) {
92
68
  let bytesUploaded = 0;
93
69
  const reportProgress = (progress) => {
94
70
  if (onProgress) {
95
- const phase = progress.phase || "computing-cids";
71
+ const phase = progress.phase || "creating";
96
72
  const phaseIndex = PHASE_INDEX[phase] ?? -1;
97
73
  let phasePercent = 0;
98
- if (phase === "computing-cids") {
99
- const done = progress.completedEntities ?? completedEntities;
100
- phasePercent = tree.files.length > 0 ? Math.round(done / tree.files.length * 100) : 100;
101
- } else if (phase === "creating") {
74
+ if (phase === "creating") {
102
75
  const done = progress.completedEntities ?? completedEntities;
103
76
  phasePercent = totalEntities > 0 ? Math.round(done / totalEntities * 100) : 100;
104
77
  } else if (phase === "backlinking") {
@@ -159,29 +132,6 @@ async function uploadTree(client, tree, options) {
159
132
  throw new Error("Must provide either collectionId or createCollection in target");
160
133
  }
161
134
  const rootParentId = target.parentId ?? collectionId;
162
- reportProgress({ phase: "computing-cids", completedEntities: 0 });
163
- const preparedFiles = [];
164
- let cidProgress = 0;
165
- await parallelLimit(tree.files, Math.max(concurrency, 20), async (file) => {
166
- try {
167
- const data = await file.getData();
168
- const cid = await computeCid(data);
169
- preparedFiles.push({ ...file, cid });
170
- cidProgress++;
171
- reportProgress({
172
- phase: "computing-cids",
173
- completedEntities: cidProgress,
174
- currentItem: file.relativePath
175
- });
176
- } catch (err) {
177
- const errorMsg = err instanceof Error ? err.message : String(err);
178
- if (continueOnError) {
179
- errors.push({ path: file.relativePath, error: `CID computation failed: ${errorMsg}` });
180
- } else {
181
- throw new Error(`Failed to compute CID for ${file.relativePath}: ${errorMsg}`);
182
- }
183
- }
184
- });
185
135
  reportProgress({ phase: "creating", completedEntities: 0 });
186
136
  const foldersByDepth = groupFoldersByDepth(tree.folders);
187
137
  const sortedDepths = [...foldersByDepth.keys()].sort((a, b) => a - b);
@@ -231,17 +181,17 @@ async function uploadTree(client, tree, options) {
231
181
  );
232
182
  }
233
183
  const FILE_CREATION_CONCURRENCY = 50;
234
- await parallelLimit(preparedFiles, FILE_CREATION_CONCURRENCY, async (file) => {
184
+ await parallelLimit(tree.files, FILE_CREATION_CONCURRENCY, async (file) => {
235
185
  try {
236
186
  const parentPath = getParentPath(file.relativePath);
237
187
  const parentId = parentPath ? foldersByPath.get(parentPath).id : rootParentId;
238
188
  const parentType = parentPath ? "folder" : parentId === collectionId ? "collection" : "folder";
239
189
  const fileBody = {
240
- key: file.cid,
190
+ key: crypto.randomUUID(),
191
+ // Generate unique storage key
241
192
  filename: file.name,
242
193
  content_type: file.mimeType,
243
194
  size: file.size,
244
- cid: file.cid,
245
195
  collection: collectionId,
246
196
  relationships: [{ predicate: "in", peer: parentId, peer_type: parentType }]
247
197
  };
@@ -254,9 +204,7 @@ async function uploadTree(client, tree, options) {
254
204
  createdFiles.push({
255
205
  ...file,
256
206
  id: data.id,
257
- entityCid: data.cid,
258
- uploadUrl: data.upload_url,
259
- uploadExpiresAt: data.upload_expires_at
207
+ entityCid: data.cid
260
208
  });
261
209
  completedEntities++;
262
210
  reportProgress({
@@ -372,44 +320,14 @@ async function uploadTree(client, tree, options) {
372
320
  } else {
373
321
  body = new Blob([fileData], { type: file.mimeType });
374
322
  }
375
- const uploadResponse = await fetch(file.uploadUrl, {
376
- method: "PUT",
323
+ const { error: uploadError } = await client.api.POST("/files/{id}/content", {
324
+ params: { path: { id: file.id } },
377
325
  body,
326
+ bodySerializer: (b) => b,
378
327
  headers: { "Content-Type": file.mimeType }
379
328
  });
380
- if (!uploadResponse.ok) {
381
- throw new Error(`S3 upload failed with status ${uploadResponse.status}`);
382
- }
383
- let confirmTip = file.entityCid;
384
- let confirmAttempts = 0;
385
- const MAX_CONFIRM_ATTEMPTS = 3;
386
- while (confirmAttempts < MAX_CONFIRM_ATTEMPTS) {
387
- confirmAttempts++;
388
- const { error: confirmError } = await client.api.POST("/files/{id}/confirm-upload", {
389
- params: { path: { id: file.id } },
390
- body: {
391
- expect_tip: confirmTip,
392
- note: note ? `${note} (confirmed)` : "Upload confirmed"
393
- }
394
- });
395
- if (!confirmError) {
396
- break;
397
- }
398
- const errorStr = JSON.stringify(confirmError);
399
- if (errorStr.includes("409") || errorStr.includes("CAS") || errorStr.includes("conflict")) {
400
- const { data: currentFile, error: fetchError } = await client.api.GET("/files/{id}", {
401
- params: { path: { id: file.id } }
402
- });
403
- if (fetchError || !currentFile) {
404
- throw new Error(`Failed to fetch file for confirm retry: ${JSON.stringify(fetchError)}`);
405
- }
406
- confirmTip = currentFile.cid;
407
- } else {
408
- throw new Error(`Confirm upload failed: ${errorStr}`);
409
- }
410
- }
411
- if (confirmAttempts >= MAX_CONFIRM_ATTEMPTS) {
412
- throw new Error(`Confirm upload failed after ${MAX_CONFIRM_ATTEMPTS} CAS retries`);
329
+ if (uploadError) {
330
+ throw new Error(`Upload failed: ${JSON.stringify(uploadError)}`);
413
331
  }
414
332
  bytesUploaded += file.size;
415
333
  reportProgress({
@@ -482,6 +400,29 @@ async function uploadTree(client, tree, options) {
482
400
  }
483
401
  }
484
402
 
403
+ // src/operations/upload/cid.ts
404
+ import { CID } from "multiformats/cid";
405
+ import { sha256 } from "multiformats/hashes/sha2";
406
+ import * as raw from "multiformats/codecs/raw";
407
+ async function computeCid(data) {
408
+ let bytes;
409
+ if (data instanceof Blob) {
410
+ const buffer = await data.arrayBuffer();
411
+ bytes = new Uint8Array(buffer);
412
+ } else if (data instanceof ArrayBuffer) {
413
+ bytes = new Uint8Array(data);
414
+ } else {
415
+ bytes = data;
416
+ }
417
+ const hash = await sha256.digest(bytes);
418
+ const cid = CID.create(1, raw.code, hash);
419
+ return cid.toString();
420
+ }
421
+ async function verifyCid(data, expectedCid) {
422
+ const computed = await computeCid(data);
423
+ return computed === expectedCid;
424
+ }
425
+
485
426
  // src/operations/upload/scanners.ts
486
427
  function getMimeType(filename) {
487
428
  const ext = filename.toLowerCase().split(".").pop() || "";
@@ -739,7 +680,7 @@ var FolderOperations = class {
739
680
  concurrency: options.concurrency,
740
681
  onProgress: options.onProgress ? (p) => {
741
682
  options.onProgress({
742
- phase: p.phase === "computing-cids" ? "creating-folders" : p.phase === "creating" ? "uploading-files" : p.phase === "backlinking" ? "linking" : p.phase === "complete" ? "complete" : "scanning",
683
+ phase: p.phase === "creating" ? "creating-folders" : p.phase === "uploading" ? "uploading-files" : p.phase === "backlinking" ? "linking" : p.phase === "complete" ? "complete" : "scanning",
743
684
  totalFiles: p.totalEntities,
744
685
  completedFiles: p.completedEntities,
745
686
  totalFolders: p.totalParents,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/operations/upload/cid.ts","../../src/operations/upload/engine.ts","../../src/operations/upload/scanners.ts","../../src/operations/folders.ts","../../src/operations/batch.ts","../../src/operations/crypto.ts"],"sourcesContent":["/**\n * CID Computation Utility\n *\n * Computes IPFS CIDv1 (base32) for file content.\n * Uses raw codec (0x55) and SHA-256 hash.\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. Compute CIDs for all files (high parallelism)\n * 2. Create folders by depth (with unidirectional 'in' → parent)\n * 3. Create file entities (metadata only, high parallelism)\n * 4. Backlink parents with 'contains' relationships\n * → Tree is now browsable! Users can explore structure immediately.\n * 5. Upload file content to S3 + confirm (byte-based pool, ~200MB in flight)\n * - PUT to presigned S3 URL\n * - POST /files/{id}/confirm-upload to set uploaded: true\n * - Auto-retry on CAS conflict (entity may have changed during upload)\n *\n * Byte-based pool (for S3 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 type { components } from '../../generated/types.js';\nimport { computeCid } from './cid.js';\nimport type {\n UploadTree,\n UploadOptions,\n UploadResult,\n UploadProgress,\n PreparedFile,\n CreatedFolder,\n CreatedFile,\n CreatedEntity,\n UploadFolder,\n} from './types.js';\n\ntype CreateCollectionRequest = components['schemas']['CreateCollectionRequest'];\ntype CreateFolderRequest = components['schemas']['CreateFolderRequest'];\ntype CreateFileRequest = components['schemas']['CreateFileRequest'];\ntype UpdateFolderRequest = components['schemas']['UpdateFolderRequest'];\ntype UpdateCollectionRequest = components['schemas']['UpdateCollectionRequest'];\n\n// Phase constants\nconst PHASE_COUNT = 4; // computing-cids, creating, backlinking, uploading (excluding complete/error)\nconst PHASE_INDEX: Record<string, number> = {\n 'computing-cids': 0,\n 'creating': 1,\n 'backlinking': 2,\n 'uploading': 3,\n 'complete': 4,\n 'error': -1,\n};\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// Byte-Based Pool\n// =============================================================================\n\n/** Target bytes in flight (~200MB) */\nconst TARGET_BYTES_IN_FLIGHT = 200 * 1024 * 1024;\n\n/**\n * Pool that maintains a target number of bytes in flight.\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)\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 waitQueue: Array<() => void> = [];\n\n constructor(private targetBytes: number = TARGET_BYTES_IN_FLIGHT) {}\n\n async run<T>(size: number, fn: () => Promise<T>): Promise<T> {\n // Wait until we have room\n // Exception: if pool is empty, always allow (handles files > targetBytes)\n while (this.bytesInFlight > 0 && this.bytesInFlight + size > this.targetBytes) {\n await new Promise<void>((resolve) => this.waitQueue.push(resolve));\n }\n\n this.bytesInFlight += size;\n try {\n return await fn();\n } finally {\n this.bytesInFlight -= size;\n // Wake up next waiting task\n const next = this.waitQueue.shift();\n if (next) next();\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, note } = options;\n\n const errors: Array<{ path: string; error: string }> = [];\n const createdFolders: CreatedFolder[] = [];\n const createdFiles: CreatedFile[] = [];\n\n // Maps for tracking\n const foldersByPath = new Map<string, { id: string; cid: 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 || 'computing-cids';\n const phaseIndex = PHASE_INDEX[phase] ?? -1;\n\n // Calculate phase percent based on current phase\n let phasePercent = 0;\n if (phase === 'computing-cids') {\n // CID phase: progress is based on files processed\n const done = progress.completedEntities ?? completedEntities;\n phasePercent = tree.files.length > 0 ? Math.round((done / tree.files.length) * 100) : 100;\n } else 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 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 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 } 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\n // ─────────────────────────────────────────────────────────────────────────\n // PHASE 1: Compute CIDs for all files\n // ─────────────────────────────────────────────────────────────────────────\n reportProgress({ phase: 'computing-cids', completedEntities: 0 });\n\n const preparedFiles: PreparedFile[] = [];\n let cidProgress = 0;\n\n await parallelLimit(tree.files, Math.max(concurrency, 20), async (file) => {\n try {\n const data = await file.getData();\n const cid = await computeCid(data);\n\n preparedFiles.push({ ...file, cid });\n\n cidProgress++;\n reportProgress({\n phase: 'computing-cids',\n completedEntities: cidProgress,\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: `CID computation failed: ${errorMsg}` });\n } else {\n throw new Error(`Failed to compute CID for ${file.relativePath}: ${errorMsg}`);\n }\n }\n });\n\n // ─────────────────────────────────────────────────────────────────────────\n // PHASE 2: 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)\n for (const depth of sortedDepths) {\n const foldersAtDepth = foldersByDepth.get(depth)!;\n\n await Promise.all(\n foldersAtDepth.map(async (folder) => {\n try {\n const parentPath = getParentPath(folder.relativePath);\n const parentId = parentPath ? foldersByPath.get(parentPath)!.id : rootParentId;\n const parentType = parentPath ? 'folder' : parentId === collectionId ? 'collection' : 'folder';\n\n const folderBody: CreateFolderRequest = {\n label: folder.name,\n collection: collectionId,\n note,\n relationships: [{ predicate: 'in', peer: parentId, peer_type: parentType }],\n };\n\n const { data, error } = await client.api.POST('/folders', {\n body: folderBody,\n });\n\n if (error || !data) {\n throw new Error(JSON.stringify(error));\n }\n\n // Track folder\n foldersByPath.set(folder.relativePath, { id: data.id, cid: data.cid });\n createdFolders.push({\n name: folder.name,\n relativePath: folder.relativePath,\n id: data.id,\n entityCid: data.cid,\n });\n\n completedEntities++;\n reportProgress({\n phase: 'creating',\n completedEntities,\n currentItem: folder.relativePath,\n });\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\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 }\n\n // Create file entities (metadata only, no content upload yet)\n // Use simple concurrency limit for API calls\n const FILE_CREATION_CONCURRENCY = 50;\n\n await parallelLimit(preparedFiles, FILE_CREATION_CONCURRENCY, async (file) => {\n try {\n const parentPath = getParentPath(file.relativePath);\n const parentId = parentPath ? foldersByPath.get(parentPath)!.id : rootParentId;\n const parentType = parentPath ? 'folder' : parentId === collectionId ? 'collection' : 'folder';\n\n // Create file entity with 'in' relationship\n const fileBody: CreateFileRequest = {\n key: file.cid,\n filename: file.name,\n content_type: file.mimeType,\n size: file.size,\n cid: file.cid,\n collection: collectionId,\n relationships: [{ predicate: 'in', peer: parentId, peer_type: parentType }],\n };\n\n const { data, error } = await client.api.POST('/files', {\n body: fileBody,\n });\n\n if (error || !data) {\n throw new Error(`Entity creation failed: ${JSON.stringify(error)}`);\n }\n\n // Track file with presigned URL for later upload\n createdFiles.push({\n ...file,\n id: data.id,\n entityCid: data.cid,\n uploadUrl: data.upload_url,\n uploadExpiresAt: data.upload_expires_at,\n });\n\n completedEntities++;\n reportProgress({\n phase: 'creating',\n completedEntities,\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: errorMsg });\n completedEntities++;\n } else {\n throw new Error(`Failed to create file ${file.relativePath}: ${errorMsg}`);\n }\n }\n });\n\n // ─────────────────────────────────────────────────────────────────────────\n // PHASE 3: Backlink - Update each parent with 'contains' relationships\n // ─────────────────────────────────────────────────────────────────────────\n\n // Build parent -> children map\n const childrenByParent = new Map<string, Array<{ id: string; type: 'file' | 'folder' }>>();\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' });\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' });\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 const parentEntries = [...childrenByParent.entries()];\n\n await parallelLimit(parentEntries, concurrency, async ([parentId, children]) => {\n try {\n const isCollection = parentId === collectionId;\n\n // Build relationships_add array with all children\n const relationshipsAdd = children.map((child) => ({\n predicate: 'contains' as const,\n peer: child.id,\n peer_type: child.type,\n }));\n\n if (isCollection) {\n // Get current collection CID for CAS\n const { data: collData, error: getError } = await client.api.GET('/collections/{id}', {\n params: { path: { id: parentId } },\n });\n if (getError || !collData) {\n throw new Error(`Failed to fetch collection: ${JSON.stringify(getError)}`);\n }\n\n // Update collection with relationships_add\n const updateBody: UpdateCollectionRequest = {\n expect_tip: collData.cid,\n relationships_add: relationshipsAdd,\n note: note ? `${note} (backlink)` : 'Upload backlink',\n };\n\n const { error } = await client.api.PUT('/collections/{id}', {\n params: { path: { id: parentId } },\n body: updateBody,\n });\n\n if (error) {\n throw new Error(JSON.stringify(error));\n }\n } else {\n // Get current folder CID for CAS\n const { data: folderData, error: getError } = await client.api.GET('/folders/{id}', {\n params: { path: { id: parentId } },\n });\n if (getError || !folderData) {\n throw new Error(`Failed to fetch folder: ${JSON.stringify(getError)}`);\n }\n\n // Update folder with relationships_add\n const updateBody: UpdateFolderRequest = {\n expect_tip: folderData.cid,\n relationships_add: relationshipsAdd,\n note: note ? `${note} (backlink)` : 'Upload backlink',\n };\n\n const { error } = await client.api.PUT('/folders/{id}', {\n params: { path: { id: parentId } },\n body: updateBody,\n });\n\n if (error) {\n throw new Error(JSON.stringify(error));\n }\n }\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 4: Upload file content to S3\n // Tree is now browsable! Users can explore while content uploads.\n // ─────────────────────────────────────────────────────────────────────────\n reportProgress({ phase: 'uploading', bytesUploaded: 0 });\n\n // Use byte-based pool to maintain ~200MB in flight\n const pool = new BytePool();\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 // Upload to presigned URL\n const uploadResponse = await fetch(file.uploadUrl, {\n method: 'PUT',\n body,\n headers: { 'Content-Type': file.mimeType },\n });\n\n if (!uploadResponse.ok) {\n throw new Error(`S3 upload failed with status ${uploadResponse.status}`);\n }\n\n // Confirm upload completed - sets uploaded: true on entity\n // Retry on CAS conflict (entity may have been updated during upload)\n let confirmTip = file.entityCid;\n let confirmAttempts = 0;\n const MAX_CONFIRM_ATTEMPTS = 3;\n\n while (confirmAttempts < MAX_CONFIRM_ATTEMPTS) {\n confirmAttempts++;\n\n const { error: confirmError } = await client.api.POST('/files/{id}/confirm-upload', {\n params: { path: { id: file.id } },\n body: {\n expect_tip: confirmTip,\n note: note ? `${note} (confirmed)` : 'Upload confirmed',\n },\n });\n\n if (!confirmError) {\n break; // Success\n }\n\n // Check for CAS conflict (409)\n const errorStr = JSON.stringify(confirmError);\n if (errorStr.includes('409') || errorStr.includes('CAS') || errorStr.includes('conflict')) {\n // Fetch current file to get updated CID\n const { data: currentFile, error: fetchError } = await client.api.GET('/files/{id}', {\n params: { path: { id: file.id } },\n });\n\n if (fetchError || !currentFile) {\n throw new Error(`Failed to fetch file for confirm retry: ${JSON.stringify(fetchError)}`);\n }\n\n confirmTip = currentFile.cid;\n // Loop will retry with new tip\n } else {\n // Non-CAS error - throw\n throw new Error(`Confirm upload failed: ${errorStr}`);\n }\n }\n\n if (confirmAttempts >= MAX_CONFIRM_ATTEMPTS) {\n throw new Error(`Confirm upload failed after ${MAX_CONFIRM_ATTEMPTS} CAS retries`);\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// Node.js Scanner\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Scan a local directory and build an UploadTree.\n *\n * **Node.js only** - uses fs and path modules.\n *\n * @param directoryPath - Absolute path to directory\n * @param options - Scanner options\n * @returns UploadTree ready for upload\n *\n * @example\n * ```typescript\n * import { scanDirectory } from '@arke-institute/sdk/operations';\n *\n * const tree = await scanDirectory('/path/to/my-project');\n * const result = await uploadTree(client, tree, { target: { collectionId: '...' } });\n * ```\n */\nexport async function scanDirectory(\n directoryPath: string,\n options: {\n /** Patterns to ignore (glob-style, e.g., 'node_modules', '.git') */\n ignore?: string[];\n /** Whether to include hidden files (default: false) */\n includeHidden?: boolean;\n } = {}\n): Promise<UploadTree> {\n // Dynamic import for Node.js modules (allows SDK to be used in browser too)\n const fs = await import('node:fs/promises');\n const path = await import('node:path');\n\n const { ignore = ['node_modules', '.git', '.DS_Store'], includeHidden = false } = options;\n\n const files: UploadFile[] = [];\n const folders: UploadFolder[] = [];\n\n // Get the root folder name\n const rootName = path.basename(directoryPath);\n\n async function scanDir(dirPath: string, relativePath: string): Promise<void> {\n const entries = await fs.readdir(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const name = entry.name;\n\n // Skip hidden files unless explicitly included\n if (!includeHidden && name.startsWith('.')) {\n continue;\n }\n\n // Skip ignored patterns\n if (ignore.some((pattern) => name === pattern || name.match(pattern))) {\n continue;\n }\n\n const fullPath = path.join(dirPath, name);\n const entryRelativePath = relativePath ? `${relativePath}/${name}` : name;\n\n if (entry.isDirectory()) {\n folders.push({\n name,\n relativePath: entryRelativePath,\n });\n\n // Recurse into subdirectory\n await scanDir(fullPath, entryRelativePath);\n } else if (entry.isFile()) {\n const stat = await fs.stat(fullPath);\n\n files.push({\n name,\n relativePath: entryRelativePath,\n size: stat.size,\n mimeType: getMimeType(name),\n getData: async () => {\n const buffer = await fs.readFile(fullPath);\n // Convert Node.js Buffer to ArrayBuffer\n return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);\n },\n });\n }\n // Skip symlinks, etc.\n }\n }\n\n await scanDir(directoryPath, '');\n\n // Sort folders by depth (important for creation order)\n folders.sort((a, b) => a.relativePath.split('/').length - b.relativePath.split('/').length);\n\n return { files, folders };\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 * Folder Operations (Legacy)\n *\n * @deprecated Use the new upload module instead:\n * ```typescript\n * import { uploadTree, scanDirectory } from '@arke-institute/sdk/operations';\n *\n * const tree = await scanDirectory('/path/to/folder');\n * const result = await uploadTree(client, tree, {\n * target: { collectionId: '...' },\n * });\n * ```\n */\n\nimport type { ArkeClient } from '../client/ArkeClient.js';\nimport { uploadTree, scanDirectory, type UploadResult } from './upload/index.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 scanDirectory functions instead:\n * ```typescript\n * import { uploadTree, scanDirectory } from '@arke-institute/sdk/operations';\n *\n * const tree = await scanDirectory('/path/to/folder');\n * const result = await uploadTree(client, tree, {\n * target: { collectionId: '...' },\n * onProgress: (p) => console.log(`${p.completedFiles}/${p.totalFiles} files`),\n * });\n * ```\n */\nexport class FolderOperations {\n constructor(private client: ArkeClient) {}\n\n /**\n * Upload a local directory to Arke\n *\n * @deprecated Use uploadTree and scanDirectory instead\n */\n async uploadDirectory(\n localPath: string,\n options: UploadDirectoryOptions\n ): Promise<UploadDirectoryResult> {\n // Use the new implementation\n const tree = await scanDirectory(localPath);\n\n const result: UploadResult = await uploadTree(this.client, tree, {\n target: {\n collectionId: options.collectionId,\n parentId: options.parentFolderId,\n },\n concurrency: options.concurrency,\n onProgress: options.onProgress\n ? (p) => {\n // Map new progress to old format\n options.onProgress!({\n phase:\n p.phase === 'computing-cids'\n ? 'creating-folders'\n : p.phase === 'creating'\n ? 'uploading-files'\n : p.phase === 'backlinking'\n ? 'linking'\n : p.phase === 'complete'\n ? 'complete'\n : 'scanning',\n totalFiles: p.totalEntities,\n completedFiles: p.completedEntities,\n totalFolders: p.totalParents,\n completedFolders: p.completedParents,\n currentFile: p.currentItem,\n });\n }\n : undefined,\n });\n\n return {\n rootFolder: result.folders[0] || null,\n folders: result.folders,\n files: result.files,\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"],"mappings":"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","names":["entries"]}
1
+ {"version":3,"sources":["../../src/operations/upload/engine.ts","../../src/operations/upload/cid.ts","../../src/operations/upload/scanners.ts","../../src/operations/folders.ts","../../src/operations/batch.ts","../../src/operations/crypto.ts"],"sourcesContent":["/**\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 type { components } from '../../generated/types.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 CreateFolderRequest = components['schemas']['CreateFolderRequest'];\ntype CreateFileRequest = components['schemas']['CreateFileRequest'];\ntype UpdateFolderRequest = components['schemas']['UpdateFolderRequest'];\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// =============================================================================\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// Byte-Based Pool\n// =============================================================================\n\n/** Target bytes in flight (~200MB) */\nconst TARGET_BYTES_IN_FLIGHT = 200 * 1024 * 1024;\n\n/**\n * Pool that maintains a target number of bytes in flight.\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)\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 waitQueue: Array<() => void> = [];\n\n constructor(private targetBytes: number = TARGET_BYTES_IN_FLIGHT) {}\n\n async run<T>(size: number, fn: () => Promise<T>): Promise<T> {\n // Wait until we have room\n // Exception: if pool is empty, always allow (handles files > targetBytes)\n while (this.bytesInFlight > 0 && this.bytesInFlight + size > this.targetBytes) {\n await new Promise<void>((resolve) => this.waitQueue.push(resolve));\n }\n\n this.bytesInFlight += size;\n try {\n return await fn();\n } finally {\n this.bytesInFlight -= size;\n // Wake up next waiting task\n const next = this.waitQueue.shift();\n if (next) next();\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, note } = options;\n\n const errors: Array<{ path: string; error: string }> = [];\n const createdFolders: CreatedFolder[] = [];\n const createdFiles: CreatedFile[] = [];\n\n // Maps for tracking\n const foldersByPath = new Map<string, { id: string; cid: 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 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 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 } 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\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)\n for (const depth of sortedDepths) {\n const foldersAtDepth = foldersByDepth.get(depth)!;\n\n await Promise.all(\n foldersAtDepth.map(async (folder) => {\n try {\n const parentPath = getParentPath(folder.relativePath);\n const parentId = parentPath ? foldersByPath.get(parentPath)!.id : rootParentId;\n const parentType = parentPath ? 'folder' : parentId === collectionId ? 'collection' : 'folder';\n\n const folderBody: CreateFolderRequest = {\n label: folder.name,\n collection: collectionId,\n note,\n relationships: [{ predicate: 'in', peer: parentId, peer_type: parentType }],\n };\n\n const { data, error } = await client.api.POST('/folders', {\n body: folderBody,\n });\n\n if (error || !data) {\n throw new Error(JSON.stringify(error));\n }\n\n // Track folder\n foldersByPath.set(folder.relativePath, { id: data.id, cid: data.cid });\n createdFolders.push({\n name: folder.name,\n relativePath: folder.relativePath,\n id: data.id,\n entityCid: data.cid,\n });\n\n completedEntities++;\n reportProgress({\n phase: 'creating',\n completedEntities,\n currentItem: folder.relativePath,\n });\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\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 }\n\n // Create file entities (metadata only, no content upload yet)\n // Use simple concurrency limit for API calls\n const FILE_CREATION_CONCURRENCY = 50;\n\n await parallelLimit(tree.files, FILE_CREATION_CONCURRENCY, async (file) => {\n try {\n const parentPath = getParentPath(file.relativePath);\n const parentId = parentPath ? foldersByPath.get(parentPath)!.id : rootParentId;\n const parentType = parentPath ? 'folder' : parentId === collectionId ? 'collection' : 'folder';\n\n // Create file entity with 'in' relationship\n // Server computes CID when content is uploaded\n const fileBody: CreateFileRequest = {\n key: crypto.randomUUID(), // Generate unique storage key\n filename: file.name,\n content_type: file.mimeType,\n size: file.size,\n collection: collectionId,\n relationships: [{ predicate: 'in', peer: parentId, peer_type: parentType }],\n };\n\n const { data, error } = await client.api.POST('/files', {\n body: fileBody,\n });\n\n if (error || !data) {\n throw new Error(`Entity creation failed: ${JSON.stringify(error)}`);\n }\n\n // Track file for later upload\n createdFiles.push({\n ...file,\n id: data.id,\n entityCid: data.cid,\n });\n\n completedEntities++;\n reportProgress({\n phase: 'creating',\n completedEntities,\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: errorMsg });\n completedEntities++;\n } else {\n throw new Error(`Failed to create file ${file.relativePath}: ${errorMsg}`);\n }\n }\n });\n\n // ─────────────────────────────────────────────────────────────────────────\n // PHASE 2: Backlink - Update each parent with 'contains' relationships\n // ─────────────────────────────────────────────────────────────────────────\n\n // Build parent -> children map\n const childrenByParent = new Map<string, Array<{ id: string; type: 'file' | 'folder' }>>();\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' });\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' });\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 const parentEntries = [...childrenByParent.entries()];\n\n await parallelLimit(parentEntries, concurrency, async ([parentId, children]) => {\n try {\n const isCollection = parentId === collectionId;\n\n // Build relationships_add array with all children\n const relationshipsAdd = children.map((child) => ({\n predicate: 'contains' as const,\n peer: child.id,\n peer_type: child.type,\n }));\n\n if (isCollection) {\n // Get current collection CID for CAS\n const { data: collData, error: getError } = await client.api.GET('/collections/{id}', {\n params: { path: { id: parentId } },\n });\n if (getError || !collData) {\n throw new Error(`Failed to fetch collection: ${JSON.stringify(getError)}`);\n }\n\n // Update collection with relationships_add\n const updateBody: UpdateCollectionRequest = {\n expect_tip: collData.cid,\n relationships_add: relationshipsAdd,\n note: note ? `${note} (backlink)` : 'Upload backlink',\n };\n\n const { error } = await client.api.PUT('/collections/{id}', {\n params: { path: { id: parentId } },\n body: updateBody,\n });\n\n if (error) {\n throw new Error(JSON.stringify(error));\n }\n } else {\n // Get current folder CID for CAS\n const { data: folderData, error: getError } = await client.api.GET('/folders/{id}', {\n params: { path: { id: parentId } },\n });\n if (getError || !folderData) {\n throw new Error(`Failed to fetch folder: ${JSON.stringify(getError)}`);\n }\n\n // Update folder with relationships_add\n const updateBody: UpdateFolderRequest = {\n expect_tip: folderData.cid,\n relationships_add: relationshipsAdd,\n note: note ? `${note} (backlink)` : 'Upload backlink',\n };\n\n const { error } = await client.api.PUT('/folders/{id}', {\n params: { path: { id: parentId } },\n body: updateBody,\n });\n\n if (error) {\n throw new Error(JSON.stringify(error));\n }\n }\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 directly to API\n // Tree is now browsable! Users can explore while content uploads.\n // ─────────────────────────────────────────────────────────────────────────\n reportProgress({ phase: 'uploading', bytesUploaded: 0 });\n\n // Use byte-based pool to maintain ~200MB in flight\n const pool = new BytePool();\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 // Upload content directly to API endpoint\n // The API streams to R2, computes CID, and updates the entity atomically\n const { error: uploadError } = await client.api.POST('/files/{id}/content', {\n params: { path: { id: file.id } },\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 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 * 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 * 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// Node.js Scanner\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Scan a local directory and build an UploadTree.\n *\n * **Node.js only** - uses fs and path modules.\n *\n * @param directoryPath - Absolute path to directory\n * @param options - Scanner options\n * @returns UploadTree ready for upload\n *\n * @example\n * ```typescript\n * import { scanDirectory } from '@arke-institute/sdk/operations';\n *\n * const tree = await scanDirectory('/path/to/my-project');\n * const result = await uploadTree(client, tree, { target: { collectionId: '...' } });\n * ```\n */\nexport async function scanDirectory(\n directoryPath: string,\n options: {\n /** Patterns to ignore (glob-style, e.g., 'node_modules', '.git') */\n ignore?: string[];\n /** Whether to include hidden files (default: false) */\n includeHidden?: boolean;\n } = {}\n): Promise<UploadTree> {\n // Dynamic import for Node.js modules (allows SDK to be used in browser too)\n const fs = await import('node:fs/promises');\n const path = await import('node:path');\n\n const { ignore = ['node_modules', '.git', '.DS_Store'], includeHidden = false } = options;\n\n const files: UploadFile[] = [];\n const folders: UploadFolder[] = [];\n\n // Get the root folder name\n const rootName = path.basename(directoryPath);\n\n async function scanDir(dirPath: string, relativePath: string): Promise<void> {\n const entries = await fs.readdir(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const name = entry.name;\n\n // Skip hidden files unless explicitly included\n if (!includeHidden && name.startsWith('.')) {\n continue;\n }\n\n // Skip ignored patterns\n if (ignore.some((pattern) => name === pattern || name.match(pattern))) {\n continue;\n }\n\n const fullPath = path.join(dirPath, name);\n const entryRelativePath = relativePath ? `${relativePath}/${name}` : name;\n\n if (entry.isDirectory()) {\n folders.push({\n name,\n relativePath: entryRelativePath,\n });\n\n // Recurse into subdirectory\n await scanDir(fullPath, entryRelativePath);\n } else if (entry.isFile()) {\n const stat = await fs.stat(fullPath);\n\n files.push({\n name,\n relativePath: entryRelativePath,\n size: stat.size,\n mimeType: getMimeType(name),\n getData: async () => {\n const buffer = await fs.readFile(fullPath);\n // Convert Node.js Buffer to ArrayBuffer\n return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);\n },\n });\n }\n // Skip symlinks, etc.\n }\n }\n\n await scanDir(directoryPath, '');\n\n // Sort folders by depth (important for creation order)\n folders.sort((a, b) => a.relativePath.split('/').length - b.relativePath.split('/').length);\n\n return { files, folders };\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 * Folder Operations (Legacy)\n *\n * @deprecated Use the new upload module instead:\n * ```typescript\n * import { uploadTree, scanDirectory } from '@arke-institute/sdk/operations';\n *\n * const tree = await scanDirectory('/path/to/folder');\n * const result = await uploadTree(client, tree, {\n * target: { collectionId: '...' },\n * });\n * ```\n */\n\nimport type { ArkeClient } from '../client/ArkeClient.js';\nimport { uploadTree, scanDirectory, type UploadResult } from './upload/index.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 scanDirectory functions instead:\n * ```typescript\n * import { uploadTree, scanDirectory } from '@arke-institute/sdk/operations';\n *\n * const tree = await scanDirectory('/path/to/folder');\n * const result = await uploadTree(client, tree, {\n * target: { collectionId: '...' },\n * onProgress: (p) => console.log(`${p.completedFiles}/${p.totalFiles} files`),\n * });\n * ```\n */\nexport class FolderOperations {\n constructor(private client: ArkeClient) {}\n\n /**\n * Upload a local directory to Arke\n *\n * @deprecated Use uploadTree and scanDirectory instead\n */\n async uploadDirectory(\n localPath: string,\n options: UploadDirectoryOptions\n ): Promise<UploadDirectoryResult> {\n // Use the new implementation\n const tree = await scanDirectory(localPath);\n\n const result: UploadResult = await uploadTree(this.client, tree, {\n target: {\n collectionId: options.collectionId,\n parentId: options.parentFolderId,\n },\n concurrency: options.concurrency,\n onProgress: options.onProgress\n ? (p) => {\n // Map new progress to old format\n options.onProgress!({\n phase:\n p.phase === 'creating'\n ? 'creating-folders'\n : p.phase === 'uploading'\n ? 'uploading-files'\n : p.phase === 'backlinking'\n ? 'linking'\n : p.phase === 'complete'\n ? 'complete'\n : 'scanning',\n totalFiles: p.totalEntities,\n completedFiles: p.completedEntities,\n totalFolders: p.totalParents,\n completedFolders: p.completedParents,\n currentFile: p.currentItem,\n });\n }\n : undefined,\n });\n\n return {\n rootFolder: result.folders[0] || null,\n folders: result.folders,\n files: result.files,\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"],"mappings":"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","names":["entries"]}