@storagehub-sdk/msp-client 0.0.4 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -77,15 +77,17 @@ const verified = await client.verify(message, signature);
77
77
  client.setToken(verified.token); // Set auth token for subsequent requests
78
78
 
79
79
  // 4. Upload a file
80
- const bucketId = '0xYourBucketId'; // StorageHub bucket identifier
81
- const fileKey = '0xYourFileKey'; // Unique file identifier
80
+ const bucketId = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'; // StorageHub bucket identifier
81
+ const fileKey = '0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890'; // Unique file identifier
82
82
  const filePath = './myfile.txt';
83
+ const owner = walletAddress; // File owner
84
+ const location = 'myfile.txt'; // File location/path within the bucket
83
85
 
84
- const receipt = await client.uploadFile(bucketId, fileKey, createReadStream(filePath));
86
+ const receipt = await client.uploadFile(bucketId, fileKey, createReadStream(filePath), owner, location);
85
87
  console.log('File uploaded successfully:', receipt);
86
88
 
87
89
  // 5. Download the file
88
- const download = await client.downloadByKey(bucketId, fileKey);
90
+ const download = await client.downloadByKey(fileKey);
89
91
  const outputPath = './downloaded-file.txt';
90
92
 
91
93
  // Stream the download to a file
@@ -135,13 +137,16 @@ console.log('Folder files:', files);
135
137
  - `message: string` - The message that was signed
136
138
  - `signature: string` - Wallet signature (0x...)
137
139
  - **`setToken(token)`** - Set authentication token for subsequent requests
138
- - **`uploadFile(bucketId, fileKey, file)`** - Upload file to storage
140
+ - **`uploadFile(bucketId, fileKey, file, owner, location)`** - Upload file to storage
139
141
  - `bucketId: string` - Storage bucket identifier
140
142
  - `fileKey: string` - Unique file key/identifier
141
- - `file: ReadStream | Blob | File` - File data to upload
142
- - **`downloadByKey(bucketId, fileKey)`** - Download file by bucket and key
143
- - Returns: `{ stream: ReadableStream, status: string }`
144
- - **`downloadByLocation(bucketId, filePath)`** - Download file by bucket and path
143
+ - `file: ReadStream | Blob | File | Uint8Array | ArrayBuffer` - File data to upload
144
+ - `owner: string` - File owner
145
+ - `location: string` - File location/path within the bucket
146
+ - **`hexToBytes(hex)`** - Convert hex string to Uint8Array (handles 0x prefix) (TODO: Move to `core` package)
147
+ - **`formFileMetadata(owner, bucketId, location, fingerprint, size)`** - Create FileMetadata instance (TODO: Move to `core` package under `FileMetadata`)
148
+ - **`computeFileKey(metadata)`** - Compute file key from FileMetadata (TODO: This already exists in `core` package under `FileManager`, but requires a file stream to be provided, make it more flexible there and remove it here)
149
+ - **`downloadByKey(fileKey)`** - Download file by key
145
150
  - Returns: `{ stream: ReadableStream, status: string }`
146
151
  - **`listBuckets()`** - List all buckets of the currently authenticated user
147
152
  - **`getBucket(bucketId)`** - Get the metadata of a specific bucket
@@ -1,14 +1,27 @@
1
- import type { Bucket, DownloadOptions, DownloadResult, FileListResponse, GetFilesOptions, HealthStatus, NonceResponse, UploadOptions, UploadReceipt, VerifyResponse } from './types.js';
1
+ import type { Bucket, DownloadOptions, DownloadResult, FileInfo, FileListResponse, GetFilesOptions, HealthStatus, InfoResponse, NonceResponse, StatsResponse, UploadOptions, UploadReceipt, ValueProp, VerifyResponse } from './types.js';
2
2
  import type { HttpClientConfig } from '@storagehub-sdk/core';
