@storagehub-sdk/msp-client 0.1.1 → 0.3.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
@@ -55,26 +55,24 @@ import { createReadStream, createWriteStream } from 'node:fs';
55
55
  import { Readable } from 'node:stream';
56
56
 
57
57
  // 1. Connect to MSP service
58
+ let sessionRef: { token: string; user: { address: string } } | undefined;
59
+ const sessionProvider = async () => sessionRef;
58
60
  const client = await MspClient.connect({
59
- baseUrl: 'http://127.0.0.1:8080' // Your MSP backend URL
60
- });
61
+ baseUrl: 'http://127.0.0.1:8080'
62
+ }, sessionProvider);
61
63
 
62
64
  // 2. Check service health
63
- const health = await client.getHealth();
65
+ const health = await client.info.getHealth();
64
66
  console.log('MSP service health:', health);
65
67
 
66
68
  // 3. Authenticate with wallet (SIWE-style)
67
- const walletAddress = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266';
68
- const chainId = 1; // Ethereum mainnet
69
-
70
- // Get authentication message to sign
71
- const { message } = await client.getNonce(walletAddress, chainId);
72
- console.log('Sign this message with your wallet:', message);
73
-
74
- // After signing with your wallet (e.g., MetaMask, WalletConnect, etc.)
75
- const signature = '0xYourWalletSignature...'; // Replace with actual signature
76
- const verified = await client.verify(message, signature);
77
- client.setToken(verified.token); // Set auth token for subsequent requests
69
+ // Example with viem's WalletClient
70
+ import { createWalletClient, http } from 'viem';
71
+ import { privateKeyToAccount } from 'viem/accounts';
72
+ const account = privateKeyToAccount('0x<your_dev_private_key>');
73
+ const wallet = createWalletClient({ account, transport: http('http://127.0.0.1:8545') });
74
+ const session = await client.auth.SIWE(wallet);
75
+ sessionRef = session;
78
76
 
79
77
  // 4. Upload a file
80
78
  const bucketId = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'; // StorageHub bucket identifier
@@ -83,11 +81,11 @@ const filePath = './myfile.txt';
83
81
  const owner = walletAddress; // File owner
84
82
  const location = 'myfile.txt'; // File location/path within the bucket
85
83
 
86
- const receipt = await client.uploadFile(bucketId, fileKey, createReadStream(filePath), owner, location);
84
+ const receipt = await client.files.uploadFile(bucketId, fileKey, createReadStream(filePath), owner, location);
87
85
  console.log('File uploaded successfully:', receipt);
88
86
 
89
87
  // 5. Download the file
90
- const download = await client.downloadByKey(fileKey);
88
+ const download = await client.files.downloadFile(fileKey);
91
89
  const outputPath = './downloaded-file.txt';
92
90
 
93
91
  // Stream the download to a file
@@ -103,55 +101,52 @@ console.log('File downloaded successfully to:', outputPath);
103
101
  console.log('Download status:', download.status);
104
102
 
105
103
  // 6. List the buckets of the currently authenticated user
106
- const buckets = await client.listBuckets();
104
+ const buckets = await client.buckets.listBuckets();
107
105
  console.log('Buckets:', buckets);
108
106
 
109
107
  // 7. Get the metadata of a specific bucket
110
- const bucket = await client.getBucket(bucketId);
108
+ const bucket = await client.buckets.getBucket(bucketId);
111
109
  console.log('Bucket:', bucket);
112
110
 
113
111
  // 8. Get the files of the root folder of a specific bucket
114
- const files = await client.getFiles(bucketId);
112
+ const files = await client.buckets.getFiles(bucketId);
115
113
  console.log('Root files:', files);
116
114
 
117
115
  // 9. Get the files of a specific folder of a specific bucket
118
- const files = await client.getFiles(bucketId, { path: '/path/to/folder' });
119
- console.log('Folder files:', files);
116
+ const folderFiles = await client.buckets.getFiles(bucketId, { path: '/path/to/folder' });
117
+ console.log('Folder files:', folderFiles);
120
118
  ```
121
119
 
122
120
  ## API Reference
123
121
 
124
122
  ### Static Methods
125
- - **`MspClient.connect(config)`** - Create and connect to MSP service
123
+ - **`MspClient.connect(config, sessionProvider)`** - Create and connect to MSP service
126
124
  - `config.baseUrl: string` - MSP backend URL (e.g., `http://127.0.0.1:8080`)
127
125
  - `config.timeoutMs?: number` - Request timeout in milliseconds
128
126
  - `config.defaultHeaders?: Record<string, string>` - Default HTTP headers
129
127
  - `config.fetchImpl?: typeof fetch` - Custom fetch implementation
130
-
131
- ### Instance Methods
132
- - **`getHealth()`** - Check MSP service health and status
133
- - **`getNonce(address, chainId)`** - Get authentication message for wallet signing
134
- - `address: string` - Wallet address (0x...)
135
- - `chainId: number` - Blockchain chain ID (1 for Ethereum mainnet)
136
- - **`verify(message, signature)`** - Verify wallet signature and get auth token
137
- - `message: string` - The message that was signed
138
- - `signature: string` - Wallet signature (0x...)
139
- - **`setToken(token)`** - Set authentication token for subsequent requests
140
- - **`uploadFile(bucketId, fileKey, file, owner, location)`** - Upload file to storage
141
- - `bucketId: string` - Storage bucket identifier
142
- - `fileKey: string` - Unique file key/identifier
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
150
- - Returns: `{ stream: ReadableStream, status: string }`
151
- - **`listBuckets()`** - List all buckets of the currently authenticated user
152
- - **`getBucket(bucketId)`** - Get the metadata of a specific bucket
153
- - **`getFiles(bucketId, options?)`** - Get the files of a specific bucket
154
- - `bucketId: string` - Storage bucket identifier
155
- - `options?: { path?: string, signal?: AbortSignal }` - Optional parameters
156
- - `path?: string` - Path to the folder to get the files from
157
- - `signal?: AbortSignal` - Abort signal to cancel the request
128
+ - `sessionProvider: () => Promise<Session | undefined>` - Returns the current session (or undefined)
129
+
130
+ ### Modules (instance properties)
131
+ - **`auth`**: SIWE auth and session helpers
132
+ - `SIWE(wallet, signal?)` runs full SIWE flow and returns `Session`
133
+ - `getProfile(signal?)` returns the authenticated user's profile
134
+ - **`info`**: MSP info and stats
135
+ - `getHealth(signal?)` returns service health and status
136
+ - `getInfo(signal?)` returns general MSP info (id, version, owner, endpoints)
137
+ - `getStats(signal?)` returns capacity and usage stats
138
+ - `getValuePropositions(signal?)` returns available value props/pricing
139
+ - `getPaymentStreams(signal?)` returns the authenticated user's payment streams
140
+ - **`buckets`**: Buckets and file listings
141
+ - `listBuckets(signal?)` returns all buckets for the current authenticated user
142
+ - `getBucket(bucketId, signal?)` returns metadata for a specific bucket
143
+ - `getFiles(bucketId, { path?, signal? })` returns the file tree at root or at a subpath
144
+ - **`files`**: File metadata, upload and download
145
+ - `getFileInfo(bucketId, fileKey, signal?)` returns metadata for a specific file
146
+ - `uploadFile(...)` uploads a file to the MSP
147
+ - `downloadFile(fileKey, options?)` downloads a file by key (supports range)
148
+
149
+ ### Utilities available via `files`
150
+ - `hexToBytes(hex)`
151
+ - `formFileMetadata(owner, bucketId, location, fingerprint, size)`
152
+ - `computeFileKey(metadata)`
@@ -1,72 +1,17 @@
1
- import type { Bucket, DownloadOptions, DownloadResult, FileInfo, FileListResponse, GetFilesOptions, HealthStatus, InfoResponse, NonceResponse, PaymentStreamsResponse, StatsResponse, UploadOptions, UploadReceipt, ValueProp, VerifyResponse } from "./types.js";
2
1
  import type { HttpClientConfig } from "@storagehub-sdk/core";
3
- import { FileMetadata } from "@storagehub-sdk/core";
4
- export declare class MspClient {
2
+ import { AuthModule } from "./modules/auth.js";
3
+ import { BucketsModule } from "./modules/buckets.js";
4
+ import { ModuleBase } from "./base.js";
5
+ import { FilesModule } from "./modules/files.js";
6
+ import { InfoModule } from "./modules/info.js";
7
+ import type { SessionProvider } from "./types.js";
8
+ export declare class MspClient extends ModuleBase {
5
9
  readonly config: HttpClientConfig;
6
- private readonly http;
7
- token?: string;
10
+ private readonly context;
11
+ readonly auth: AuthModule;
12
+ readonly buckets: BucketsModule;
13
+ readonly files: FilesModule;
14
+ readonly info: InfoModule;
8
15
  private constructor();
9
- static connect(config: HttpClientConfig): Promise<MspClient>;
10
- getHealth(options?: {
11
- signal?: AbortSignal;
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[]>;
25
- /** Get payment streams for current authenticated user */
26
- getPaymentStreams(options?: {
27
- signal?: AbortSignal;
28
- }): Promise<PaymentStreamsResponse>;
29
- /** Request a SIWE-style nonce message for the given address and chainId */
30
- getNonce(address: string, chainId: number, options?: {
31
- signal?: AbortSignal;
32
- }): Promise<NonceResponse>;
33
- /** Verify signed message and receive JWT token */
34
- verify(message: string, signature: string, options?: {
35
- signal?: AbortSignal;
36
- }): Promise<VerifyResponse>;
37
- /** Store token to be sent on subsequent protected requests */
38
- setToken(token: string): void;
39
- /** Merge Authorization header when token is present */
40
- private withAuth;
41
- /** List all buckets for the current authenticateduser */
42
- listBuckets(options?: {
43
- signal?: AbortSignal;
44
- }): Promise<Bucket[]>;
45
- /** Get a specific bucket's metadata by its bucket ID */
46
- getBucket(bucketId: string, options?: {
47
- signal?: AbortSignal;
48
- }): Promise<Bucket>;
49
- /** 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. */
50
- getFiles(bucketId: string, options?: GetFilesOptions): Promise<FileListResponse>;
51
- /** Get metadata for a file in a bucket by fileKey */
52
- getFileInfo(bucketId: string, fileKey: string, options?: {
53
- signal?: AbortSignal;
54
- }): Promise<FileInfo>;
55
- /**
56
- * Upload a file to a bucket with a specific key.
57
- *
58
- * Always uses multipart/form-data upload with both file data and encoded FileMetadata.
59
- * The file data is loaded into memory to create the multipart request.
60
- *
61
- */
62
- uploadFile(bucketId: string, fileKey: string, file: Blob | ArrayBuffer | Uint8Array | ReadableStream<Uint8Array> | unknown, owner: string, location: string, _options?: UploadOptions): Promise<UploadReceipt>;
63
- private coerceToFormPart;
64
- private computeFileFingerprint;
65
- formFileMetadata(owner: string, bucketId: string, location: string, fingerprint: Uint8Array, size: bigint): Promise<FileMetadata>;
66
- hexToBytes(hex: string): Uint8Array;
67
- computeFileKey(fileMetadata: FileMetadata): Promise<Uint8Array>;
68
- /** Download a file by key. */
69
- downloadByKey(fileKey: string, options?: DownloadOptions): Promise<DownloadResult>;
70
- /** Download a file by its location path under a bucket. */
71
- downloadByLocation(bucketId: string, filePath: string, options?: DownloadOptions): Promise<DownloadResult>;
16
+ static connect(config: HttpClientConfig, sessionProvider: SessionProvider): Promise<MspClient>;
72
17
  }
package/dist/base.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ import type { MspClientContext } from "./context.js";
2
+ import type { SessionProvider } from "./types.js";
3
+ export declare abstract class ModuleBase {
4
+ protected readonly ctx: MspClientContext;
5
+ private readonly sessionProvider;
6
+ constructor(ctx: MspClientContext, sessionProvider: SessionProvider);
7
+ protected withAuth(headers?: Record<string, string>): Promise<Record<string, string> | undefined>;
8
+ /**
9
+ * Normalize a user-provided path for HTTP query usage.
10
+ * - Removes all leading '/' characters to avoid double slashes in URLs.
11
+ * - Collapses any repeated slashes in the middle or at the end to a single '/'.
12
+ * Examples:
13
+ * "/foo/bar" -> "foo/bar"
14
+ * "///docs" -> "docs"
15
+ * "foo//bar" -> "foo/bar"
16
+ * "///a//b///" -> "a/b/"
17
+ * "foo/bar" -> "foo/bar" (unchanged)
18
+ * "/" -> ""
19
+ */
20
+ protected normalizePath(path: string): string;
21
+ }
@@ -0,0 +1,5 @@
1
+ import type { HttpClient, HttpClientConfig } from "@storagehub-sdk/core";
2
+ export interface MspClientContext {
3
+ config: HttpClientConfig;
4
+ http: HttpClient;
5
+ }
@@ -1,2 +1,2 @@
1
- import{FileMetadata as B,FileTrie as F,HttpClient as P,initWasm as f}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 P({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}})}getPaymentStreams(e){let t=this.withAuth();return this.http.get("/payment_streams",{...t?{headers:t}:{},...e?.signal?{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(),r=`/buckets/${encodeURIComponent(e)}/info/${encodeURIComponent(t)}`;return this.http.get(r,{...a?{headers:a}:{},...n?.signal?{signal:n.signal}:{}}).then(s=>({...s,uploadedAt:new Date(s.uploadedAt)}))}async uploadFile(e,t,n,a,r,s){await f();let o=`/buckets/${encodeURIComponent(e)}/upload/${encodeURIComponent(t)}`,i=this.withAuth(),l=await this.coerceToFormPart(n),p=l.size,u=await this.computeFileFingerprint(l),d=await this.formFileMetadata(a,e,r,u,BigInt(p)),c=await this.computeFileKey(d),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 m=d.encode(),h=new FormData,y=new Blob([new Uint8Array(m)],{type:"application/octet-stream"});return h.append("file_metadata",y,"file_metadata"),h.append("file",l,"file"),await this.http.put(o,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:o,value:i}=await t.read();if(o)break;i&&(n.push(i),a+=i.length)}}finally{t.releaseLock()}let r=new Uint8Array(a),s=0;for(let o of n)r.set(o,s),s+=o.length;return new Blob([r],{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,r=0;for(;r<n.length;){let s=Math.min(r+a,n.length),o=n.slice(r,s);t.push_chunk(o),r=s}return t.get_root()}async formFileMetadata(e,t,n,a,r){let s=this.hexToBytes(e),o=this.hexToBytes(t),i=new TextEncoder().encode(n);return await f(),new B(s,o,i,r,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 f(),e.getFileKey()}async downloadByKey(e,t){let n=`/download/${encodeURIComponent(e)}`,a={Accept:"*/*"};if(t?.range){let{start:d,end:c}=t.range,g=`bytes=${d}-${c??""}`;a.Range=g}let r=this.withAuth(a),s=await this.http.getRaw(n,{...r?{headers:r}:{},...t?.signal?{signal:t.signal}:{}});if(!s.body)throw new Error("Response body is null - unable to create stream");let o=s.headers.get("content-type"),i=s.headers.get("content-range"),l=s.headers.get("content-length"),p=l!==null?Number(l):void 0,u=typeof p=="number"&&Number.isFinite(p)?p:null;return{stream:s.body,status:s.status,contentType:o,contentRange:i,contentLength:u}}async downloadByLocation(e,t,n){let r=t.replace(/^\/+/,"").split("/").map(encodeURIComponent).join("/"),s=`/buckets/${encodeURIComponent(e)}/download/path/${r}`,o={Accept:"*/*"};if(n?.range){let{start:m,end:h}=n.range,y=`bytes=${m}-${h??""}`;o.Range=y}let i=this.withAuth(o),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 p=l.headers.get("content-type"),u=l.headers.get("content-range"),d=l.headers.get("content-length"),c=d!==null?Number(d):void 0,g=typeof c=="number"&&Number.isFinite(c)?c:null;return{stream:l.body,status:l.status,contentType:p,contentRange:u,contentLength:g}}};export{b as MspClient};
1
+ import{HttpClient as k}from"@storagehub-sdk/core";import{getAddress as F}from"viem";var c=class{ctx;sessionProvider;constructor(t,e){this.ctx=t,this.sessionProvider=e}async withAuth(t){let n=(await this.sessionProvider())?.token;return n?t?{...t,Authorization:`Bearer ${n}`}:{Authorization:`Bearer ${n}`}:t}normalizePath(t){return t.replace(/^\/+|\/{2,}/g,(e,n)=>n===0?"":"/")}};var d=class extends c{getNonce(t,e,n){return this.ctx.http.post("/auth/nonce",{body:{address:t,chainId:e},headers:{"Content-Type":"application/json"},...n?{signal:n}:{}})}async verify(t,e,n){return await this.ctx.http.post("/auth/verify",{body:{message:t,signature:e},headers:{"Content-Type":"application/json"},...n?{signal:n}:{}})}async SIWE(t,e){let n=t.account,s=typeof n=="string"?n:n?.address;if(!s||!n)throw new Error("Wallet client has no active account; set wallet.account before calling SIWE");let r=F(s),o=await t.getChainId(),{message:i}=await this.getNonce(r,o,e),a=await t.signMessage({account:n,message:i});return this.verify(i,a,e)}async getProfile(t){let e=await this.withAuth();return this.ctx.http.get("/auth/profile",{...e?{headers:e}:{},...t?{signal:t}:{}})}};var m=class extends c{async listBuckets(t){let e=await this.withAuth();return this.ctx.http.get("/buckets",{...e?{headers:e}:{},...t?{signal:t}:{}})}async getBucket(t,e){let n=await this.withAuth(),s=`/buckets/${encodeURIComponent(t)}`;return this.ctx.http.get(s,{...n?{headers:n}:{},...e?{signal:e}:{}})}async getFiles(t,e){let n=await this.withAuth(),s=`/buckets/${encodeURIComponent(t)}/files`;return this.ctx.http.get(s,{...n?{headers:n}:{},...e?.signal?{signal:e.signal}:{},...e?.path?{query:{path:this.normalizePath(e.path)}}:{}})}};import{FileMetadata as U,FileTrie as I,initWasm as A}from"@storagehub-sdk/core";var y=class extends c{async getFileInfo(t,e,n){let s=await this.withAuth(),r=`/buckets/${encodeURIComponent(t)}/info/${encodeURIComponent(e)}`,o=await this.ctx.http.get(r,{...s?{headers:s}:{},...n?{signal:n}:{}});return{...o,uploadedAt:new Date(o.uploadedAt)}}async uploadFile(t,e,n,s,r,o){await A();let i=`/buckets/${encodeURIComponent(t)}/upload/${encodeURIComponent(e)}`,a=await this.withAuth(),l=await this.coerceToFormPart(n),u=l.size,g=await this.computeFileFingerprint(l),x=await this.formFileMetadata(s,t,r,g,BigInt(u)),w=await this.computeFileKey(x),b=this.hexToBytes(e);if(w.length!==b.length||!w.every((P,C)=>P===b[C]))throw new Error(`Computed file key ${w.toString()} does not match provided file key ${b.toString()}`);let B=x.encode(),h=new FormData,R=new Blob([new Uint8Array(B)],{type:"application/octet-stream"});return h.append("file_metadata",R,"file_metadata"),h.append("file",l,"file"),await this.ctx.http.put(i,a?{body:h,headers:a}:{body:h})}async downloadFile(t,e){let n=`/download/${encodeURIComponent(t)}`,s={Accept:"*/*"};if(e?.range){let{start:o,end:i}=e.range,a=`bytes=${o}-${i??""}`;s.Range=a}let r=await this.withAuth(s);try{let o=await this.ctx.http.getRaw(n,{...r?{headers:r}:{},...e?.signal?{signal:e.signal}:{}});if(!o.body)throw new Error("Response body is null - unable to create stream");let i=o.headers.get("content-type"),a=o.headers.get("content-range"),l=o.headers.get("content-length"),u=l!==null?Number(l):void 0,g=typeof u=="number"&&Number.isFinite(u)?u:null;return{stream:o.body,status:o.status,contentType:i,contentRange:a,contentLength:g}}catch(o){if(o&&typeof o=="object"&&"status"in o&&typeof o.status=="number")return{stream:new ReadableStream({start(a){a.close()}}),status:o.status,contentType:null,contentRange:null,contentLength:null};throw o}}async coerceToFormPart(t){if(typeof Blob<"u"&&t instanceof Blob)return t;if(t instanceof Uint8Array)return new Blob([t.buffer]);if(typeof ArrayBuffer<"u"&&t instanceof ArrayBuffer)return new Blob([t]);if(t instanceof ReadableStream){let e=t.getReader(),n=[],s=0;try{for(;;){let{done:i,value:a}=await e.read();if(i)break;a&&(n.push(a),s+=a.length)}}finally{e.releaseLock()}let r=new Uint8Array(s),o=0;for(let i of n)r.set(i,o),o+=i.length;return new Blob([r],{type:"application/octet-stream"})}return new Blob([t],{type:"application/octet-stream"})}async computeFileFingerprint(t){let e=new I,n=new Uint8Array(await t.arrayBuffer()),s=1024,r=0;for(;r<n.length;){let o=Math.min(r+s,n.length),i=n.slice(r,o);e.push_chunk(i),r=o}return e.get_root()}async formFileMetadata(t,e,n,s,r){let o=this.hexToBytes(t),i=this.hexToBytes(e),a=new TextEncoder().encode(n);return await A(),new U(o,i,a,r,s)}hexToBytes(t){if(!t)throw new Error("hex string cannot be empty");let e=t.startsWith("0x")?t.slice(2):t;if(e.length%2!==0)throw new Error("hex string must have an even number of characters");if(!/^[0-9a-fA-F]*$/.test(e))throw new Error("hex string contains invalid characters");return new Uint8Array(e.match(/.{2}/g)?.map(n=>Number.parseInt(n,16))||[])}async computeFileKey(t){return await A(),t.getFileKey()}};var f=class extends c{getHealth(t){return this.ctx.http.get("/health",{...t?{signal:t}:{}})}getInfo(t){return this.ctx.http.get("/info",{...t?{signal:t}:{}})}getStats(t){return this.ctx.http.get("/stats",{...t?{signal:t}:{}})}getValuePropositions(t){return this.ctx.http.get("/value-props",{...t?{signal:t}:{}})}async getPaymentStreams(t){let e=await this.withAuth();return this.ctx.http.get("/payment_streams",{...e?{headers:e}:{},...t?{signal:t}:{}})}};var S=class p extends c{config;context;auth;buckets;files;info;constructor(t,e,n){let s={config:t,http:e};super(s,n),this.config=t,this.context=s,this.auth=new d(this.context,n),this.buckets=new m(this.context,n),this.files=new y(this.context,n),this.info=new f(this.context,n)}static async connect(t,e){if(!t?.baseUrl)throw new Error("MspClient.connect: baseUrl is required");let n=new k({baseUrl:t.baseUrl,...t.timeoutMs!==void 0&&{timeoutMs:t.timeoutMs},...t.defaultHeaders!==void 0&&{defaultHeaders:t.defaultHeaders},...t.fetchImpl!==void 0&&{fetchImpl:t.fetchImpl}});if(!e)throw new Error("MspClient.connect: sessionProvider is required");return new p(t,n,e)}};export{S as MspClient};
2
2
  //# sourceMappingURL=index.browser.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/MspClient.ts"],
4
- "sourcesContent": ["import type {\n Bucket,\n DownloadOptions,\n DownloadResult,\n FileInfo,\n FileListResponse,\n GetFilesOptions,\n HealthStatus,\n InfoResponse,\n NonceResponse,\n PaymentStreamsResponse,\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 && {\n defaultHeaders: config.defaultHeaders\n }),\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 /** Get payment streams for current authenticated user */\n getPaymentStreams(options?: { signal?: AbortSignal }): Promise<PaymentStreamsResponse> {\n const headers = this.withAuth();\n return this.http.get<PaymentStreamsResponse>(\"/payment_streams\", {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {})\n });\n }\n\n // Auth endpoints:\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 /** 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(\n (wire): FileInfo => ({\n ...wire,\n uploadedAt: new Date(wire.uploadedAt)\n })\n );\n }\n\n // File endpoints:\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", "headers", "address", "chainId", "message", "signature", "token", "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"]
3
+ "sources": ["../src/MspClient.ts", "../src/modules/auth.ts", "../src/base.ts", "../src/modules/buckets.ts", "../src/modules/files.ts", "../src/modules/info.ts"],
4
+ "sourcesContent": ["import type { HttpClientConfig } from \"@storagehub-sdk/core\";\nimport { HttpClient } from \"@storagehub-sdk/core\";\nimport type { MspClientContext } from \"./context.js\";\nimport { AuthModule } from \"./modules/auth.js\";\nimport { BucketsModule } from \"./modules/buckets.js\";\nimport { ModuleBase } from \"./base.js\";\nimport { FilesModule } from \"./modules/files.js\";\nimport { InfoModule } from \"./modules/info.js\";\nimport type { SessionProvider } from \"./types.js\";\n\nexport class MspClient extends ModuleBase {\n public readonly config: HttpClientConfig;\n private readonly context: MspClientContext;\n public readonly auth: AuthModule;\n public readonly buckets: BucketsModule;\n public readonly files: FilesModule;\n public readonly info: InfoModule;\n\n private constructor(\n config: HttpClientConfig,\n http: HttpClient,\n sessionProvider: SessionProvider\n ) {\n const context: MspClientContext = { config, http };\n super(context, sessionProvider);\n this.config = config;\n this.context = context;\n this.auth = new AuthModule(this.context, sessionProvider);\n this.buckets = new BucketsModule(this.context, sessionProvider);\n this.files = new FilesModule(this.context, sessionProvider);\n this.info = new InfoModule(this.context, sessionProvider);\n }\n\n static async connect(\n config: HttpClientConfig,\n sessionProvider: SessionProvider\n ): 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 && {\n defaultHeaders: config.defaultHeaders\n }),\n ...(config.fetchImpl !== undefined && { fetchImpl: config.fetchImpl })\n });\n\n if (!sessionProvider) throw new Error(\"MspClient.connect: sessionProvider is required\");\n return new MspClient(config, http, sessionProvider);\n }\n}\n", "import type { NonceResponse, Session, UserInfo } from \"../types.js\";\nimport { getAddress, type WalletClient } from \"viem\";\nimport { ModuleBase } from \"../base.js\";\n\nexport class AuthModule extends ModuleBase {\n /**\n * Request nonce for SIWE.\n * - Input: EVM `address`, `chainId`.\n * - Output: message to sign.\n */\n private getNonce(address: string, chainId: number, signal?: AbortSignal): Promise<NonceResponse> {\n return this.ctx.http.post<NonceResponse>(\"/auth/nonce\", {\n body: { address, chainId },\n headers: { \"Content-Type\": \"application/json\" },\n ...(signal ? { signal } : {})\n });\n }\n\n /**\n * Verify SIWE signature.\n * - Persists `session` in context on success.\n */\n private async verify(message: string, signature: string, signal?: AbortSignal): Promise<Session> {\n const session = await this.ctx.http.post<Session>(\"/auth/verify\", {\n body: { message, signature },\n headers: { \"Content-Type\": \"application/json\" },\n ...(signal ? { signal } : {})\n });\n\n return session;\n }\n\n /**\n * Full SIWE flow using a `WalletClient`.\n * - Derives address, fetches nonce, signs message, verifies and stores session.\n */\n async SIWE(wallet: WalletClient, signal?: AbortSignal): Promise<Session> {\n // Resolve the current active account from the WalletClient.\n // - Browser wallets (e.g., MetaMask) surface the user-selected address here.\n // - Viem/local wallets must set `wallet.account` explicitly before calling.\n const account = wallet.account;\n const resolvedAddress = typeof account === \"string\" ? account : account?.address;\n if (!resolvedAddress || !account) {\n throw new Error(\n \"Wallet client has no active account; set wallet.account before calling SIWE\"\n );\n }\n // Get the checksummed address\n const address = getAddress(resolvedAddress);\n const chainId = await wallet.getChainId();\n const { message } = await this.getNonce(address, chainId, signal);\n\n // Sign using the active account resolved above (string or Account object)\n const signature = await wallet.signMessage({ account, message });\n\n return this.verify(message, signature, signal);\n }\n\n /**\n * Fetch authenticated user's profile.\n * - Requires valid `session` (Authorization header added automatically).\n */\n async getProfile(signal?: AbortSignal): Promise<UserInfo> {\n const headers = await this.withAuth();\n return this.ctx.http.get<UserInfo>(\"/auth/profile\", {\n ...(headers ? { headers } : {}),\n ...(signal ? { signal } : {})\n });\n }\n}\n", "import type { MspClientContext } from \"./context.js\";\nimport type { SessionProvider } from \"./types.js\";\n\nexport abstract class ModuleBase {\n protected readonly ctx: MspClientContext;\n private readonly sessionProvider: SessionProvider;\n\n constructor(ctx: MspClientContext, sessionProvider: SessionProvider) {\n this.ctx = ctx;\n this.sessionProvider = sessionProvider;\n }\n\n protected async withAuth(\n headers?: Record<string, string>\n ): Promise<Record<string, string> | undefined> {\n const session = await this.sessionProvider();\n const token = session?.token;\n if (!token) return headers;\n return headers\n ? { ...headers, Authorization: `Bearer ${token}` }\n : { Authorization: `Bearer ${token}` };\n }\n\n /**\n * Normalize a user-provided path for HTTP query usage.\n * - Removes all leading '/' characters to avoid double slashes in URLs.\n * - Collapses any repeated slashes in the middle or at the end to a single '/'.\n * Examples:\n * \"/foo/bar\" -> \"foo/bar\"\n * \"///docs\" -> \"docs\"\n * \"foo//bar\" -> \"foo/bar\"\n * \"///a//b///\" -> \"a/b/\"\n * \"foo/bar\" -> \"foo/bar\" (unchanged)\n * \"/\" -> \"\"\n */\n protected normalizePath(path: string): string {\n // Drop leading slashes (offset === 0), collapse others to '/'\n return path.replace(/^\\/+|\\/{2,}/g, (_m, offset: number) => (offset === 0 ? \"\" : \"/\"));\n }\n}\n", "import type { Bucket, FileListResponse, GetFilesOptions } from \"../types.js\";\nimport { ModuleBase } from \"../base.js\";\n\nexport class BucketsModule extends ModuleBase {\n /** List all buckets for the current authenticated user */\n async listBuckets(signal?: AbortSignal): Promise<Bucket[]> {\n const headers = await this.withAuth();\n return this.ctx.http.get<Bucket[]>(\"/buckets\", {\n ...(headers ? { headers } : {}),\n ...(signal ? { signal } : {})\n });\n }\n\n /** Get a specific bucket's metadata by its bucket ID */\n async getBucket(bucketId: string, signal?: AbortSignal): Promise<Bucket> {\n const headers = await this.withAuth();\n const path = `/buckets/${encodeURIComponent(bucketId)}`;\n return this.ctx.http.get<Bucket>(path, {\n ...(headers ? { headers } : {}),\n ...(signal ? { signal } : {})\n });\n }\n\n /** List files/folders under a path for a bucket (root if no path) */\n async getFiles(bucketId: string, options?: GetFilesOptions): Promise<FileListResponse> {\n const headers = await this.withAuth();\n const path = `/buckets/${encodeURIComponent(bucketId)}/files`;\n return this.ctx.http.get<FileListResponse>(path, {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n ...(options?.path ? { query: { path: this.normalizePath(options.path) } } : {})\n });\n }\n}\n", "import { ModuleBase } from \"../base.js\";\nimport type {\n DownloadOptions,\n DownloadResult,\n FileInfo,\n UploadOptions,\n UploadReceipt\n} from \"../types.js\";\nimport { FileMetadata, FileTrie, initWasm } from \"@storagehub-sdk/core\";\n\nexport class FilesModule extends ModuleBase {\n /** Get metadata for a file in a bucket by fileKey */\n async getFileInfo(bucketId: string, fileKey: string, signal?: AbortSignal): Promise<FileInfo> {\n const headers = await this.withAuth();\n const path = `/buckets/${encodeURIComponent(bucketId)}/info/${encodeURIComponent(fileKey)}`;\n type FileInfoWire = Omit<FileInfo, \"uploadedAt\"> & { uploadedAt: string };\n const wire = await this.ctx.http.get<FileInfoWire>(path, {\n ...(headers ? { headers } : {}),\n ...(signal ? { signal } : {})\n });\n return {\n ...wire,\n uploadedAt: new Date(wire.uploadedAt)\n };\n }\n\n /** Upload a file to a bucket with a specific key */\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 = await 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 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.ctx.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 /** Download a file by key */\n async downloadFile(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\n const headers = await this.withAuth(baseHeaders);\n\n try {\n const res = await this.ctx.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 } catch (error) {\n // Handle HTTP errors by returning them as a DownloadResult with the error status\n if (\n error &&\n typeof error === \"object\" &&\n \"status\" in error &&\n typeof error.status === \"number\"\n ) {\n // Create an empty stream for error responses\n const emptyStream = new ReadableStream<Uint8Array>({\n start(controller) {\n controller.close();\n }\n });\n\n return {\n stream: emptyStream,\n status: error.status,\n contentType: null,\n contentRange: null,\n contentLength: null\n };\n }\n // Re-throw non-HTTP errors\n throw error;\n }\n }\n\n // Helpers\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 private 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 private 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 private async computeFileKey(fileMetadata: FileMetadata): Promise<Uint8Array> {\n await initWasm();\n return fileMetadata.getFileKey();\n }\n}\n", "import { ModuleBase } from \"../base.js\";\nimport type {\n HealthStatus,\n InfoResponse,\n PaymentStreamsResponse,\n StatsResponse,\n ValueProp\n} from \"../types.js\";\n\nexport class InfoModule extends ModuleBase {\n getHealth(signal?: AbortSignal): Promise<HealthStatus> {\n return this.ctx.http.get<HealthStatus>(\"/health\", {\n ...(signal ? { signal } : {})\n });\n }\n\n /** Get general MSP information */\n getInfo(signal?: AbortSignal): Promise<InfoResponse> {\n return this.ctx.http.get<InfoResponse>(\"/info\", {\n ...(signal ? { signal } : {})\n });\n }\n\n /** Get MSP statistics */\n getStats(signal?: AbortSignal): Promise<StatsResponse> {\n return this.ctx.http.get<StatsResponse>(\"/stats\", {\n ...(signal ? { signal } : {})\n });\n }\n\n /** Get available value propositions */\n getValuePropositions(signal?: AbortSignal): Promise<ValueProp[]> {\n return this.ctx.http.get<ValueProp[]>(\"/value-props\", {\n ...(signal ? { signal } : {})\n });\n }\n\n /** Get payment streams for current authenticated user */\n async getPaymentStreams(signal?: AbortSignal): Promise<PaymentStreamsResponse> {\n const headers = await this.withAuth();\n return this.ctx.http.get<PaymentStreamsResponse>(\"/payment_streams\", {\n ...(headers ? { headers } : {}),\n ...(signal ? { signal } : {})\n });\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["HttpClient", "getAddress", "ModuleBase", "ctx", "sessionProvider", "headers", "token", "path", "_m", "offset", "AuthModule", "ModuleBase", "address", "chainId", "signal", "message", "signature", "wallet", "account", "resolvedAddress", "getAddress", "headers", "BucketsModule", "ModuleBase", "signal", "headers", "bucketId", "path", "options", "FileMetadata", "FileTrie", "initWasm", "FilesModule", "ModuleBase", "bucketId", "fileKey", "signal", "headers", "path", "wire", "file", "owner", "location", "_options", "backendPath", "authHeaders", "fileBlob", "fileSize", "fingerprint", "metadata", "computedFileKey", "expectedFileKeyBytes", "byte", "index", "encodedMetadata", "form", "fileMetadataBlob", "options", "baseHeaders", "start", "end", "rangeValue", "res", "contentType", "contentRange", "contentLengthHeader", "parsedLength", "contentLength", "error", "controller", "reader", "chunks", "totalLength", "done", "value", "combined", "offset", "chunk", "trie", "fileBytes", "CHUNK_SIZE", "size", "ownerBytes", "bucketIdBytes", "locationBytes", "hex", "cleanHex", "fileMetadata", "InfoModule", "ModuleBase", "signal", "headers", "MspClient", "_MspClient", "ModuleBase", "config", "http", "sessionProvider", "context", "AuthModule", "BucketsModule", "FilesModule", "InfoModule", "HttpClient"]
7
7
  }
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export { MspClient } from "./MspClient.js";
2
- export type { Bucket, Capacity, DownloadOptions, DownloadResult, FileTree, FileInfo, FileListResponse, HealthStatus, InfoResponse, NonceResponse, PaymentStreamInfo, PaymentStreamsResponse, StatsResponse, UploadOptions, UploadReceipt, ValueProp, VerifyResponse } from "./types.js";
2
+ export type { Bucket, Capacity, DownloadOptions, DownloadResult, FileTree, FileInfo, FileListResponse, HealthStatus, InfoResponse, NonceResponse, AuthState, AuthStatus, UserInfo, PaymentStreamInfo, PaymentStreamsResponse, StatsResponse, UploadOptions, UploadReceipt, ValueProp, Session, SessionProvider } from "./types.js";
@@ -1,2 +1,2 @@
1
- import{FileMetadata as B,FileTrie as F,HttpClient as P,initWasm as f}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 P({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}})}getPaymentStreams(e){let t=this.withAuth();return this.http.get("/payment_streams",{...t?{headers:t}:{},...e?.signal?{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(),r=`/buckets/${encodeURIComponent(e)}/info/${encodeURIComponent(t)}`;return this.http.get(r,{...a?{headers:a}:{},...n?.signal?{signal:n.signal}:{}}).then(s=>({...s,uploadedAt:new Date(s.uploadedAt)}))}async uploadFile(e,t,n,a,r,s){await f();let o=`/buckets/${encodeURIComponent(e)}/upload/${encodeURIComponent(t)}`,i=this.withAuth(),l=await this.coerceToFormPart(n),p=l.size,u=await this.computeFileFingerprint(l),d=await this.formFileMetadata(a,e,r,u,BigInt(p)),c=await this.computeFileKey(d),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 m=d.encode(),h=new FormData,y=new Blob([new Uint8Array(m)],{type:"application/octet-stream"});return h.append("file_metadata",y,"file_metadata"),h.append("file",l,"file"),await this.http.put(o,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:o,value:i}=await t.read();if(o)break;i&&(n.push(i),a+=i.length)}}finally{t.releaseLock()}let r=new Uint8Array(a),s=0;for(let o of n)r.set(o,s),s+=o.length;return new Blob([r],{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,r=0;for(;r<n.length;){let s=Math.min(r+a,n.length),o=n.slice(r,s);t.push_chunk(o),r=s}return t.get_root()}async formFileMetadata(e,t,n,a,r){let s=this.hexToBytes(e),o=this.hexToBytes(t),i=new TextEncoder().encode(n);return await f(),new B(s,o,i,r,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 f(),e.getFileKey()}async downloadByKey(e,t){let n=`/download/${encodeURIComponent(e)}`,a={Accept:"*/*"};if(t?.range){let{start:d,end:c}=t.range,g=`bytes=${d}-${c??""}`;a.Range=g}let r=this.withAuth(a),s=await this.http.getRaw(n,{...r?{headers:r}:{},...t?.signal?{signal:t.signal}:{}});if(!s.body)throw new Error("Response body is null - unable to create stream");let o=s.headers.get("content-type"),i=s.headers.get("content-range"),l=s.headers.get("content-length"),p=l!==null?Number(l):void 0,u=typeof p=="number"&&Number.isFinite(p)?p:null;return{stream:s.body,status:s.status,contentType:o,contentRange:i,contentLength:u}}async downloadByLocation(e,t,n){let r=t.replace(/^\/+/,"").split("/").map(encodeURIComponent).join("/"),s=`/buckets/${encodeURIComponent(e)}/download/path/${r}`,o={Accept:"*/*"};if(n?.range){let{start:m,end:h}=n.range,y=`bytes=${m}-${h??""}`;o.Range=y}let i=this.withAuth(o),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 p=l.headers.get("content-type"),u=l.headers.get("content-range"),d=l.headers.get("content-length"),c=d!==null?Number(d):void 0,g=typeof c=="number"&&Number.isFinite(c)?c:null;return{stream:l.body,status:l.status,contentType:p,contentRange:u,contentLength:g}}};export{b as MspClient};
1
+ import{HttpClient as k}from"@storagehub-sdk/core";import{getAddress as F}from"viem";var c=class{ctx;sessionProvider;constructor(t,e){this.ctx=t,this.sessionProvider=e}async withAuth(t){let n=(await this.sessionProvider())?.token;return n?t?{...t,Authorization:`Bearer ${n}`}:{Authorization:`Bearer ${n}`}:t}normalizePath(t){return t.replace(/^\/+|\/{2,}/g,(e,n)=>n===0?"":"/")}};var d=class extends c{getNonce(t,e,n){return this.ctx.http.post("/auth/nonce",{body:{address:t,chainId:e},headers:{"Content-Type":"application/json"},...n?{signal:n}:{}})}async verify(t,e,n){return await this.ctx.http.post("/auth/verify",{body:{message:t,signature:e},headers:{"Content-Type":"application/json"},...n?{signal:n}:{}})}async SIWE(t,e){let n=t.account,s=typeof n=="string"?n:n?.address;if(!s||!n)throw new Error("Wallet client has no active account; set wallet.account before calling SIWE");let r=F(s),o=await t.getChainId(),{message:i}=await this.getNonce(r,o,e),a=await t.signMessage({account:n,message:i});return this.verify(i,a,e)}async getProfile(t){let e=await this.withAuth();return this.ctx.http.get("/auth/profile",{...e?{headers:e}:{},...t?{signal:t}:{}})}};var m=class extends c{async listBuckets(t){let e=await this.withAuth();return this.ctx.http.get("/buckets",{...e?{headers:e}:{},...t?{signal:t}:{}})}async getBucket(t,e){let n=await this.withAuth(),s=`/buckets/${encodeURIComponent(t)}`;return this.ctx.http.get(s,{...n?{headers:n}:{},...e?{signal:e}:{}})}async getFiles(t,e){let n=await this.withAuth(),s=`/buckets/${encodeURIComponent(t)}/files`;return this.ctx.http.get(s,{...n?{headers:n}:{},...e?.signal?{signal:e.signal}:{},...e?.path?{query:{path:this.normalizePath(e.path)}}:{}})}};import{FileMetadata as U,FileTrie as I,initWasm as A}from"@storagehub-sdk/core";var y=class extends c{async getFileInfo(t,e,n){let s=await this.withAuth(),r=`/buckets/${encodeURIComponent(t)}/info/${encodeURIComponent(e)}`,o=await this.ctx.http.get(r,{...s?{headers:s}:{},...n?{signal:n}:{}});return{...o,uploadedAt:new Date(o.uploadedAt)}}async uploadFile(t,e,n,s,r,o){await A();let i=`/buckets/${encodeURIComponent(t)}/upload/${encodeURIComponent(e)}`,a=await this.withAuth(),l=await this.coerceToFormPart(n),u=l.size,g=await this.computeFileFingerprint(l),x=await this.formFileMetadata(s,t,r,g,BigInt(u)),w=await this.computeFileKey(x),b=this.hexToBytes(e);if(w.length!==b.length||!w.every((P,C)=>P===b[C]))throw new Error(`Computed file key ${w.toString()} does not match provided file key ${b.toString()}`);let B=x.encode(),h=new FormData,R=new Blob([new Uint8Array(B)],{type:"application/octet-stream"});return h.append("file_metadata",R,"file_metadata"),h.append("file",l,"file"),await this.ctx.http.put(i,a?{body:h,headers:a}:{body:h})}async downloadFile(t,e){let n=`/download/${encodeURIComponent(t)}`,s={Accept:"*/*"};if(e?.range){let{start:o,end:i}=e.range,a=`bytes=${o}-${i??""}`;s.Range=a}let r=await this.withAuth(s);try{let o=await this.ctx.http.getRaw(n,{...r?{headers:r}:{},...e?.signal?{signal:e.signal}:{}});if(!o.body)throw new Error("Response body is null - unable to create stream");let i=o.headers.get("content-type"),a=o.headers.get("content-range"),l=o.headers.get("content-length"),u=l!==null?Number(l):void 0,g=typeof u=="number"&&Number.isFinite(u)?u:null;return{stream:o.body,status:o.status,contentType:i,contentRange:a,contentLength:g}}catch(o){if(o&&typeof o=="object"&&"status"in o&&typeof o.status=="number")return{stream:new ReadableStream({start(a){a.close()}}),status:o.status,contentType:null,contentRange:null,contentLength:null};throw o}}async coerceToFormPart(t){if(typeof Blob<"u"&&t instanceof Blob)return t;if(t instanceof Uint8Array)return new Blob([t.buffer]);if(typeof ArrayBuffer<"u"&&t instanceof ArrayBuffer)return new Blob([t]);if(t instanceof ReadableStream){let e=t.getReader(),n=[],s=0;try{for(;;){let{done:i,value:a}=await e.read();if(i)break;a&&(n.push(a),s+=a.length)}}finally{e.releaseLock()}let r=new Uint8Array(s),o=0;for(let i of n)r.set(i,o),o+=i.length;return new Blob([r],{type:"application/octet-stream"})}return new Blob([t],{type:"application/octet-stream"})}async computeFileFingerprint(t){let e=new I,n=new Uint8Array(await t.arrayBuffer()),s=1024,r=0;for(;r<n.length;){let o=Math.min(r+s,n.length),i=n.slice(r,o);e.push_chunk(i),r=o}return e.get_root()}async formFileMetadata(t,e,n,s,r){let o=this.hexToBytes(t),i=this.hexToBytes(e),a=new TextEncoder().encode(n);return await A(),new U(o,i,a,r,s)}hexToBytes(t){if(!t)throw new Error("hex string cannot be empty");let e=t.startsWith("0x")?t.slice(2):t;if(e.length%2!==0)throw new Error("hex string must have an even number of characters");if(!/^[0-9a-fA-F]*$/.test(e))throw new Error("hex string contains invalid characters");return new Uint8Array(e.match(/.{2}/g)?.map(n=>Number.parseInt(n,16))||[])}async computeFileKey(t){return await A(),t.getFileKey()}};var f=class extends c{getHealth(t){return this.ctx.http.get("/health",{...t?{signal:t}:{}})}getInfo(t){return this.ctx.http.get("/info",{...t?{signal:t}:{}})}getStats(t){return this.ctx.http.get("/stats",{...t?{signal:t}:{}})}getValuePropositions(t){return this.ctx.http.get("/value-props",{...t?{signal:t}:{}})}async getPaymentStreams(t){let e=await this.withAuth();return this.ctx.http.get("/payment_streams",{...e?{headers:e}:{},...t?{signal:t}:{}})}};var S=class p extends c{config;context;auth;buckets;files;info;constructor(t,e,n){let s={config:t,http:e};super(s,n),this.config=t,this.context=s,this.auth=new d(this.context,n),this.buckets=new m(this.context,n),this.files=new y(this.context,n),this.info=new f(this.context,n)}static async connect(t,e){if(!t?.baseUrl)throw new Error("MspClient.connect: baseUrl is required");let n=new k({baseUrl:t.baseUrl,...t.timeoutMs!==void 0&&{timeoutMs:t.timeoutMs},...t.defaultHeaders!==void 0&&{defaultHeaders:t.defaultHeaders},...t.fetchImpl!==void 0&&{fetchImpl:t.fetchImpl}});if(!e)throw new Error("MspClient.connect: sessionProvider is required");return new p(t,n,e)}};export{S as MspClient};
2
2
  //# sourceMappingURL=index.node.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/MspClient.ts"],
4
- "sourcesContent": ["import type {\n Bucket,\n DownloadOptions,\n DownloadResult,\n FileInfo,\n FileListResponse,\n GetFilesOptions,\n HealthStatus,\n InfoResponse,\n NonceResponse,\n PaymentStreamsResponse,\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 && {\n defaultHeaders: config.defaultHeaders\n }),\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 /** Get payment streams for current authenticated user */\n getPaymentStreams(options?: { signal?: AbortSignal }): Promise<PaymentStreamsResponse> {\n const headers = this.withAuth();\n return this.http.get<PaymentStreamsResponse>(\"/payment_streams\", {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {})\n });\n }\n\n // Auth endpoints:\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 /** 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(\n (wire): FileInfo => ({\n ...wire,\n uploadedAt: new Date(wire.uploadedAt)\n })\n );\n }\n\n // File endpoints:\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", "headers", "address", "chainId", "message", "signature", "token", "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"]
3
+ "sources": ["../src/MspClient.ts", "../src/modules/auth.ts", "../src/base.ts", "../src/modules/buckets.ts", "../src/modules/files.ts", "../src/modules/info.ts"],
4
+ "sourcesContent": ["import type { HttpClientConfig } from \"@storagehub-sdk/core\";\nimport { HttpClient } from \"@storagehub-sdk/core\";\nimport type { MspClientContext } from \"./context.js\";\nimport { AuthModule } from \"./modules/auth.js\";\nimport { BucketsModule } from \"./modules/buckets.js\";\nimport { ModuleBase } from \"./base.js\";\nimport { FilesModule } from \"./modules/files.js\";\nimport { InfoModule } from \"./modules/info.js\";\nimport type { SessionProvider } from \"./types.js\";\n\nexport class MspClient extends ModuleBase {\n public readonly config: HttpClientConfig;\n private readonly context: MspClientContext;\n public readonly auth: AuthModule;\n public readonly buckets: BucketsModule;\n public readonly files: FilesModule;\n public readonly info: InfoModule;\n\n private constructor(\n config: HttpClientConfig,\n http: HttpClient,\n sessionProvider: SessionProvider\n ) {\n const context: MspClientContext = { config, http };\n super(context, sessionProvider);\n this.config = config;\n this.context = context;\n this.auth = new AuthModule(this.context, sessionProvider);\n this.buckets = new BucketsModule(this.context, sessionProvider);\n this.files = new FilesModule(this.context, sessionProvider);\n this.info = new InfoModule(this.context, sessionProvider);\n }\n\n static async connect(\n config: HttpClientConfig,\n sessionProvider: SessionProvider\n ): 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 && {\n defaultHeaders: config.defaultHeaders\n }),\n ...(config.fetchImpl !== undefined && { fetchImpl: config.fetchImpl })\n });\n\n if (!sessionProvider) throw new Error(\"MspClient.connect: sessionProvider is required\");\n return new MspClient(config, http, sessionProvider);\n }\n}\n", "import type { NonceResponse, Session, UserInfo } from \"../types.js\";\nimport { getAddress, type WalletClient } from \"viem\";\nimport { ModuleBase } from \"../base.js\";\n\nexport class AuthModule extends ModuleBase {\n /**\n * Request nonce for SIWE.\n * - Input: EVM `address`, `chainId`.\n * - Output: message to sign.\n */\n private getNonce(address: string, chainId: number, signal?: AbortSignal): Promise<NonceResponse> {\n return this.ctx.http.post<NonceResponse>(\"/auth/nonce\", {\n body: { address, chainId },\n headers: { \"Content-Type\": \"application/json\" },\n ...(signal ? { signal } : {})\n });\n }\n\n /**\n * Verify SIWE signature.\n * - Persists `session` in context on success.\n */\n private async verify(message: string, signature: string, signal?: AbortSignal): Promise<Session> {\n const session = await this.ctx.http.post<Session>(\"/auth/verify\", {\n body: { message, signature },\n headers: { \"Content-Type\": \"application/json\" },\n ...(signal ? { signal } : {})\n });\n\n return session;\n }\n\n /**\n * Full SIWE flow using a `WalletClient`.\n * - Derives address, fetches nonce, signs message, verifies and stores session.\n */\n async SIWE(wallet: WalletClient, signal?: AbortSignal): Promise<Session> {\n // Resolve the current active account from the WalletClient.\n // - Browser wallets (e.g., MetaMask) surface the user-selected address here.\n // - Viem/local wallets must set `wallet.account` explicitly before calling.\n const account = wallet.account;\n const resolvedAddress = typeof account === \"string\" ? account : account?.address;\n if (!resolvedAddress || !account) {\n throw new Error(\n \"Wallet client has no active account; set wallet.account before calling SIWE\"\n );\n }\n // Get the checksummed address\n const address = getAddress(resolvedAddress);\n const chainId = await wallet.getChainId();\n const { message } = await this.getNonce(address, chainId, signal);\n\n // Sign using the active account resolved above (string or Account object)\n const signature = await wallet.signMessage({ account, message });\n\n return this.verify(message, signature, signal);\n }\n\n /**\n * Fetch authenticated user's profile.\n * - Requires valid `session` (Authorization header added automatically).\n */\n async getProfile(signal?: AbortSignal): Promise<UserInfo> {\n const headers = await this.withAuth();\n return this.ctx.http.get<UserInfo>(\"/auth/profile\", {\n ...(headers ? { headers } : {}),\n ...(signal ? { signal } : {})\n });\n }\n}\n", "import type { MspClientContext } from \"./context.js\";\nimport type { SessionProvider } from \"./types.js\";\n\nexport abstract class ModuleBase {\n protected readonly ctx: MspClientContext;\n private readonly sessionProvider: SessionProvider;\n\n constructor(ctx: MspClientContext, sessionProvider: SessionProvider) {\n this.ctx = ctx;\n this.sessionProvider = sessionProvider;\n }\n\n protected async withAuth(\n headers?: Record<string, string>\n ): Promise<Record<string, string> | undefined> {\n const session = await this.sessionProvider();\n const token = session?.token;\n if (!token) return headers;\n return headers\n ? { ...headers, Authorization: `Bearer ${token}` }\n : { Authorization: `Bearer ${token}` };\n }\n\n /**\n * Normalize a user-provided path for HTTP query usage.\n * - Removes all leading '/' characters to avoid double slashes in URLs.\n * - Collapses any repeated slashes in the middle or at the end to a single '/'.\n * Examples:\n * \"/foo/bar\" -> \"foo/bar\"\n * \"///docs\" -> \"docs\"\n * \"foo//bar\" -> \"foo/bar\"\n * \"///a//b///\" -> \"a/b/\"\n * \"foo/bar\" -> \"foo/bar\" (unchanged)\n * \"/\" -> \"\"\n */\n protected normalizePath(path: string): string {\n // Drop leading slashes (offset === 0), collapse others to '/'\n return path.replace(/^\\/+|\\/{2,}/g, (_m, offset: number) => (offset === 0 ? \"\" : \"/\"));\n }\n}\n", "import type { Bucket, FileListResponse, GetFilesOptions } from \"../types.js\";\nimport { ModuleBase } from \"../base.js\";\n\nexport class BucketsModule extends ModuleBase {\n /** List all buckets for the current authenticated user */\n async listBuckets(signal?: AbortSignal): Promise<Bucket[]> {\n const headers = await this.withAuth();\n return this.ctx.http.get<Bucket[]>(\"/buckets\", {\n ...(headers ? { headers } : {}),\n ...(signal ? { signal } : {})\n });\n }\n\n /** Get a specific bucket's metadata by its bucket ID */\n async getBucket(bucketId: string, signal?: AbortSignal): Promise<Bucket> {\n const headers = await this.withAuth();\n const path = `/buckets/${encodeURIComponent(bucketId)}`;\n return this.ctx.http.get<Bucket>(path, {\n ...(headers ? { headers } : {}),\n ...(signal ? { signal } : {})\n });\n }\n\n /** List files/folders under a path for a bucket (root if no path) */\n async getFiles(bucketId: string, options?: GetFilesOptions): Promise<FileListResponse> {\n const headers = await this.withAuth();\n const path = `/buckets/${encodeURIComponent(bucketId)}/files`;\n return this.ctx.http.get<FileListResponse>(path, {\n ...(headers ? { headers } : {}),\n ...(options?.signal ? { signal: options.signal } : {}),\n ...(options?.path ? { query: { path: this.normalizePath(options.path) } } : {})\n });\n }\n}\n", "import { ModuleBase } from \"../base.js\";\nimport type {\n DownloadOptions,\n DownloadResult,\n FileInfo,\n UploadOptions,\n UploadReceipt\n} from \"../types.js\";\nimport { FileMetadata, FileTrie, initWasm } from \"@storagehub-sdk/core\";\n\nexport class FilesModule extends ModuleBase {\n /** Get metadata for a file in a bucket by fileKey */\n async getFileInfo(bucketId: string, fileKey: string, signal?: AbortSignal): Promise<FileInfo> {\n const headers = await this.withAuth();\n const path = `/buckets/${encodeURIComponent(bucketId)}/info/${encodeURIComponent(fileKey)}`;\n type FileInfoWire = Omit<FileInfo, \"uploadedAt\"> & { uploadedAt: string };\n const wire = await this.ctx.http.get<FileInfoWire>(path, {\n ...(headers ? { headers } : {}),\n ...(signal ? { signal } : {})\n });\n return {\n ...wire,\n uploadedAt: new Date(wire.uploadedAt)\n };\n }\n\n /** Upload a file to a bucket with a specific key */\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 = await 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 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.ctx.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 /** Download a file by key */\n async downloadFile(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\n const headers = await this.withAuth(baseHeaders);\n\n try {\n const res = await this.ctx.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 } catch (error) {\n // Handle HTTP errors by returning them as a DownloadResult with the error status\n if (\n error &&\n typeof error === \"object\" &&\n \"status\" in error &&\n typeof error.status === \"number\"\n ) {\n // Create an empty stream for error responses\n const emptyStream = new ReadableStream<Uint8Array>({\n start(controller) {\n controller.close();\n }\n });\n\n return {\n stream: emptyStream,\n status: error.status,\n contentType: null,\n contentRange: null,\n contentLength: null\n };\n }\n // Re-throw non-HTTP errors\n throw error;\n }\n }\n\n // Helpers\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 private 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 private 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 private async computeFileKey(fileMetadata: FileMetadata): Promise<Uint8Array> {\n await initWasm();\n return fileMetadata.getFileKey();\n }\n}\n", "import { ModuleBase } from \"../base.js\";\nimport type {\n HealthStatus,\n InfoResponse,\n PaymentStreamsResponse,\n StatsResponse,\n ValueProp\n} from \"../types.js\";\n\nexport class InfoModule extends ModuleBase {\n getHealth(signal?: AbortSignal): Promise<HealthStatus> {\n return this.ctx.http.get<HealthStatus>(\"/health\", {\n ...(signal ? { signal } : {})\n });\n }\n\n /** Get general MSP information */\n getInfo(signal?: AbortSignal): Promise<InfoResponse> {\n return this.ctx.http.get<InfoResponse>(\"/info\", {\n ...(signal ? { signal } : {})\n });\n }\n\n /** Get MSP statistics */\n getStats(signal?: AbortSignal): Promise<StatsResponse> {\n return this.ctx.http.get<StatsResponse>(\"/stats\", {\n ...(signal ? { signal } : {})\n });\n }\n\n /** Get available value propositions */\n getValuePropositions(signal?: AbortSignal): Promise<ValueProp[]> {\n return this.ctx.http.get<ValueProp[]>(\"/value-props\", {\n ...(signal ? { signal } : {})\n });\n }\n\n /** Get payment streams for current authenticated user */\n async getPaymentStreams(signal?: AbortSignal): Promise<PaymentStreamsResponse> {\n const headers = await this.withAuth();\n return this.ctx.http.get<PaymentStreamsResponse>(\"/payment_streams\", {\n ...(headers ? { headers } : {}),\n ...(signal ? { signal } : {})\n });\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["HttpClient", "getAddress", "ModuleBase", "ctx", "sessionProvider", "headers", "token", "path", "_m", "offset", "AuthModule", "ModuleBase", "address", "chainId", "signal", "message", "signature", "wallet", "account", "resolvedAddress", "getAddress", "headers", "BucketsModule", "ModuleBase", "signal", "headers", "bucketId", "path", "options", "FileMetadata", "FileTrie", "initWasm", "FilesModule", "ModuleBase", "bucketId", "fileKey", "signal", "headers", "path", "wire", "file", "owner", "location", "_options", "backendPath", "authHeaders", "fileBlob", "fileSize", "fingerprint", "metadata", "computedFileKey", "expectedFileKeyBytes", "byte", "index", "encodedMetadata", "form", "fileMetadataBlob", "options", "baseHeaders", "start", "end", "rangeValue", "res", "contentType", "contentRange", "contentLengthHeader", "parsedLength", "contentLength", "error", "controller", "reader", "chunks", "totalLength", "done", "value", "combined", "offset", "chunk", "trie", "fileBytes", "CHUNK_SIZE", "size", "ownerBytes", "bucketIdBytes", "locationBytes", "hex", "cleanHex", "fileMetadata", "InfoModule", "ModuleBase", "signal", "headers", "MspClient", "_MspClient", "ModuleBase", "config", "http", "sessionProvider", "context", "AuthModule", "BucketsModule", "FilesModule", "InfoModule", "HttpClient"]
7
7
  }
@@ -0,0 +1,26 @@
1
+ import type { Session, UserInfo } from "../types.js";
2
+ import { type WalletClient } from "viem";
3
+ import { ModuleBase } from "../base.js";
4
+ export declare class AuthModule extends ModuleBase {
5
+ /**
6
+ * Request nonce for SIWE.
7
+ * - Input: EVM `address`, `chainId`.
8
+ * - Output: message to sign.
9
+ */
10
+ private getNonce;
11
+ /**
12
+ * Verify SIWE signature.
13
+ * - Persists `session` in context on success.
14
+ */
15
+ private verify;
16
+ /**
17
+ * Full SIWE flow using a `WalletClient`.
18
+ * - Derives address, fetches nonce, signs message, verifies and stores session.
19
+ */
20
+ SIWE(wallet: WalletClient, signal?: AbortSignal): Promise<Session>;
21
+ /**
22
+ * Fetch authenticated user's profile.
23
+ * - Requires valid `session` (Authorization header added automatically).
24
+ */
25
+ getProfile(signal?: AbortSignal): Promise<UserInfo>;
26
+ }
@@ -0,0 +1,10 @@
1
+ import type { Bucket, FileListResponse, GetFilesOptions } from "../types.js";
2
+ import { ModuleBase } from "../base.js";
3
+ export declare class BucketsModule extends ModuleBase {
4
+ /** List all buckets for the current authenticated user */
5
+ listBuckets(signal?: AbortSignal): Promise<Bucket[]>;
6
+ /** Get a specific bucket's metadata by its bucket ID */
7
+ getBucket(bucketId: string, signal?: AbortSignal): Promise<Bucket>;
8
+ /** List files/folders under a path for a bucket (root if no path) */
9
+ getFiles(bucketId: string, options?: GetFilesOptions): Promise<FileListResponse>;
10
+ }
@@ -0,0 +1,15 @@
1
+ import { ModuleBase } from "../base.js";
2
+ import type { DownloadOptions, DownloadResult, FileInfo, UploadOptions, UploadReceipt } from "../types.js";
3
+ export declare class FilesModule extends ModuleBase {
4
+ /** Get metadata for a file in a bucket by fileKey */
5
+ getFileInfo(bucketId: string, fileKey: string, signal?: AbortSignal): Promise<FileInfo>;
6
+ /** Upload a file to a bucket with a specific key */
7
+ uploadFile(bucketId: string, fileKey: string, file: Blob | ArrayBuffer | Uint8Array | ReadableStream<Uint8Array> | unknown, owner: string, location: string, _options?: UploadOptions): Promise<UploadReceipt>;
8
+ /** Download a file by key */
9
+ downloadFile(fileKey: string, options?: DownloadOptions): Promise<DownloadResult>;
10
+ private coerceToFormPart;
11
+ private computeFileFingerprint;
12
+ private formFileMetadata;
13
+ private hexToBytes;
14
+ private computeFileKey;
15
+ }
@@ -0,0 +1,13 @@
1
+ import { ModuleBase } from "../base.js";
2
+ import type { HealthStatus, InfoResponse, PaymentStreamsResponse, StatsResponse, ValueProp } from "../types.js";
3
+ export declare class InfoModule extends ModuleBase {
4
+ getHealth(signal?: AbortSignal): Promise<HealthStatus>;
5
+ /** Get general MSP information */
6
+ getInfo(signal?: AbortSignal): Promise<InfoResponse>;
7
+ /** Get MSP statistics */
8
+ getStats(signal?: AbortSignal): Promise<StatsResponse>;
9
+ /** Get available value propositions */
10
+ getValuePropositions(signal?: AbortSignal): Promise<ValueProp[]>;
11
+ /** Get payment streams for current authenticated user */
12
+ getPaymentStreams(signal?: AbortSignal): Promise<PaymentStreamsResponse>;
13
+ }
package/dist/types.d.ts CHANGED
@@ -58,7 +58,20 @@ export interface UploadReceipt {
58
58
  export interface NonceResponse {
59
59
  message: string;
60
60
  }
61
- export interface VerifyResponse {
61
+ export declare enum AuthState {
62
+ NotAuthenticated = "NotAuthenticated",
63
+ TokenExpired = "TokenExpired",
64
+ Authenticated = "Authenticated"
65
+ }
66
+ export interface AuthStatus {
67
+ status: AuthState;
68
+ [k: string]: unknown;
69
+ }
70
+ export interface UserInfo {
71
+ address: string;
72
+ ens: string;
73
+ }
74
+ export interface Session {
62
75
  token: string;
63
76
  user: {
64
77
  address: string;
@@ -66,6 +79,7 @@ export interface VerifyResponse {
66
79
  };
67
80
  [k: string]: unknown;
68
81
  }
82
+ export type SessionProvider = () => Promise<Readonly<Session> | undefined>;
69
83
  export interface DownloadOptions {
70
84
  range?: {
71
85
  start: number;
@@ -89,12 +103,14 @@ export interface Bucket {
89
103
  valuePropId: string;
90
104
  fileCount: number;
91
105
  }
106
+ export type FileStatus = "inProgress" | "ready" | "expired" | "deletionInProgress";
92
107
  export type FileTree = {
93
108
  name: string;
94
109
  } & ({
95
110
  type: "file";
96
111
  sizeBytes: number;
97
112
  fileKey: string;
113
+ status: FileStatus;
98
114
  } | {
99
115
  type: "folder";
100
116
  children: FileTree[];
@@ -145,6 +161,7 @@ export interface FileInfo {
145
161
  size: number;
146
162
  isPublic: boolean;
147
163
  uploadedAt: Date;
164
+ status: FileStatus;
148
165
  }
149
166
  export type PaymentProviderType = "msp" | "bsp";
150
167
  export interface PaymentStreamInfo {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storagehub-sdk/msp-client",
3
- "version": "0.1.1",
3
+ "version": "0.3.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",
@@ -23,10 +23,12 @@
23
23
  "README.md"
24
24
  ],
25
25
  "peerDependencies": {
26
- "@storagehub-sdk/core": ">=0.0.5"
26
+ "@storagehub-sdk/core": ">=0.0.5",
27
+ "viem": ">=2.37.6"
27
28
  },
28
29
  "devDependencies": {
29
- "@storagehub-sdk/core": "0.1.1"
30
+ "viem": "2.37.6",
31
+ "@storagehub-sdk/core": "0.3.0"
30
32
  },
31
33
  "engines": {
32
34
  "node": ">=22"
@@ -1 +0,0 @@
1
- export {};