3
+ import { FileMetadata } from '@storagehub-sdk/core';
3
4
  export declare class MspClient {
4
5
  readonly config: HttpClientConfig;
5
6
  private readonly http;
6
- private token?;
7
+ token?: string;
7
8
  private constructor();
8
9
  static connect(config: HttpClientConfig): Promise<MspClient>;
9
10
  getHealth(options?: {
10
11
  signal?: AbortSignal;
11
12
  }): Promise<HealthStatus>;
13
+ /** Get general MSP information */
14
+ getInfo(options?: {
15
+ signal?: AbortSignal;
16
+ }): Promise<InfoResponse>;
17
+ /** Get MSP statistics */
18
+ getStats(options?: {
19
+ signal?: AbortSignal;
20
+ }): Promise<StatsResponse>;
21
+ /** Get available value propositions */
22
+ getValuePropositions(options?: {
23
+ signal?: AbortSignal;
24
+ }): Promise<ValueProp[]>;
12
25
  /** Request a SIWE-style nonce message for the given address and chainId */
13
26
  getNonce(address: string, chainId: number, options?: {
14
27
  signal?: AbortSignal;
@@ -31,18 +44,25 @@ export declare class MspClient {
31
44
  }): Promise<Bucket>;
32
45
  /** Gets the list of files and folders under the specified path for a bucket. If no path is provided, it returns the files and folders found at root. */
33
46
  getFiles(bucketId: string, options?: GetFilesOptions): Promise<FileListResponse>;
47
+ /** Get metadata for a file in a bucket by fileKey */
48
+ getFileInfo(bucketId: string, fileKey: string, options?: {
49
+ signal?: AbortSignal;
50
+ }): Promise<FileInfo>;
34
51
  /**
35
52
  * Upload a file to a bucket with a specific key.
36
53
  *
37
- * For small files (Blob, ArrayBuffer, Uint8Array), uses multipart/form-data upload.
38
- * For large files (ReadableStream), uses memory-efficient streaming upload with
39
- * application/octet-stream to prevent loading entire file into memory.
54
+ * Always uses multipart/form-data upload with both file data and encoded FileMetadata.
55
+ * The file data is loaded into memory to create the multipart request.
40
56
  *
41
57
  */
42
- uploadFile(bucketId: string, fileKey: string, file: Blob | ArrayBuffer | Uint8Array | ReadableStream<Uint8Array> | unknown, _options?: UploadOptions): Promise<UploadReceipt>;
58
+ uploadFile(bucketId: string, fileKey: string, file: Blob | ArrayBuffer | Uint8Array | ReadableStream<Uint8Array> | unknown, owner: string, location: string, _options?: UploadOptions): Promise<UploadReceipt>;
43
59
  private coerceToFormPart;
44
- /** Download a file by bucket and key. */
45
- downloadByKey(bucketId: string, fileKey: string, options?: DownloadOptions): Promise<DownloadResult>;
60
+ private computeFileFingerprint;
61
+ formFileMetadata(owner: string, bucketId: string, location: string, fingerprint: Uint8Array, size: bigint): Promise<FileMetadata>;
62
+ hexToBytes(hex: string): Uint8Array;
63
+ computeFileKey(fileMetadata: FileMetadata): Promise<Uint8Array>;
64
+ /** Download a file by key. */
65
+ downloadByKey(fileKey: string, options?: DownloadOptions): Promise<DownloadResult>;
46
66
  /** Download a file by its location path under a bucket. */
47
67
  downloadByLocation(bucketId: string, filePath: string, options?: DownloadOptions): Promise<DownloadResult>;
48
68
  }
@@ -1,2 +1,2 @@
1
- import{HttpClient as R}from"@storagehub-sdk/core";var y=class m{config;http;token;constructor(e,n){this.config=e,this.http=n}static async connect(e){if(!e?.baseUrl)throw new Error("MspClient.connect: baseUrl is required");let n=new R({baseUrl:e.baseUrl,...e.timeoutMs!==void 0&&{timeoutMs:e.timeoutMs},...e.defaultHeaders!==void 0&&{defaultHeaders:e.defaultHeaders},...e.fetchImpl!==void 0&&{fetchImpl:e.fetchImpl}});return new m(e,n)}getHealth(e){return this.http.get("/health",{...e?.signal!==void 0&&{signal:e.signal}})}getNonce(e,n,t){return this.http.post("/auth/nonce",{body:{address:e,chainId:n},headers:{"Content-Type":"application/json"},...t?.signal!==void 0&&{signal:t.signal}})}verify(e,n,t){return this.http.post("/auth/verify",{body:{message:e,signature:n},headers:{"Content-Type":"application/json"},...t?.signal!==void 0&&{signal:t.signal}})}setToken(e){this.token=e}withAuth(e){return this.token?{...e??{},Authorization:`Bearer ${this.token}`}:e}listBuckets(e){let n=this.withAuth();return this.http.get("/buckets",{...n?{headers:n}:{},...e?.signal?{signal:e.signal}:{}})}getBucket(e,n){let t=this.withAuth(),a=`/buckets/${encodeURIComponent(e)}`;return this.http.get(a,{...t?{headers:t}:{},...n?.signal?{signal:n.signal}:{}})}getFiles(e,n){let t=this.withAuth(),a=`/buckets/${encodeURIComponent(e)}/files`;return this.http.get(a,{...t?{headers:t}:{},...n?.signal?{signal:n.signal}:{},...n?.path?{query:{path:n.path.replace(/^\/+/,"")}}:{}})}async uploadFile(e,n,t,a){let i=`/buckets/${encodeURIComponent(e)}/upload/${encodeURIComponent(n)}`,o=this.withAuth();if(t instanceof ReadableStream)return await this.http.put(i,o?{body:t,headers:{...o,"Content-Type":"application/octet-stream"}}:{body:t,headers:{"Content-Type":"application/octet-stream"}});let s=new FormData,l=await this.coerceToFormPart(t);return s.append("file",l,"file"),await this.http.put(i,o?{body:s,headers:o}:{body:s})}async coerceToFormPart(e){return typeof Blob<"u"&&e instanceof Blob?e:e instanceof Uint8Array?new Blob([e]):typeof ArrayBuffer<"u"&&e instanceof ArrayBuffer?new Blob([e]):new Blob([e])}async downloadByKey(e,n,t){let a=`/buckets/${encodeURIComponent(e)}/download/${encodeURIComponent(n)}`,i={Accept:"*/*"};if(t?.range){let{start:d,end:h}=t.range,g=`bytes=${d}-${h??""}`;i.Range=g}let o=this.withAuth(i),s=await this.http.getRaw(a,{...o?{headers:o}:{},...t?.signal?{signal:t.signal}:{}});if(!s.body)throw new Error("Response body is null - unable to create stream");let l=s.headers.get("content-type"),r=s.headers.get("content-range"),c=s.headers.get("content-length"),p=c!==null?Number(c):void 0,u=typeof p=="number"&&Number.isFinite(p)?p:null;return{stream:s.body,status:s.status,contentType:l,contentRange:r,contentLength:u}}async downloadByLocation(e,n,t){let i=n.replace(/^\/+/,"").split("/").map(encodeURIComponent).join("/"),o=`/buckets/${encodeURIComponent(e)}/download/path/${i}`,s={Accept:"*/*"};if(t?.range){let{start:g,end:b}=t.range,f=`bytes=${g}-${b??""}`;s.Range=f}let l=this.withAuth(s),r=await this.http.getRaw(o,{...l?{headers:l}:{},...t?.signal?{signal:t.signal}:{}});if(!r.body)throw new Error("Response body is null - unable to create stream");let c=r.headers.get("content-type"),p=r.headers.get("content-range"),u=r.headers.get("content-length"),d=u!==null?Number(u):void 0,h=typeof d=="number"&&Number.isFinite(d)?d:null;return{stream:r.body,status:r.status,contentType:c,contentRange:p,contentLength:h}}};export{y as MspClient};
1
+ import{FileMetadata as B,FileTrie as F,HttpClient as U,initWasm as m}from"@storagehub-sdk/core";var b=class w{config;http;token;constructor(e,t){this.config=e,this.http=t}static async connect(e){if(!e?.baseUrl)throw new Error("MspClient.connect: baseUrl is required");let t=new U({baseUrl:e.baseUrl,...e.timeoutMs!==void 0&&{timeoutMs:e.timeoutMs},...e.defaultHeaders!==void 0&&{defaultHeaders:e.defaultHeaders},...e.fetchImpl!==void 0&&{fetchImpl:e.fetchImpl}});return new w(e,t)}getHealth(e){return this.http.get("/health",{...e?.signal!==void 0&&{signal:e.signal}})}getInfo(e){return this.http.get("/info",{...e?.signal!==void 0&&{signal:e.signal}})}getStats(e){return this.http.get("/stats",{...e?.signal!==void 0&&{signal:e.signal}})}getValuePropositions(e){return this.http.get("/value-props",{...e?.signal!==void 0&&{signal:e.signal}})}getNonce(e,t,n){return this.http.post("/auth/nonce",{body:{address:e,chainId:t},headers:{"Content-Type":"application/json"},...n?.signal!==void 0&&{signal:n.signal}})}verify(e,t,n){return this.http.post("/auth/verify",{body:{message:e,signature:t},headers:{"Content-Type":"application/json"},...n?.signal!==void 0&&{signal:n.signal}})}setToken(e){this.token=e}withAuth(e){return this.token?{...e??{},Authorization:`Bearer ${this.token}`}:e}listBuckets(e){let t=this.withAuth();return this.http.get("/buckets",{...t?{headers:t}:{},...e?.signal?{signal:e.signal}:{}})}getBucket(e,t){let n=this.withAuth(),a=`/buckets/${encodeURIComponent(e)}`;return this.http.get(a,{...n?{headers:n}:{},...t?.signal?{signal:t.signal}:{}})}getFiles(e,t){let n=this.withAuth(),a=`/buckets/${encodeURIComponent(e)}/files`;return this.http.get(a,{...n?{headers:n}:{},...t?.signal?{signal:t.signal}:{},...t?.path?{query:{path:t.path.replace(/^\/+/,"")}}:{}})}getFileInfo(e,t,n){let a=this.withAuth(),o=`/buckets/${encodeURIComponent(e)}/info/${encodeURIComponent(t)}`;return this.http.get(o,{...a?{headers:a}:{},...n?.signal?{signal:n.signal}:{}}).then(s=>({...s,uploadedAt:new Date(s.uploadedAt)}))}async uploadFile(e,t,n,a,o,s){await m();let r=`/buckets/${encodeURIComponent(e)}/upload/${encodeURIComponent(t)}`,i=this.withAuth(),l=await this.coerceToFormPart(n),d=l.size,u=await this.computeFileFingerprint(l),p=await this.formFileMetadata(a,e,o,u,BigInt(d)),c=await this.computeFileKey(p),g=this.hexToBytes(t);if(c.length!==g.length||!c.every((R,A)=>R===g[A]))throw new Error(`Computed file key ${c.toString()} does not match provided file key ${g.toString()}`);let f=p.encode(),h=new FormData,y=new Blob([new Uint8Array(f)],{type:"application/octet-stream"});return h.append("file_metadata",y,"file_metadata"),h.append("file",l,"file"),await this.http.put(r,i?{body:h,headers:i}:{body:h})}async coerceToFormPart(e){if(typeof Blob<"u"&&e instanceof Blob)return e;if(e instanceof Uint8Array)return new Blob([e.buffer]);if(typeof ArrayBuffer<"u"&&e instanceof ArrayBuffer)return new Blob([e]);if(e instanceof ReadableStream){let t=e.getReader(),n=[],a=0;try{for(;;){let{done:r,value:i}=await t.read();if(r)break;i&&(n.push(i),a+=i.length)}}finally{t.releaseLock()}let o=new Uint8Array(a),s=0;for(let r of n)o.set(r,s),s+=r.length;return new Blob([o],{type:"application/octet-stream"})}return new Blob([e],{type:"application/octet-stream"})}async computeFileFingerprint(e){let t=new F,n=new Uint8Array(await e.arrayBuffer()),a=1024,o=0;for(;o<n.length;){let s=Math.min(o+a,n.length),r=n.slice(o,s);t.push_chunk(r),o=s}return t.get_root()}async formFileMetadata(e,t,n,a,o){let s=this.hexToBytes(e),r=this.hexToBytes(t),i=new TextEncoder().encode(n);return await m(),new B(s,r,i,o,a)}hexToBytes(e){if(!e)throw new Error("hex string cannot be empty");let t=e.startsWith("0x")?e.slice(2):e;if(t.length%2!==0)throw new Error("hex string must have an even number of characters");if(!/^[0-9a-fA-F]*$/.test(t))throw new Error("hex string contains invalid characters");return new Uint8Array(t.match(/.{2}/g)?.map(n=>Number.parseInt(n,16))||[])}async computeFileKey(e){return await m(),e.getFileKey()}async downloadByKey(e,t){let n=`/download/${encodeURIComponent(e)}`,a={Accept:"*/*"};if(t?.range){let{start:p,end:c}=t.range,g=`bytes=${p}-${c??""}`;a.Range=g}let o=this.withAuth(a),s=await this.http.getRaw(n,{...o?{headers:o}:{},...t?.signal?{signal:t.signal}:{}});if(!s.body)throw new Error("Response body is null - unable to create stream");let r=s.headers.get("content-type"),i=s.headers.get("content-range"),l=s.headers.get("content-length"),d=l!==null?Number(l):void 0,u=typeof d=="number"&&Number.isFinite(d)?d:null;return{stream:s.body,status:s.status,contentType:r,contentRange:i,contentLength:u}}async downloadByLocation(e,t,n){let o=t.replace(/^\/+/,"").split("/").map(encodeURIComponent).join("/"),s=`/buckets/${encodeURIComponent(e)}/download/path/${o}`,r={Accept:"*/*"};if(n?.range){let{start:f,end:h}=n.range,y=`bytes=${f}-${h??""}`;r.Range=y}let i=this.withAuth(r),l=await this.http.getRaw(s,{...i?{headers:i}:{},...n?.signal?{signal:n.signal}:{}});if(!l.body)throw new Error("Response body is null - unable to create stream");let d=l.headers.get("content-type"),u=l.headers.get("content-range"),p=l.headers.get("content-length"),c=p!==null?Number(p):void 0,g=typeof c=="number"&&Number.isFinite(c)?c:null;return{stream:l.body,status:l.status,contentType:d,contentRange:u,contentLength:g}}};export{b as MspClient};
2
2
  //# sourceMappingURL=index.browser.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/MspClient.ts"],
4
- "sourcesContent": ["import type {\n Bucket,\n DownloadOptions,\n DownloadResult,\n FileListResponse,\n GetFilesOptions,\n HealthStatus,\n NonceResponse,\n UploadOptions,\n UploadReceipt,\n VerifyResponse,\n} from './types.js';\nimport type { HttpClientConfig } from '@storagehub-sdk/core';\nimport { HttpClient } from '@storagehub-sdk/core';\n\nexport class MspClient {\n public readonly config: HttpClientConfig;\n private readonly http: HttpClient;\n private token?: string;\n\n private constructor(config: HttpClientConfig, http: HttpClient) {\n this.config = config;\n this.http = http;\n }\n\n static async connect(config: HttpClientConfig): Promise<MspClient> {\n if (!config?.baseUrl) throw new Error('MspClient.connect: baseUrl is required');\n\n const http = new HttpClient({\n baseUrl: config.baseUrl,\n ...(config.timeoutMs !== undefined && { timeoutMs: config.timeoutMs }),\n ...(config.defaultHeaders !== undefined && { defaultHeaders: config.defaultHeaders }),\n ...(config.fetchImpl !== undefined && { fetchImpl: config.fetchImpl }),\n });\n\n return new MspClient(config, http);\n }\n\n getHealth(options?: { signal?: AbortSignal }): Promise<HealthStatus> {\n return this.http.get<HealthStatus>('/health', {\n ...(options?.signal !== undefined && { signal: options.signal }),\n });\n }\n\n // Auth endpoints:\n\n /** Request a SIWE-style nonce message for the given address and chainId */\n getNonce(\n address: string,\n chainId: number,\n options?: { signal?: AbortSignal },\n ): Promise<NonceResponse> {\n return this.http.post<NonceResponse>('/auth/nonce', {\n body: { address, chainId },\n headers: { 'Content-Type': 'application/json' },\n ...(options?.signal !== undefined && { signal: options.signal }),\n });\n }\n\n /** Verify signed message and receive JWT token */\n verify(\n message: string,\n signature: string,\n options?: { signal?: AbortSignal },\n ): Promise<VerifyResponse> {\n return this.http.post<VerifyResponse>('/auth/verify', {\n body: { message, signature },\n headers: { 'Content-Type': 'application/json' },\n ...(options?.signal !== undefined && { signal: options.signal }),\n });\n }\n\n /** Store token to be sent on subsequent protected requests */\n setToken(token: string): void {\n this.token = token;\n }\n\n /** Merge Authorization header when token is present */\n private withAuth(headers?: Record<string, string>): Record<string, string> | undefined {\n if (!this.token) return headers;\n return { ...(headers ?? {}), Authorization: `Bearer ${this.token}` };\n }\n\n // Bucket endpoints:\n\n /** List all buckets for the current authenticateduser */\n listBuckets(options?: { signal?: AbortSignal }): Promise<Bucket[]> {\n const headers = this.withAuth();\n return this.http.get<Bucket[]>('/buckets', {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n });\n }\n\n /** Get a specific bucket's metadata by its bucket ID */\n getBucket(bucketId: string, options?: { signal?: AbortSignal }): Promise<Bucket> {\n const headers = this.withAuth();\n const path = `/buckets/${encodeURIComponent(bucketId)}`;\n return this.http.get<Bucket>(path, {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n });\n }\n\n /** Gets the list of files and folders under the specified path for a bucket. If no path is provided, it returns the files and folders found at root. */\n getFiles(bucketId: string, options?: GetFilesOptions): Promise<FileListResponse> {\n const headers = this.withAuth();\n const path = `/buckets/${encodeURIComponent(bucketId)}/files`;\n return this.http.get<FileListResponse>(path, {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n ...(options?.path ? { query: { path: options.path.replace(/^\\/+/, '') } } : {}),\n });\n }\n\n // File endpoints:\n\n /**\n * Upload a file to a bucket with a specific key.\n *\n * For small files (Blob, ArrayBuffer, Uint8Array), uses multipart/form-data upload.\n * For large files (ReadableStream), uses memory-efficient streaming upload with\n * application/octet-stream to prevent loading entire file into memory.\n *\n */\n async uploadFile(\n bucketId: string,\n fileKey: string,\n file: Blob | ArrayBuffer | Uint8Array | ReadableStream<Uint8Array> | unknown,\n _options?: UploadOptions,\n ): Promise<UploadReceipt> {\n void _options;\n\n const path = `/buckets/${encodeURIComponent(bucketId)}/upload/${encodeURIComponent(fileKey)}`;\n const authHeaders = this.withAuth();\n\n // For ReadableStream, use direct streaming upload (memory efficient)\n if (file instanceof ReadableStream) {\n const res = await this.http.put<UploadReceipt>(\n path,\n authHeaders\n ? {\n body: file,\n headers: {\n ...authHeaders,\n 'Content-Type': 'application/octet-stream',\n },\n }\n : {\n body: file,\n headers: { 'Content-Type': 'application/octet-stream' },\n },\n );\n return res;\n }\n\n // For other types, use FormData (traditional multipart upload)\n const form = new FormData();\n const part = await this.coerceToFormPart(file);\n form.append('file', part as Blob, 'file'); // part is now guaranteed to be Blob\n\n const res = await this.http.put<UploadReceipt>(\n path,\n authHeaders\n ? { body: form as unknown as BodyInit, headers: authHeaders }\n : { body: form as unknown as BodyInit },\n );\n return res;\n }\n\n private async coerceToFormPart(file: Blob | ArrayBuffer | Uint8Array | unknown): Promise<Blob> {\n if (typeof Blob !== 'undefined' && file instanceof Blob) return file;\n if (file instanceof Uint8Array) return new Blob([file]);\n if (typeof ArrayBuffer !== 'undefined' && file instanceof ArrayBuffer) return new Blob([file]);\n\n return new Blob([file as BlobPart]);\n }\n\n /** Download a file by bucket and key. */\n async downloadByKey(\n bucketId: string,\n fileKey: string,\n options?: DownloadOptions,\n ): Promise<DownloadResult> {\n const path = `/buckets/${encodeURIComponent(bucketId)}/download/${encodeURIComponent(fileKey)}`;\n const baseHeaders: Record<string, string> = { Accept: '*/*' };\n if (options?.range) {\n const { start, end } = options.range;\n const rangeValue = `bytes=${start}-${end ?? ''}`;\n baseHeaders.Range = rangeValue;\n }\n const headers = this.withAuth(baseHeaders);\n const res = await this.http.getRaw(path, {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n });\n\n if (!res.body) {\n throw new Error('Response body is null - unable to create stream');\n }\n\n const contentType = res.headers.get('content-type');\n const contentRange = res.headers.get('content-range');\n const contentLengthHeader = res.headers.get('content-length');\n const parsedLength = contentLengthHeader !== null ? Number(contentLengthHeader) : undefined;\n const contentLength =\n typeof parsedLength === 'number' && Number.isFinite(parsedLength) ? parsedLength : null;\n\n return {\n stream: res.body,\n status: res.status,\n contentType,\n contentRange,\n contentLength,\n };\n }\n\n /** Download a file by its location path under a bucket. */\n async downloadByLocation(\n bucketId: string,\n filePath: string,\n options?: DownloadOptions,\n ): Promise<DownloadResult> {\n const normalized = filePath.replace(/^\\/+/, '');\n const encodedPath = normalized.split('/').map(encodeURIComponent).join('/');\n const path = `/buckets/${encodeURIComponent(bucketId)}/download/path/${encodedPath}`;\n const baseHeaders: Record<string, string> = { Accept: '*/*' };\n if (options?.range) {\n const { start, end } = options.range;\n const rangeValue = `bytes=${start}-${end ?? ''}`;\n baseHeaders.Range = rangeValue;\n }\n const headers = this.withAuth(baseHeaders);\n const res = await this.http.getRaw(path, {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n });\n\n if (!res.body) {\n throw new Error('Response body is null - unable to create stream');\n }\n\n const contentType = res.headers.get('content-type');\n const contentRange = res.headers.get('content-range');\n const contentLengthHeader = res.headers.get('content-length');\n const parsedLength = contentLengthHeader !== null ? Number(contentLengthHeader) : undefined;\n const contentLength =\n typeof parsedLength === 'number' && Number.isFinite(parsedLength) ? parsedLength : null;\n\n return {\n stream: res.body,\n status: res.status,\n contentType,\n contentRange,\n contentLength,\n };\n }\n}\n"],
5
- "mappings": "AAaA,OAAS,cAAAA,MAAkB,uBAEpB,IAAMC,EAAN,MAAMC,CAAU,CACL,OACC,KACT,MAEA,YAAYC,EAA0BC,EAAkB,CAC9D,KAAK,OAASD,EACd,KAAK,KAAOC,CACd,CAEA,aAAa,QAAQD,EAA8C,CACjE,GAAI,CAACA,GAAQ,QAAS,MAAM,IAAI,MAAM,wCAAwC,EAE9E,IAAMC,EAAO,IAAIJ,EAAW,CAC1B,QAASG,EAAO,QAChB,GAAIA,EAAO,YAAc,QAAa,CAAE,UAAWA,EAAO,SAAU,EACpE,GAAIA,EAAO,iBAAmB,QAAa,CAAE,eAAgBA,EAAO,cAAe,EACnF,GAAIA,EAAO,YAAc,QAAa,CAAE,UAAWA,EAAO,SAAU,CACtE,CAAC,EAED,OAAO,IAAID,EAAUC,EAAQC,CAAI,CACnC,CAEA,UAAUC,EAA2D,CACnE,OAAO,KAAK,KAAK,IAAkB,UAAW,CAC5C,GAAIA,GAAS,SAAW,QAAa,CAAE,OAAQA,EAAQ,MAAO,CAChE,CAAC,CACH,CAKA,SACEC,EACAC,EACAF,EACwB,CACxB,OAAO,KAAK,KAAK,KAAoB,cAAe,CAClD,KAAM,CAAE,QAAAC,EAAS,QAAAC,CAAQ,EACzB,QAAS,CAAE,eAAgB,kBAAmB,EAC9C,GAAIF,GAAS,SAAW,QAAa,CAAE,OAAQA,EAAQ,MAAO,CAChE,CAAC,CACH,CAGA,OACEG,EACAC,EACAJ,EACyB,CACzB,OAAO,KAAK,KAAK,KAAqB,eAAgB,CACpD,KAAM,CAAE,QAAAG,EAAS,UAAAC,CAAU,EAC3B,QAAS,CAAE,eAAgB,kBAAmB,EAC9C,GAAIJ,GAAS,SAAW,QAAa,CAAE,OAAQA,EAAQ,MAAO,CAChE,CAAC,CACH,CAGA,SAASK,EAAqB,CAC5B,KAAK,MAAQA,CACf,CAGQ,SAASC,EAAsE,CACrF,OAAK,KAAK,MACH,CAAE,GAAIA,GAAW,CAAC,EAAI,cAAe,UAAU,KAAK,KAAK,EAAG,EAD3CA,CAE1B,CAKA,YAAYN,EAAuD,CACjE,IAAMM,EAAU,KAAK,SAAS,EAC9B,OAAO,KAAK,KAAK,IAAc,WAAY,CACzC,GAAIA,EAAU,CAAE,QAAAA,CAAQ,EAAI,CAAC,EAC7B,GAAIN,GAAS,OAAS,CAAE,OAAQA,EAAQ,MAAO,EAAI,CAAC,CACtD,CAAC,CACH,CAGA,UAAUO,EAAkBP,EAAqD,CAC/E,IAAMM,EAAU,KAAK,SAAS,EACxBE,EAAO,YAAY,mBAAmBD,CAAQ,CAAC,GACrD,OAAO,KAAK,KAAK,IAAYC,EAAM,CACjC,GAAIF,EAAU,CAAE,QAAAA,CAAQ,EAAI,CAAC,EAC7B,GAAIN,GAAS,OAAS,CAAE,OAAQA,EAAQ,MAAO,EAAI,CAAC,CACtD,CAAC,CACH,CAGA,SAASO,EAAkBP,EAAsD,CAC/E,IAAMM,EAAU,KAAK,SAAS,EACxBE,EAAO,YAAY,mBAAmBD,CAAQ,CAAC,SACrD,OAAO,KAAK,KAAK,IAAsBC,EAAM,CAC3C,GAAIF,EAAU,CAAE,QAAAA,CAAQ,EAAI,CAAC,EAC7B,GAAIN,GAAS,OAAS,CAAE,OAAQA,EAAQ,MAAO,EAAI,CAAC,EACpD,GAAIA,GAAS,KAAO,CAAE,MAAO,CAAE,KAAMA,EAAQ,KAAK,QAAQ,OAAQ,EAAE,CAAE,CAAE,EAAI,CAAC,CAC/E,CAAC,CACH,CAYA,MAAM,WACJO,EACAE,EACAC,EACAC,EACwB,CAGxB,IAAMH,EAAO,YAAY,mBAAmBD,CAAQ,CAAC,WAAW,mBAAmBE,CAAO,CAAC,GACrFG,EAAc,KAAK,SAAS,EAGlC,GAAIF,aAAgB,eAgBlB,OAfY,MAAM,KAAK,KAAK,IAC1BF,EACAI,EACI,CACE,KAAMF,EACN,QAAS,CACP,GAAGE,EACH,eAAgB,0BAClB,CACF,EACA,CACE,KAAMF,EACN,QAAS,CAAE,eAAgB,0BAA2B,CACxD,CACN,EAKF,IAAMG,EAAO,IAAI,SACXC,EAAO,MAAM,KAAK,iBAAiBJ,CAAI,EAC7C,OAAAG,EAAK,OAAO,OAAQC,EAAc,MAAM,EAE5B,MAAM,KAAK,KAAK,IAC1BN,EACAI,EACI,CAAE,KAAMC,EAA6B,QAASD,CAAY,EAC1D,CAAE,KAAMC,CAA4B,CAC1C,CAEF,CAEA,MAAc,iBAAiBH,EAAgE,CAC7F,OAAI,OAAO,KAAS,KAAeA,aAAgB,KAAaA,EAC5DA,aAAgB,WAAmB,IAAI,KAAK,CAACA,CAAI,CAAC,EAClD,OAAO,YAAgB,KAAeA,aAAgB,YAAoB,IAAI,KAAK,CAACA,CAAI,CAAC,EAEtF,IAAI,KAAK,CAACA,CAAgB,CAAC,CACpC,CAGA,MAAM,cACJH,EACAE,EACAT,EACyB,CACzB,IAAMQ,EAAO,YAAY,mBAAmBD,CAAQ,CAAC,aAAa,mBAAmBE,CAAO,CAAC,GACvFM,EAAsC,CAAE,OAAQ,KAAM,EAC5D,GAAIf,GAAS,MAAO,CAClB,GAAM,CAAE,MAAAgB,EAAO,IAAAC,CAAI,EAAIjB,EAAQ,MACzBkB,EAAa,SAASF,CAAK,IAAIC,GAAO,EAAE,GAC9CF,EAAY,MAAQG,CACtB,CACA,IAAMZ,EAAU,KAAK,SAASS,CAAW,EACnCI,EAAM,MAAM,KAAK,KAAK,OAAOX,EAAM,CACvC,GAAIF,EAAU,CAAE,QAAAA,CAAQ,EAAI,CAAC,EAC7B,GAAIN,GAAS,OAAS,CAAE,OAAQA,EAAQ,MAAO,EAAI,CAAC,CACtD,CAAC,EAED,GAAI,CAACmB,EAAI,KACP,MAAM,IAAI,MAAM,iDAAiD,EAGnE,IAAMC,EAAcD,EAAI,QAAQ,IAAI,cAAc,EAC5CE,EAAeF,EAAI,QAAQ,IAAI,eAAe,EAC9CG,EAAsBH,EAAI,QAAQ,IAAI,gBAAgB,EACtDI,EAAeD,IAAwB,KAAO,OAAOA,CAAmB,EAAI,OAC5EE,EACJ,OAAOD,GAAiB,UAAY,OAAO,SAASA,CAAY,EAAIA,EAAe,KAErF,MAAO,CACL,OAAQJ,EAAI,KACZ,OAAQA,EAAI,OACZ,YAAAC,EACA,aAAAC,EACA,cAAAG,CACF,CACF,CAGA,MAAM,mBACJjB,EACAkB,EACAzB,EACyB,CAEzB,IAAM0B,EADaD,EAAS,QAAQ,OAAQ,EAAE,EACf,MAAM,GAAG,EAAE,IAAI,kBAAkB,EAAE,KAAK,GAAG,EACpEjB,EAAO,YAAY,mBAAmBD,CAAQ,CAAC,kBAAkBmB,CAAW,GAC5EX,EAAsC,CAAE,OAAQ,KAAM,EAC5D,GAAIf,GAAS,MAAO,CAClB,GAAM,CAAE,MAAAgB,EAAO,IAAAC,CAAI,EAAIjB,EAAQ,MACzBkB,EAAa,SAASF,CAAK,IAAIC,GAAO,EAAE,GAC9CF,EAAY,MAAQG,CACtB,CACA,IAAMZ,EAAU,KAAK,SAASS,CAAW,EACnCI,EAAM,MAAM,KAAK,KAAK,OAAOX,EAAM,CACvC,GAAIF,EAAU,CAAE,QAAAA,CAAQ,EAAI,CAAC,EAC7B,GAAIN,GAAS,OAAS,CAAE,OAAQA,EAAQ,MAAO,EAAI,CAAC,CACtD,CAAC,EAED,GAAI,CAACmB,EAAI,KACP,MAAM,IAAI,MAAM,iDAAiD,EAGnE,IAAMC,EAAcD,EAAI,QAAQ,IAAI,cAAc,EAC5CE,EAAeF,EAAI,QAAQ,IAAI,eAAe,EAC9CG,EAAsBH,EAAI,QAAQ,IAAI,gBAAgB,EACtDI,EAAeD,IAAwB,KAAO,OAAOA,CAAmB,EAAI,OAC5EE,EACJ,OAAOD,GAAiB,UAAY,OAAO,SAASA,CAAY,EAAIA,EAAe,KAErF,MAAO,CACL,OAAQJ,EAAI,KACZ,OAAQA,EAAI,OACZ,YAAAC,EACA,aAAAC,EACA,cAAAG,CACF,CACF,CACF",
6
- "names": ["HttpClient", "MspClient", "_MspClient", "config", "http", "options", "address", "chainId", "message", "signature", "token", "headers", "bucketId", "path", "fileKey", "file", "_options", "authHeaders", "form", "part", "baseHeaders", "start", "end", "rangeValue", "res", "contentType", "contentRange", "contentLengthHeader", "parsedLength", "contentLength", "filePath", "encodedPath"]
4
+ "sourcesContent": ["import type {\n Bucket,\n DownloadOptions,\n DownloadResult,\n FileInfo,\n FileListResponse,\n GetFilesOptions,\n HealthStatus,\n InfoResponse,\n NonceResponse,\n StatsResponse,\n UploadOptions,\n UploadReceipt,\n ValueProp,\n VerifyResponse,\n} from './types.js';\nimport type { HttpClientConfig } from '@storagehub-sdk/core';\nimport { FileMetadata, FileTrie, HttpClient, initWasm } from '@storagehub-sdk/core';\n\nexport class MspClient {\n public readonly config: HttpClientConfig;\n private readonly http: HttpClient;\n public token?: string;\n\n private constructor(config: HttpClientConfig, http: HttpClient) {\n this.config = config;\n this.http = http;\n }\n\n static async connect(config: HttpClientConfig): Promise<MspClient> {\n if (!config?.baseUrl) throw new Error('MspClient.connect: baseUrl is required');\n\n const http = new HttpClient({\n baseUrl: config.baseUrl,\n ...(config.timeoutMs !== undefined && { timeoutMs: config.timeoutMs }),\n ...(config.defaultHeaders !== undefined && { defaultHeaders: config.defaultHeaders }),\n ...(config.fetchImpl !== undefined && { fetchImpl: config.fetchImpl }),\n });\n\n return new MspClient(config, http);\n }\n\n getHealth(options?: { signal?: AbortSignal }): Promise<HealthStatus> {\n return this.http.get<HealthStatus>('/health', {\n ...(options?.signal !== undefined && { signal: options.signal }),\n });\n }\n\n /** Get general MSP information */\n getInfo(options?: { signal?: AbortSignal }): Promise<InfoResponse> {\n return this.http.get<InfoResponse>('/info', {\n ...(options?.signal !== undefined && { signal: options.signal }),\n });\n }\n\n /** Get MSP statistics */\n getStats(options?: { signal?: AbortSignal }): Promise<StatsResponse> {\n return this.http.get<StatsResponse>('/stats', {\n ...(options?.signal !== undefined && { signal: options.signal }),\n });\n }\n\n /** Get available value propositions */\n getValuePropositions(options?: { signal?: AbortSignal }): Promise<ValueProp[]> {\n return this.http.get<ValueProp[]>('/value-props', {\n ...(options?.signal !== undefined && { signal: options.signal }),\n });\n }\n\n // Auth endpoints:\n\n /** Request a SIWE-style nonce message for the given address and chainId */\n getNonce(\n address: string,\n chainId: number,\n options?: { signal?: AbortSignal },\n ): Promise<NonceResponse> {\n return this.http.post<NonceResponse>('/auth/nonce', {\n body: { address, chainId },\n headers: { 'Content-Type': 'application/json' },\n ...(options?.signal !== undefined && { signal: options.signal }),\n });\n }\n\n /** Verify signed message and receive JWT token */\n verify(\n message: string,\n signature: string,\n options?: { signal?: AbortSignal },\n ): Promise<VerifyResponse> {\n return this.http.post<VerifyResponse>('/auth/verify', {\n body: { message, signature },\n headers: { 'Content-Type': 'application/json' },\n ...(options?.signal !== undefined && { signal: options.signal }),\n });\n }\n\n /** Store token to be sent on subsequent protected requests */\n setToken(token: string): void {\n this.token = token;\n }\n\n /** Merge Authorization header when token is present */\n private withAuth(headers?: Record<string, string>): Record<string, string> | undefined {\n if (!this.token) return headers;\n return { ...(headers ?? {}), Authorization: `Bearer ${this.token}` };\n }\n\n // Bucket endpoints:\n\n /** List all buckets for the current authenticateduser */\n listBuckets(options?: { signal?: AbortSignal }): Promise<Bucket[]> {\n const headers = this.withAuth();\n return this.http.get<Bucket[]>('/buckets', {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n });\n }\n\n /** Get a specific bucket's metadata by its bucket ID */\n getBucket(bucketId: string, options?: { signal?: AbortSignal }): Promise<Bucket> {\n const headers = this.withAuth();\n const path = `/buckets/${encodeURIComponent(bucketId)}`;\n return this.http.get<Bucket>(path, {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n });\n }\n\n /** Gets the list of files and folders under the specified path for a bucket. If no path is provided, it returns the files and folders found at root. */\n getFiles(bucketId: string, options?: GetFilesOptions): Promise<FileListResponse> {\n const headers = this.withAuth();\n const path = `/buckets/${encodeURIComponent(bucketId)}/files`;\n return this.http.get<FileListResponse>(path, {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n ...(options?.path ? { query: { path: options.path.replace(/^\\/+/, '') } } : {}),\n });\n }\n\n /** Get metadata for a file in a bucket by fileKey */\n getFileInfo(\n bucketId: string,\n fileKey: string,\n options?: { signal?: AbortSignal },\n ): Promise<FileInfo> {\n const headers = this.withAuth();\n const path = `/buckets/${encodeURIComponent(bucketId)}/info/${encodeURIComponent(fileKey)}`;\n type FileInfoWire = Omit<FileInfo, 'uploadedAt'> & { uploadedAt: string };\n return this.http\n .get<FileInfoWire>(path, {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n })\n .then((wire): FileInfo => ({ ...wire, uploadedAt: new Date(wire.uploadedAt) }));\n }\n\n // File endpoints:\n\n /**\n * Upload a file to a bucket with a specific key.\n *\n * Always uses multipart/form-data upload with both file data and encoded FileMetadata.\n * The file data is loaded into memory to create the multipart request.\n *\n */\n async uploadFile(\n bucketId: string,\n fileKey: string,\n file: Blob | ArrayBuffer | Uint8Array | ReadableStream<Uint8Array> | unknown,\n owner: string,\n location: string,\n _options?: UploadOptions,\n ): Promise<UploadReceipt> {\n void _options;\n\n await initWasm();\n\n const backendPath = `/buckets/${encodeURIComponent(bucketId)}/upload/${encodeURIComponent(fileKey)}`;\n const authHeaders = this.withAuth();\n\n // Convert the file to a blob and get its size\n const fileBlob = await this.coerceToFormPart(file);\n const fileSize = fileBlob.size;\n\n // Compute the fingerprint first\n // TODO: We should instead use FileManager here and use its `getFingerprint` method.\n // This would allow us to remove the `initWasm` call at the top and to stream the file\n // instead of loading it into memory as a blob.\n const fingerprint = await this.computeFileFingerprint(fileBlob);\n\n // Create the FileMetadata instance\n const metadata = await this.formFileMetadata(\n owner,\n bucketId,\n location,\n fingerprint,\n BigInt(fileSize),\n );\n\n // Compute the file key and ensure it matches the provided file key\n const computedFileKey = await this.computeFileKey(metadata);\n const expectedFileKeyBytes = this.hexToBytes(fileKey);\n if (\n computedFileKey.length !== expectedFileKeyBytes.length ||\n !computedFileKey.every((byte, index) => byte === expectedFileKeyBytes[index])\n ) {\n throw new Error(\n `Computed file key ${computedFileKey.toString()} does not match provided file key ${expectedFileKeyBytes.toString()}`,\n );\n }\n\n // Encode the file metadata\n const encodedMetadata = metadata.encode();\n\n // Create the multipart form with both the file and its metadata\n const form = new FormData();\n const fileMetadataBlob = new Blob([new Uint8Array(encodedMetadata)], {\n type: 'application/octet-stream',\n });\n form.append('file_metadata', fileMetadataBlob, 'file_metadata');\n form.append('file', fileBlob, 'file');\n\n const res = await this.http.put<UploadReceipt>(\n backendPath,\n authHeaders\n ? { body: form as unknown as BodyInit, headers: authHeaders }\n : { body: form as unknown as BodyInit },\n );\n return res;\n }\n\n private async coerceToFormPart(\n file: Blob | ArrayBuffer | Uint8Array | ReadableStream<Uint8Array> | unknown,\n ): Promise<Blob> {\n if (typeof Blob !== 'undefined' && file instanceof Blob) return file;\n if (file instanceof Uint8Array) return new Blob([file.buffer as ArrayBuffer]);\n if (typeof ArrayBuffer !== 'undefined' && file instanceof ArrayBuffer) return new Blob([file]);\n\n // Handle ReadableStream by reading it into memory\n if (file instanceof ReadableStream) {\n const reader = file.getReader();\n const chunks: Uint8Array[] = [];\n let totalLength = 0;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value) {\n chunks.push(value);\n totalLength += value.length;\n }\n }\n } finally {\n reader.releaseLock();\n }\n\n // Combine all chunks into a single Uint8Array\n const combined = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n\n return new Blob([combined], { type: 'application/octet-stream' });\n }\n\n return new Blob([file as BlobPart], { type: 'application/octet-stream' });\n }\n\n private async computeFileFingerprint(fileBlob: Blob): Promise<Uint8Array> {\n const trie = new FileTrie();\n const fileBytes = new Uint8Array(await fileBlob.arrayBuffer());\n\n // Process the file in 1KB chunks (matching CHUNK_SIZE from constants)\n const CHUNK_SIZE = 1024;\n let offset = 0;\n\n while (offset < fileBytes.length) {\n const end = Math.min(offset + CHUNK_SIZE, fileBytes.length);\n const chunk = fileBytes.slice(offset, end);\n trie.push_chunk(chunk);\n offset = end;\n }\n\n return trie.get_root();\n }\n\n async formFileMetadata(\n owner: string,\n bucketId: string,\n location: string,\n fingerprint: Uint8Array,\n size: bigint,\n ): Promise<FileMetadata> {\n const ownerBytes = this.hexToBytes(owner);\n const bucketIdBytes = this.hexToBytes(bucketId);\n const locationBytes = new TextEncoder().encode(location);\n await initWasm();\n return new FileMetadata(ownerBytes, bucketIdBytes, locationBytes, size, fingerprint);\n }\n\n hexToBytes(hex: string): Uint8Array {\n if (!hex) {\n throw new Error('hex string cannot be empty');\n }\n\n const cleanHex = hex.startsWith('0x') ? hex.slice(2) : hex;\n\n if (cleanHex.length % 2 !== 0) {\n throw new Error('hex string must have an even number of characters');\n }\n\n if (!/^[0-9a-fA-F]*$/.test(cleanHex)) {\n throw new Error('hex string contains invalid characters');\n }\n\n return new Uint8Array(cleanHex.match(/.{2}/g)?.map((byte) => Number.parseInt(byte, 16)) || []);\n }\n\n async computeFileKey(fileMetadata: FileMetadata): Promise<Uint8Array> {\n await initWasm();\n return fileMetadata.getFileKey();\n }\n\n /** Download a file by key. */\n async downloadByKey(fileKey: string, options?: DownloadOptions): Promise<DownloadResult> {\n const path = `/download/${encodeURIComponent(fileKey)}`;\n const baseHeaders: Record<string, string> = { Accept: '*/*' };\n if (options?.range) {\n const { start, end } = options.range;\n const rangeValue = `bytes=${start}-${end ?? ''}`;\n baseHeaders.Range = rangeValue;\n }\n const headers = this.withAuth(baseHeaders);\n const res = await this.http.getRaw(path, {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n });\n\n if (!res.body) {\n throw new Error('Response body is null - unable to create stream');\n }\n\n const contentType = res.headers.get('content-type');\n const contentRange = res.headers.get('content-range');\n const contentLengthHeader = res.headers.get('content-length');\n const parsedLength = contentLengthHeader !== null ? Number(contentLengthHeader) : undefined;\n const contentLength =\n typeof parsedLength === 'number' && Number.isFinite(parsedLength) ? parsedLength : null;\n\n return {\n stream: res.body,\n status: res.status,\n contentType,\n contentRange,\n contentLength,\n };\n }\n\n /** Download a file by its location path under a bucket. */\n async downloadByLocation(\n bucketId: string,\n filePath: string,\n options?: DownloadOptions,\n ): Promise<DownloadResult> {\n const normalized = filePath.replace(/^\\/+/, '');\n const encodedPath = normalized.split('/').map(encodeURIComponent).join('/');\n const path = `/buckets/${encodeURIComponent(bucketId)}/download/path/${encodedPath}`;\n const baseHeaders: Record<string, string> = { Accept: '*/*' };\n if (options?.range) {\n const { start, end } = options.range;\n const rangeValue = `bytes=${start}-${end ?? ''}`;\n baseHeaders.Range = rangeValue;\n }\n const headers = this.withAuth(baseHeaders);\n const res = await this.http.getRaw(path, {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n });\n\n if (!res.body) {\n throw new Error('Response body is null - unable to create stream');\n }\n\n const contentType = res.headers.get('content-type');\n const contentRange = res.headers.get('content-range');\n const contentLengthHeader = res.headers.get('content-length');\n const parsedLength = contentLengthHeader !== null ? Number(contentLengthHeader) : undefined;\n const contentLength =\n typeof parsedLength === 'number' && Number.isFinite(parsedLength) ? parsedLength : null;\n\n return {\n stream: res.body,\n status: res.status,\n contentType,\n contentRange,\n contentLength,\n };\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["FileMetadata", "FileTrie", "HttpClient", "initWasm", "MspClient", "_MspClient", "config", "http", "options", "address", "chainId", "message", "signature", "token", "headers", "bucketId", "path", "fileKey", "wire", "file", "owner", "location", "_options", "backendPath", "authHeaders", "fileBlob", "fileSize", "fingerprint", "metadata", "computedFileKey", "expectedFileKeyBytes", "byte", "index", "encodedMetadata", "form", "fileMetadataBlob", "reader", "chunks", "totalLength", "done", "value", "combined", "offset", "chunk", "trie", "fileBytes", "CHUNK_SIZE", "end", "size", "ownerBytes", "bucketIdBytes", "locationBytes", "hex", "cleanHex", "fileMetadata", "baseHeaders", "start", "rangeValue", "res", "contentType", "contentRange", "contentLengthHeader", "parsedLength", "contentLength", "filePath", "encodedPath"]
7
7
  }
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export { MspClient } from './MspClient.js';
2
- export type { Bucket, FileEntry, FileListResponse, HealthStatus, NonceResponse, UploadOptions, UploadReceipt, VerifyResponse, } from './types.js';
2
+ export type { Bucket, Capacity, DownloadOptions, DownloadResult, FileEntry, FileInfo, FileListResponse, HealthStatus, InfoResponse, NonceResponse, StatsResponse, UploadOptions, UploadReceipt, ValueProp, VerifyResponse, } from './types.js';
@@ -1,2 +1,2 @@
1
- import{HttpClient as R}from"@storagehub-sdk/core";var y=class m{config;http;token;constructor(e,n){this.config=e,this.http=n}static async connect(e){if(!e?.baseUrl)throw new Error("MspClient.connect: baseUrl is required");let n=new R({baseUrl:e.baseUrl,...e.timeoutMs!==void 0&&{timeoutMs:e.timeoutMs},...e.defaultHeaders!==void 0&&{defaultHeaders:e.defaultHeaders},...e.fetchImpl!==void 0&&{fetchImpl:e.fetchImpl}});return new m(e,n)}getHealth(e){return this.http.get("/health",{...e?.signal!==void 0&&{signal:e.signal}})}getNonce(e,n,t){return this.http.post("/auth/nonce",{body:{address:e,chainId:n},headers:{"Content-Type":"application/json"},...t?.signal!==void 0&&{signal:t.signal}})}verify(e,n,t){return this.http.post("/auth/verify",{body:{message:e,signature:n},headers:{"Content-Type":"application/json"},...t?.signal!==void 0&&{signal:t.signal}})}setToken(e){this.token=e}withAuth(e){return this.token?{...e??{},Authorization:`Bearer ${this.token}`}:e}listBuckets(e){let n=this.withAuth();return this.http.get("/buckets",{...n?{headers:n}:{},...e?.signal?{signal:e.signal}:{}})}getBucket(e,n){let t=this.withAuth(),a=`/buckets/${encodeURIComponent(e)}`;return this.http.get(a,{...t?{headers:t}:{},...n?.signal?{signal:n.signal}:{}})}getFiles(e,n){let t=this.withAuth(),a=`/buckets/${encodeURIComponent(e)}/files`;return this.http.get(a,{...t?{headers:t}:{},...n?.signal?{signal:n.signal}:{},...n?.path?{query:{path:n.path.replace(/^\/+/,"")}}:{}})}async uploadFile(e,n,t,a){let i=`/buckets/${encodeURIComponent(e)}/upload/${encodeURIComponent(n)}`,o=this.withAuth();if(t instanceof ReadableStream)return await this.http.put(i,o?{body:t,headers:{...o,"Content-Type":"application/octet-stream"}}:{body:t,headers:{"Content-Type":"application/octet-stream"}});let s=new FormData,l=await this.coerceToFormPart(t);return s.append("file",l,"file"),await this.http.put(i,o?{body:s,headers:o}:{body:s})}async coerceToFormPart(e){return typeof Blob<"u"&&e instanceof Blob?e:e instanceof Uint8Array?new Blob([e]):typeof ArrayBuffer<"u"&&e instanceof ArrayBuffer?new Blob([e]):new Blob([e])}async downloadByKey(e,n,t){let a=`/buckets/${encodeURIComponent(e)}/download/${encodeURIComponent(n)}`,i={Accept:"*/*"};if(t?.range){let{start:d,end:h}=t.range,g=`bytes=${d}-${h??""}`;i.Range=g}let o=this.withAuth(i),s=await this.http.getRaw(a,{...o?{headers:o}:{},...t?.signal?{signal:t.signal}:{}});if(!s.body)throw new Error("Response body is null - unable to create stream");let l=s.headers.get("content-type"),r=s.headers.get("content-range"),c=s.headers.get("content-length"),p=c!==null?Number(c):void 0,u=typeof p=="number"&&Number.isFinite(p)?p:null;return{stream:s.body,status:s.status,contentType:l,contentRange:r,contentLength:u}}async downloadByLocation(e,n,t){let i=n.replace(/^\/+/,"").split("/").map(encodeURIComponent).join("/"),o=`/buckets/${encodeURIComponent(e)}/download/path/${i}`,s={Accept:"*/*"};if(t?.range){let{start:g,end:b}=t.range,f=`bytes=${g}-${b??""}`;s.Range=f}let l=this.withAuth(s),r=await this.http.getRaw(o,{...l?{headers:l}:{},...t?.signal?{signal:t.signal}:{}});if(!r.body)throw new Error("Response body is null - unable to create stream");let c=r.headers.get("content-type"),p=r.headers.get("content-range"),u=r.headers.get("content-length"),d=u!==null?Number(u):void 0,h=typeof d=="number"&&Number.isFinite(d)?d:null;return{stream:r.body,status:r.status,contentType:c,contentRange:p,contentLength:h}}};export{y as MspClient};
1
+ import{FileMetadata as B,FileTrie as F,HttpClient as U,initWasm as m}from"@storagehub-sdk/core";var b=class w{config;http;token;constructor(e,t){this.config=e,this.http=t}static async connect(e){if(!e?.baseUrl)throw new Error("MspClient.connect: baseUrl is required");let t=new U({baseUrl:e.baseUrl,...e.timeoutMs!==void 0&&{timeoutMs:e.timeoutMs},...e.defaultHeaders!==void 0&&{defaultHeaders:e.defaultHeaders},...e.fetchImpl!==void 0&&{fetchImpl:e.fetchImpl}});return new w(e,t)}getHealth(e){return this.http.get("/health",{...e?.signal!==void 0&&{signal:e.signal}})}getInfo(e){return this.http.get("/info",{...e?.signal!==void 0&&{signal:e.signal}})}getStats(e){return this.http.get("/stats",{...e?.signal!==void 0&&{signal:e.signal}})}getValuePropositions(e){return this.http.get("/value-props",{...e?.signal!==void 0&&{signal:e.signal}})}getNonce(e,t,n){return this.http.post("/auth/nonce",{body:{address:e,chainId:t},headers:{"Content-Type":"application/json"},...n?.signal!==void 0&&{signal:n.signal}})}verify(e,t,n){return this.http.post("/auth/verify",{body:{message:e,signature:t},headers:{"Content-Type":"application/json"},...n?.signal!==void 0&&{signal:n.signal}})}setToken(e){this.token=e}withAuth(e){return this.token?{...e??{},Authorization:`Bearer ${this.token}`}:e}listBuckets(e){let t=this.withAuth();return this.http.get("/buckets",{...t?{headers:t}:{},...e?.signal?{signal:e.signal}:{}})}getBucket(e,t){let n=this.withAuth(),a=`/buckets/${encodeURIComponent(e)}`;return this.http.get(a,{...n?{headers:n}:{},...t?.signal?{signal:t.signal}:{}})}getFiles(e,t){let n=this.withAuth(),a=`/buckets/${encodeURIComponent(e)}/files`;return this.http.get(a,{...n?{headers:n}:{},...t?.signal?{signal:t.signal}:{},...t?.path?{query:{path:t.path.replace(/^\/+/,"")}}:{}})}getFileInfo(e,t,n){let a=this.withAuth(),o=`/buckets/${encodeURIComponent(e)}/info/${encodeURIComponent(t)}`;return this.http.get(o,{...a?{headers:a}:{},...n?.signal?{signal:n.signal}:{}}).then(s=>({...s,uploadedAt:new Date(s.uploadedAt)}))}async uploadFile(e,t,n,a,o,s){await m();let r=`/buckets/${encodeURIComponent(e)}/upload/${encodeURIComponent(t)}`,i=this.withAuth(),l=await this.coerceToFormPart(n),d=l.size,u=await this.computeFileFingerprint(l),p=await this.formFileMetadata(a,e,o,u,BigInt(d)),c=await this.computeFileKey(p),g=this.hexToBytes(t);if(c.length!==g.length||!c.every((R,A)=>R===g[A]))throw new Error(`Computed file key ${c.toString()} does not match provided file key ${g.toString()}`);let f=p.encode(),h=new FormData,y=new Blob([new Uint8Array(f)],{type:"application/octet-stream"});return h.append("file_metadata",y,"file_metadata"),h.append("file",l,"file"),await this.http.put(r,i?{body:h,headers:i}:{body:h})}async coerceToFormPart(e){if(typeof Blob<"u"&&e instanceof Blob)return e;if(e instanceof Uint8Array)return new Blob([e.buffer]);if(typeof ArrayBuffer<"u"&&e instanceof ArrayBuffer)return new Blob([e]);if(e instanceof ReadableStream){let t=e.getReader(),n=[],a=0;try{for(;;){let{done:r,value:i}=await t.read();if(r)break;i&&(n.push(i),a+=i.length)}}finally{t.releaseLock()}let o=new Uint8Array(a),s=0;for(let r of n)o.set(r,s),s+=r.length;return new Blob([o],{type:"application/octet-stream"})}return new Blob([e],{type:"application/octet-stream"})}async computeFileFingerprint(e){let t=new F,n=new Uint8Array(await e.arrayBuffer()),a=1024,o=0;for(;o<n.length;){let s=Math.min(o+a,n.length),r=n.slice(o,s);t.push_chunk(r),o=s}return t.get_root()}async formFileMetadata(e,t,n,a,o){let s=this.hexToBytes(e),r=this.hexToBytes(t),i=new TextEncoder().encode(n);return await m(),new B(s,r,i,o,a)}hexToBytes(e){if(!e)throw new Error("hex string cannot be empty");let t=e.startsWith("0x")?e.slice(2):e;if(t.length%2!==0)throw new Error("hex string must have an even number of characters");if(!/^[0-9a-fA-F]*$/.test(t))throw new Error("hex string contains invalid characters");return new Uint8Array(t.match(/.{2}/g)?.map(n=>Number.parseInt(n,16))||[])}async computeFileKey(e){return await m(),e.getFileKey()}async downloadByKey(e,t){let n=`/download/${encodeURIComponent(e)}`,a={Accept:"*/*"};if(t?.range){let{start:p,end:c}=t.range,g=`bytes=${p}-${c??""}`;a.Range=g}let o=this.withAuth(a),s=await this.http.getRaw(n,{...o?{headers:o}:{},...t?.signal?{signal:t.signal}:{}});if(!s.body)throw new Error("Response body is null - unable to create stream");let r=s.headers.get("content-type"),i=s.headers.get("content-range"),l=s.headers.get("content-length"),d=l!==null?Number(l):void 0,u=typeof d=="number"&&Number.isFinite(d)?d:null;return{stream:s.body,status:s.status,contentType:r,contentRange:i,contentLength:u}}async downloadByLocation(e,t,n){let o=t.replace(/^\/+/,"").split("/").map(encodeURIComponent).join("/"),s=`/buckets/${encodeURIComponent(e)}/download/path/${o}`,r={Accept:"*/*"};if(n?.range){let{start:f,end:h}=n.range,y=`bytes=${f}-${h??""}`;r.Range=y}let i=this.withAuth(r),l=await this.http.getRaw(s,{...i?{headers:i}:{},...n?.signal?{signal:n.signal}:{}});if(!l.body)throw new Error("Response body is null - unable to create stream");let d=l.headers.get("content-type"),u=l.headers.get("content-range"),p=l.headers.get("content-length"),c=p!==null?Number(p):void 0,g=typeof c=="number"&&Number.isFinite(c)?c:null;return{stream:l.body,status:l.status,contentType:d,contentRange:u,contentLength:g}}};export{b as MspClient};
2
2
  //# sourceMappingURL=index.node.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/MspClient.ts"],
4
- "sourcesContent": ["import type {\n Bucket,\n DownloadOptions,\n DownloadResult,\n FileListResponse,\n GetFilesOptions,\n HealthStatus,\n NonceResponse,\n UploadOptions,\n UploadReceipt,\n VerifyResponse,\n} from './types.js';\nimport type { HttpClientConfig } from '@storagehub-sdk/core';\nimport { HttpClient } from '@storagehub-sdk/core';\n\nexport class MspClient {\n public readonly config: HttpClientConfig;\n private readonly http: HttpClient;\n private token?: string;\n\n private constructor(config: HttpClientConfig, http: HttpClient) {\n this.config = config;\n this.http = http;\n }\n\n static async connect(config: HttpClientConfig): Promise<MspClient> {\n if (!config?.baseUrl) throw new Error('MspClient.connect: baseUrl is required');\n\n const http = new HttpClient({\n baseUrl: config.baseUrl,\n ...(config.timeoutMs !== undefined && { timeoutMs: config.timeoutMs }),\n ...(config.defaultHeaders !== undefined && { defaultHeaders: config.defaultHeaders }),\n ...(config.fetchImpl !== undefined && { fetchImpl: config.fetchImpl }),\n });\n\n return new MspClient(config, http);\n }\n\n getHealth(options?: { signal?: AbortSignal }): Promise<HealthStatus> {\n return this.http.get<HealthStatus>('/health', {\n ...(options?.signal !== undefined && { signal: options.signal }),\n });\n }\n\n // Auth endpoints:\n\n /** Request a SIWE-style nonce message for the given address and chainId */\n getNonce(\n address: string,\n chainId: number,\n options?: { signal?: AbortSignal },\n ): Promise<NonceResponse> {\n return this.http.post<NonceResponse>('/auth/nonce', {\n body: { address, chainId },\n headers: { 'Content-Type': 'application/json' },\n ...(options?.signal !== undefined && { signal: options.signal }),\n });\n }\n\n /** Verify signed message and receive JWT token */\n verify(\n message: string,\n signature: string,\n options?: { signal?: AbortSignal },\n ): Promise<VerifyResponse> {\n return this.http.post<VerifyResponse>('/auth/verify', {\n body: { message, signature },\n headers: { 'Content-Type': 'application/json' },\n ...(options?.signal !== undefined && { signal: options.signal }),\n });\n }\n\n /** Store token to be sent on subsequent protected requests */\n setToken(token: string): void {\n this.token = token;\n }\n\n /** Merge Authorization header when token is present */\n private withAuth(headers?: Record<string, string>): Record<string, string> | undefined {\n if (!this.token) return headers;\n return { ...(headers ?? {}), Authorization: `Bearer ${this.token}` };\n }\n\n // Bucket endpoints:\n\n /** List all buckets for the current authenticateduser */\n listBuckets(options?: { signal?: AbortSignal }): Promise<Bucket[]> {\n const headers = this.withAuth();\n return this.http.get<Bucket[]>('/buckets', {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n });\n }\n\n /** Get a specific bucket's metadata by its bucket ID */\n getBucket(bucketId: string, options?: { signal?: AbortSignal }): Promise<Bucket> {\n const headers = this.withAuth();\n const path = `/buckets/${encodeURIComponent(bucketId)}`;\n return this.http.get<Bucket>(path, {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n });\n }\n\n /** Gets the list of files and folders under the specified path for a bucket. If no path is provided, it returns the files and folders found at root. */\n getFiles(bucketId: string, options?: GetFilesOptions): Promise<FileListResponse> {\n const headers = this.withAuth();\n const path = `/buckets/${encodeURIComponent(bucketId)}/files`;\n return this.http.get<FileListResponse>(path, {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n ...(options?.path ? { query: { path: options.path.replace(/^\\/+/, '') } } : {}),\n });\n }\n\n // File endpoints:\n\n /**\n * Upload a file to a bucket with a specific key.\n *\n * For small files (Blob, ArrayBuffer, Uint8Array), uses multipart/form-data upload.\n * For large files (ReadableStream), uses memory-efficient streaming upload with\n * application/octet-stream to prevent loading entire file into memory.\n *\n */\n async uploadFile(\n bucketId: string,\n fileKey: string,\n file: Blob | ArrayBuffer | Uint8Array | ReadableStream<Uint8Array> | unknown,\n _options?: UploadOptions,\n ): Promise<UploadReceipt> {\n void _options;\n\n const path = `/buckets/${encodeURIComponent(bucketId)}/upload/${encodeURIComponent(fileKey)}`;\n const authHeaders = this.withAuth();\n\n // For ReadableStream, use direct streaming upload (memory efficient)\n if (file instanceof ReadableStream) {\n const res = await this.http.put<UploadReceipt>(\n path,\n authHeaders\n ? {\n body: file,\n headers: {\n ...authHeaders,\n 'Content-Type': 'application/octet-stream',\n },\n }\n : {\n body: file,\n headers: { 'Content-Type': 'application/octet-stream' },\n },\n );\n return res;\n }\n\n // For other types, use FormData (traditional multipart upload)\n const form = new FormData();\n const part = await this.coerceToFormPart(file);\n form.append('file', part as Blob, 'file'); // part is now guaranteed to be Blob\n\n const res = await this.http.put<UploadReceipt>(\n path,\n authHeaders\n ? { body: form as unknown as BodyInit, headers: authHeaders }\n : { body: form as unknown as BodyInit },\n );\n return res;\n }\n\n private async coerceToFormPart(file: Blob | ArrayBuffer | Uint8Array | unknown): Promise<Blob> {\n if (typeof Blob !== 'undefined' && file instanceof Blob) return file;\n if (file instanceof Uint8Array) return new Blob([file]);\n if (typeof ArrayBuffer !== 'undefined' && file instanceof ArrayBuffer) return new Blob([file]);\n\n return new Blob([file as BlobPart]);\n }\n\n /** Download a file by bucket and key. */\n async downloadByKey(\n bucketId: string,\n fileKey: string,\n options?: DownloadOptions,\n ): Promise<DownloadResult> {\n const path = `/buckets/${encodeURIComponent(bucketId)}/download/${encodeURIComponent(fileKey)}`;\n const baseHeaders: Record<string, string> = { Accept: '*/*' };\n if (options?.range) {\n const { start, end } = options.range;\n const rangeValue = `bytes=${start}-${end ?? ''}`;\n baseHeaders.Range = rangeValue;\n }\n const headers = this.withAuth(baseHeaders);\n const res = await this.http.getRaw(path, {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n });\n\n if (!res.body) {\n throw new Error('Response body is null - unable to create stream');\n }\n\n const contentType = res.headers.get('content-type');\n const contentRange = res.headers.get('content-range');\n const contentLengthHeader = res.headers.get('content-length');\n const parsedLength = contentLengthHeader !== null ? Number(contentLengthHeader) : undefined;\n const contentLength =\n typeof parsedLength === 'number' && Number.isFinite(parsedLength) ? parsedLength : null;\n\n return {\n stream: res.body,\n status: res.status,\n contentType,\n contentRange,\n contentLength,\n };\n }\n\n /** Download a file by its location path under a bucket. */\n async downloadByLocation(\n bucketId: string,\n filePath: string,\n options?: DownloadOptions,\n ): Promise<DownloadResult> {\n const normalized = filePath.replace(/^\\/+/, '');\n const encodedPath = normalized.split('/').map(encodeURIComponent).join('/');\n const path = `/buckets/${encodeURIComponent(bucketId)}/download/path/${encodedPath}`;\n const baseHeaders: Record<string, string> = { Accept: '*/*' };\n if (options?.range) {\n const { start, end } = options.range;\n const rangeValue = `bytes=${start}-${end ?? ''}`;\n baseHeaders.Range = rangeValue;\n }\n const headers = this.withAuth(baseHeaders);\n const res = await this.http.getRaw(path, {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n });\n\n if (!res.body) {\n throw new Error('Response body is null - unable to create stream');\n }\n\n const contentType = res.headers.get('content-type');\n const contentRange = res.headers.get('content-range');\n const contentLengthHeader = res.headers.get('content-length');\n const parsedLength = contentLengthHeader !== null ? Number(contentLengthHeader) : undefined;\n const contentLength =\n typeof parsedLength === 'number' && Number.isFinite(parsedLength) ? parsedLength : null;\n\n return {\n stream: res.body,\n status: res.status,\n contentType,\n contentRange,\n contentLength,\n };\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["HttpClient", "MspClient", "_MspClient", "config", "http", "options", "address", "chainId", "message", "signature", "token", "headers", "bucketId", "path", "fileKey", "file", "_options", "authHeaders", "form", "part", "baseHeaders", "start", "end", "rangeValue", "res", "contentType", "contentRange", "contentLengthHeader", "parsedLength", "contentLength", "filePath", "encodedPath"]
4
+ "sourcesContent": ["import type {\n Bucket,\n DownloadOptions,\n DownloadResult,\n FileInfo,\n FileListResponse,\n GetFilesOptions,\n HealthStatus,\n InfoResponse,\n NonceResponse,\n StatsResponse,\n UploadOptions,\n UploadReceipt,\n ValueProp,\n VerifyResponse,\n} from './types.js';\nimport type { HttpClientConfig } from '@storagehub-sdk/core';\nimport { FileMetadata, FileTrie, HttpClient, initWasm } from '@storagehub-sdk/core';\n\nexport class MspClient {\n public readonly config: HttpClientConfig;\n private readonly http: HttpClient;\n public token?: string;\n\n private constructor(config: HttpClientConfig, http: HttpClient) {\n this.config = config;\n this.http = http;\n }\n\n static async connect(config: HttpClientConfig): Promise<MspClient> {\n if (!config?.baseUrl) throw new Error('MspClient.connect: baseUrl is required');\n\n const http = new HttpClient({\n baseUrl: config.baseUrl,\n ...(config.timeoutMs !== undefined && { timeoutMs: config.timeoutMs }),\n ...(config.defaultHeaders !== undefined && { defaultHeaders: config.defaultHeaders }),\n ...(config.fetchImpl !== undefined && { fetchImpl: config.fetchImpl }),\n });\n\n return new MspClient(config, http);\n }\n\n getHealth(options?: { signal?: AbortSignal }): Promise<HealthStatus> {\n return this.http.get<HealthStatus>('/health', {\n ...(options?.signal !== undefined && { signal: options.signal }),\n });\n }\n\n /** Get general MSP information */\n getInfo(options?: { signal?: AbortSignal }): Promise<InfoResponse> {\n return this.http.get<InfoResponse>('/info', {\n ...(options?.signal !== undefined && { signal: options.signal }),\n });\n }\n\n /** Get MSP statistics */\n getStats(options?: { signal?: AbortSignal }): Promise<StatsResponse> {\n return this.http.get<StatsResponse>('/stats', {\n ...(options?.signal !== undefined && { signal: options.signal }),\n });\n }\n\n /** Get available value propositions */\n getValuePropositions(options?: { signal?: AbortSignal }): Promise<ValueProp[]> {\n return this.http.get<ValueProp[]>('/value-props', {\n ...(options?.signal !== undefined && { signal: options.signal }),\n });\n }\n\n // Auth endpoints:\n\n /** Request a SIWE-style nonce message for the given address and chainId */\n getNonce(\n address: string,\n chainId: number,\n options?: { signal?: AbortSignal },\n ): Promise<NonceResponse> {\n return this.http.post<NonceResponse>('/auth/nonce', {\n body: { address, chainId },\n headers: { 'Content-Type': 'application/json' },\n ...(options?.signal !== undefined && { signal: options.signal }),\n });\n }\n\n /** Verify signed message and receive JWT token */\n verify(\n message: string,\n signature: string,\n options?: { signal?: AbortSignal },\n ): Promise<VerifyResponse> {\n return this.http.post<VerifyResponse>('/auth/verify', {\n body: { message, signature },\n headers: { 'Content-Type': 'application/json' },\n ...(options?.signal !== undefined && { signal: options.signal }),\n });\n }\n\n /** Store token to be sent on subsequent protected requests */\n setToken(token: string): void {\n this.token = token;\n }\n\n /** Merge Authorization header when token is present */\n private withAuth(headers?: Record<string, string>): Record<string, string> | undefined {\n if (!this.token) return headers;\n return { ...(headers ?? {}), Authorization: `Bearer ${this.token}` };\n }\n\n // Bucket endpoints:\n\n /** List all buckets for the current authenticateduser */\n listBuckets(options?: { signal?: AbortSignal }): Promise<Bucket[]> {\n const headers = this.withAuth();\n return this.http.get<Bucket[]>('/buckets', {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n });\n }\n\n /** Get a specific bucket's metadata by its bucket ID */\n getBucket(bucketId: string, options?: { signal?: AbortSignal }): Promise<Bucket> {\n const headers = this.withAuth();\n const path = `/buckets/${encodeURIComponent(bucketId)}`;\n return this.http.get<Bucket>(path, {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n });\n }\n\n /** Gets the list of files and folders under the specified path for a bucket. If no path is provided, it returns the files and folders found at root. */\n getFiles(bucketId: string, options?: GetFilesOptions): Promise<FileListResponse> {\n const headers = this.withAuth();\n const path = `/buckets/${encodeURIComponent(bucketId)}/files`;\n return this.http.get<FileListResponse>(path, {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n ...(options?.path ? { query: { path: options.path.replace(/^\\/+/, '') } } : {}),\n });\n }\n\n /** Get metadata for a file in a bucket by fileKey */\n getFileInfo(\n bucketId: string,\n fileKey: string,\n options?: { signal?: AbortSignal },\n ): Promise<FileInfo> {\n const headers = this.withAuth();\n const path = `/buckets/${encodeURIComponent(bucketId)}/info/${encodeURIComponent(fileKey)}`;\n type FileInfoWire = Omit<FileInfo, 'uploadedAt'> & { uploadedAt: string };\n return this.http\n .get<FileInfoWire>(path, {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n })\n .then((wire): FileInfo => ({ ...wire, uploadedAt: new Date(wire.uploadedAt) }));\n }\n\n // File endpoints:\n\n /**\n * Upload a file to a bucket with a specific key.\n *\n * Always uses multipart/form-data upload with both file data and encoded FileMetadata.\n * The file data is loaded into memory to create the multipart request.\n *\n */\n async uploadFile(\n bucketId: string,\n fileKey: string,\n file: Blob | ArrayBuffer | Uint8Array | ReadableStream<Uint8Array> | unknown,\n owner: string,\n location: string,\n _options?: UploadOptions,\n ): Promise<UploadReceipt> {\n void _options;\n\n await initWasm();\n\n const backendPath = `/buckets/${encodeURIComponent(bucketId)}/upload/${encodeURIComponent(fileKey)}`;\n const authHeaders = this.withAuth();\n\n // Convert the file to a blob and get its size\n const fileBlob = await this.coerceToFormPart(file);\n const fileSize = fileBlob.size;\n\n // Compute the fingerprint first\n // TODO: We should instead use FileManager here and use its `getFingerprint` method.\n // This would allow us to remove the `initWasm` call at the top and to stream the file\n // instead of loading it into memory as a blob.\n const fingerprint = await this.computeFileFingerprint(fileBlob);\n\n // Create the FileMetadata instance\n const metadata = await this.formFileMetadata(\n owner,\n bucketId,\n location,\n fingerprint,\n BigInt(fileSize),\n );\n\n // Compute the file key and ensure it matches the provided file key\n const computedFileKey = await this.computeFileKey(metadata);\n const expectedFileKeyBytes = this.hexToBytes(fileKey);\n if (\n computedFileKey.length !== expectedFileKeyBytes.length ||\n !computedFileKey.every((byte, index) => byte === expectedFileKeyBytes[index])\n ) {\n throw new Error(\n `Computed file key ${computedFileKey.toString()} does not match provided file key ${expectedFileKeyBytes.toString()}`,\n );\n }\n\n // Encode the file metadata\n const encodedMetadata = metadata.encode();\n\n // Create the multipart form with both the file and its metadata\n const form = new FormData();\n const fileMetadataBlob = new Blob([new Uint8Array(encodedMetadata)], {\n type: 'application/octet-stream',\n });\n form.append('file_metadata', fileMetadataBlob, 'file_metadata');\n form.append('file', fileBlob, 'file');\n\n const res = await this.http.put<UploadReceipt>(\n backendPath,\n authHeaders\n ? { body: form as unknown as BodyInit, headers: authHeaders }\n : { body: form as unknown as BodyInit },\n );\n return res;\n }\n\n private async coerceToFormPart(\n file: Blob | ArrayBuffer | Uint8Array | ReadableStream<Uint8Array> | unknown,\n ): Promise<Blob> {\n if (typeof Blob !== 'undefined' && file instanceof Blob) return file;\n if (file instanceof Uint8Array) return new Blob([file.buffer as ArrayBuffer]);\n if (typeof ArrayBuffer !== 'undefined' && file instanceof ArrayBuffer) return new Blob([file]);\n\n // Handle ReadableStream by reading it into memory\n if (file instanceof ReadableStream) {\n const reader = file.getReader();\n const chunks: Uint8Array[] = [];\n let totalLength = 0;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value) {\n chunks.push(value);\n totalLength += value.length;\n }\n }\n } finally {\n reader.releaseLock();\n }\n\n // Combine all chunks into a single Uint8Array\n const combined = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n\n return new Blob([combined], { type: 'application/octet-stream' });\n }\n\n return new Blob([file as BlobPart], { type: 'application/octet-stream' });\n }\n\n private async computeFileFingerprint(fileBlob: Blob): Promise<Uint8Array> {\n const trie = new FileTrie();\n const fileBytes = new Uint8Array(await fileBlob.arrayBuffer());\n\n // Process the file in 1KB chunks (matching CHUNK_SIZE from constants)\n const CHUNK_SIZE = 1024;\n let offset = 0;\n\n while (offset < fileBytes.length) {\n const end = Math.min(offset + CHUNK_SIZE, fileBytes.length);\n const chunk = fileBytes.slice(offset, end);\n trie.push_chunk(chunk);\n offset = end;\n }\n\n return trie.get_root();\n }\n\n async formFileMetadata(\n owner: string,\n bucketId: string,\n location: string,\n fingerprint: Uint8Array,\n size: bigint,\n ): Promise<FileMetadata> {\n const ownerBytes = this.hexToBytes(owner);\n const bucketIdBytes = this.hexToBytes(bucketId);\n const locationBytes = new TextEncoder().encode(location);\n await initWasm();\n return new FileMetadata(ownerBytes, bucketIdBytes, locationBytes, size, fingerprint);\n }\n\n hexToBytes(hex: string): Uint8Array {\n if (!hex) {\n throw new Error('hex string cannot be empty');\n }\n\n const cleanHex = hex.startsWith('0x') ? hex.slice(2) : hex;\n\n if (cleanHex.length % 2 !== 0) {\n throw new Error('hex string must have an even number of characters');\n }\n\n if (!/^[0-9a-fA-F]*$/.test(cleanHex)) {\n throw new Error('hex string contains invalid characters');\n }\n\n return new Uint8Array(cleanHex.match(/.{2}/g)?.map((byte) => Number.parseInt(byte, 16)) || []);\n }\n\n async computeFileKey(fileMetadata: FileMetadata): Promise<Uint8Array> {\n await initWasm();\n return fileMetadata.getFileKey();\n }\n\n /** Download a file by key. */\n async downloadByKey(fileKey: string, options?: DownloadOptions): Promise<DownloadResult> {\n const path = `/download/${encodeURIComponent(fileKey)}`;\n const baseHeaders: Record<string, string> = { Accept: '*/*' };\n if (options?.range) {\n const { start, end } = options.range;\n const rangeValue = `bytes=${start}-${end ?? ''}`;\n baseHeaders.Range = rangeValue;\n }\n const headers = this.withAuth(baseHeaders);\n const res = await this.http.getRaw(path, {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n });\n\n if (!res.body) {\n throw new Error('Response body is null - unable to create stream');\n }\n\n const contentType = res.headers.get('content-type');\n const contentRange = res.headers.get('content-range');\n const contentLengthHeader = res.headers.get('content-length');\n const parsedLength = contentLengthHeader !== null ? Number(contentLengthHeader) : undefined;\n const contentLength =\n typeof parsedLength === 'number' && Number.isFinite(parsedLength) ? parsedLength : null;\n\n return {\n stream: res.body,\n status: res.status,\n contentType,\n contentRange,\n contentLength,\n };\n }\n\n /** Download a file by its location path under a bucket. */\n async downloadByLocation(\n bucketId: string,\n filePath: string,\n options?: DownloadOptions,\n ): Promise<DownloadResult> {\n const normalized = filePath.replace(/^\\/+/, '');\n const encodedPath = normalized.split('/').map(encodeURIComponent).join('/');\n const path = `/buckets/${encodeURIComponent(bucketId)}/download/path/${encodedPath}`;\n const baseHeaders: Record<string, string> = { Accept: '*/*' };\n if (options?.range) {\n const { start, end } = options.range;\n const rangeValue = `bytes=${start}-${end ?? ''}`;\n baseHeaders.Range = rangeValue;\n }\n const headers = this.withAuth(baseHeaders);\n const res = await this.http.getRaw(path, {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n });\n\n if (!res.body) {\n throw new Error('Response body is null - unable to create stream');\n }\n\n const contentType = res.headers.get('content-type');\n const contentRange = res.headers.get('content-range');\n const contentLengthHeader = res.headers.get('content-length');\n const parsedLength = contentLengthHeader !== null ? Number(contentLengthHeader) : undefined;\n const contentLength =\n typeof parsedLength === 'number' && Number.isFinite(parsedLength) ? parsedLength : null;\n\n return {\n stream: res.body,\n status: res.status,\n contentType,\n contentRange,\n contentLength,\n };\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["FileMetadata", "FileTrie", "HttpClient", "initWasm", "MspClient", "_MspClient", "config", "http", "options", "address", "chainId", "message", "signature", "token", "headers", "bucketId", "path", "fileKey", "wire", "file", "owner", "location", "_options", "backendPath", "authHeaders", "fileBlob", "fileSize", "fingerprint", "metadata", "computedFileKey", "expectedFileKeyBytes", "byte", "index", "encodedMetadata", "form", "fileMetadataBlob", "reader", "chunks", "totalLength", "done", "value", "combined", "offset", "chunk", "trie", "fileBytes", "CHUNK_SIZE", "end", "size", "ownerBytes", "bucketIdBytes", "locationBytes", "hex", "cleanHex", "fileMetadata", "baseHeaders", "start", "rangeValue", "res", "contentType", "contentRange", "contentLengthHeader", "parsedLength", "contentLength", "filePath", "encodedPath"]
7
7
  }
package/dist/types.d.ts CHANGED
@@ -105,3 +105,42 @@ export interface GetFilesOptions {
105
105
  path?: string;
106
106
  signal?: AbortSignal;
107
107
  }
108
+ export interface InfoResponse {
109
+ client: string;
110
+ version: string;
111
+ mspId: string;
112
+ multiaddresses: string[];
113
+ ownerAccount: string;
114
+ paymentAccount: string;
115
+ status: string;
116
+ activeSince: number;
117
+ uptime: string;
118
+ }
119
+ export interface Capacity {
120
+ totalBytes: number;
121
+ availableBytes: number;
122
+ usedBytes: number;
123
+ }
124
+ export interface StatsResponse {
125
+ capacity: Capacity;
126
+ activeUsers: number;
127
+ lastCapacityChange: number;
128
+ valuePropsAmount: number;
129
+ bucketsAmount: number;
130
+ }
131
+ export interface ValueProp {
132
+ id: string;
133
+ pricePerGbBlock: number;
134
+ dataLimitPerBucketBytes: number;
135
+ isAvailable: boolean;
136
+ }
137
+ export interface FileInfo {
138
+ fileKey: string;
139
+ fingerprint: string;
140
+ bucketId: string;
141
+ name: string;
142
+ location: string;
143
+ size: number;
144
+ isPublic: boolean;
145
+ uploadedAt: Date;
146
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storagehub-sdk/msp-client",
3
- "version": "0.0.4",
3
+ "version": "0.1.0",
4
4
  "description": "High-level TypeScript client for StorageHub MSP: simple file upload/download, SIWE-style auth, health checks, and bucket browsing built on top of @storagehub-sdk/core.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.node.js",
@@ -22,6 +22,22 @@
22
22
  "dist",
23
23
  "README.md"
24
24
  ],
25
+ "peerDependencies": {
26
+ "@storagehub-sdk/core": ">=0.0.5"
27
+ },
28
+ "devDependencies": {
29
+ "@eslint/eslintrc": "3.3.1",
30
+ "@typescript-eslint/eslint-plugin": "^8.37.0",
31
+ "@typescript-eslint/parser": "^8.37.0",
32
+ "eslint": "^9.31.0",
33
+ "eslint-config-prettier": "^10.1.5",
34
+ "eslint-plugin-simple-import-sort": "^12.1.1",
35
+ "prettier": "^3.6.2",
36
+ "@storagehub-sdk/core": "0.1.0"
37
+ },
38
+ "engines": {
39
+ "node": ">=22"
40
+ },
25
41
  "scripts": {
26
42
  "build": "node ./build.js && pnpm run build:types",
27
43
  "build:types": "tsc --emitDeclarationOnly -p tsconfig.json",
@@ -34,11 +50,5 @@
34
50
  "lint:fix": "eslint \"{src/**/*.ts,src/**/*.tsx}\" --fix",
35
51
  "test": "vitest",
36
52
  "typecheck": "tsc --noEmit"
37
- },
38
- "dependencies": {
39
- "@storagehub-sdk/core": "workspace:*"
40
- },
41
- "engines": {
42
- "node": ">=22"
43
53
  }
44
- }
54
+ }