@storagehub-sdk/msp-client 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -6
- package/dist/MspClient.d.ts +2 -1
- package/dist/base.d.ts +4 -2
- package/dist/context.d.ts +0 -2
- package/dist/index.browser.js +1 -1
- package/dist/index.browser.js.map +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.node.js +1 -1
- package/dist/index.node.js.map +3 -3
- package/dist/modules/auth.d.ts +3 -6
- package/dist/types.d.ts +4 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -55,9 +55,11 @@ 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'
|
|
60
|
-
});
|
|
61
|
+
baseUrl: 'http://127.0.0.1:8080'
|
|
62
|
+
}, sessionProvider);
|
|
61
63
|
|
|
62
64
|
// 2. Check service health
|
|
63
65
|
const health = await client.info.getHealth();
|
|
@@ -69,7 +71,8 @@ import { createWalletClient, http } from 'viem';
|
|
|
69
71
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
70
72
|
const account = privateKeyToAccount('0x<your_dev_private_key>');
|
|
71
73
|
const wallet = createWalletClient({ account, transport: http('http://127.0.0.1:8545') });
|
|
72
|
-
await client.auth.SIWE(wallet);
|
|
74
|
+
const session = await client.auth.SIWE(wallet);
|
|
75
|
+
sessionRef = session;
|
|
73
76
|
|
|
74
77
|
// 4. Upload a file
|
|
75
78
|
const bucketId = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'; // StorageHub bucket identifier
|
|
@@ -117,17 +120,17 @@ console.log('Folder files:', folderFiles);
|
|
|
117
120
|
## API Reference
|
|
118
121
|
|
|
119
122
|
### Static Methods
|
|
120
|
-
- **`MspClient.connect(config)`** - Create and connect to MSP service
|
|
123
|
+
- **`MspClient.connect(config, sessionProvider)`** - Create and connect to MSP service
|
|
121
124
|
- `config.baseUrl: string` - MSP backend URL (e.g., `http://127.0.0.1:8080`)
|
|
122
125
|
- `config.timeoutMs?: number` - Request timeout in milliseconds
|
|
123
126
|
- `config.defaultHeaders?: Record<string, string>` - Default HTTP headers
|
|
124
127
|
- `config.fetchImpl?: typeof fetch` - Custom fetch implementation
|
|
128
|
+
- `sessionProvider: () => Promise<Session | undefined>` - Returns the current session (or undefined)
|
|
125
129
|
|
|
126
130
|
### Modules (instance properties)
|
|
127
131
|
- **`auth`**: SIWE auth and session helpers
|
|
128
|
-
- `SIWE(wallet, signal?)` – runs full SIWE flow and
|
|
132
|
+
- `SIWE(wallet, signal?)` – runs full SIWE flow and returns `Session`
|
|
129
133
|
- `getProfile(signal?)` – returns the authenticated user's profile
|
|
130
|
-
- `getAuthStatus()` – returns NotAuthenticated | TokenExpired | Authenticated
|
|
131
134
|
- **`info`**: MSP info and stats
|
|
132
135
|
- `getHealth(signal?)` – returns service health and status
|
|
133
136
|
- `getInfo(signal?)` – returns general MSP info (id, version, owner, endpoints)
|
package/dist/MspClient.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { BucketsModule } from "./modules/buckets.js";
|
|
|
4
4
|
import { ModuleBase } from "./base.js";
|
|
5
5
|
import { FilesModule } from "./modules/files.js";
|
|
6
6
|
import { InfoModule } from "./modules/info.js";
|
|
7
|
+
import type { SessionProvider } from "./types.js";
|
|
7
8
|
export declare class MspClient extends ModuleBase {
|
|
8
9
|
readonly config: HttpClientConfig;
|
|
9
10
|
private readonly context;
|
|
@@ -12,5 +13,5 @@ export declare class MspClient extends ModuleBase {
|
|
|
12
13
|
readonly files: FilesModule;
|
|
13
14
|
readonly info: InfoModule;
|
|
14
15
|
private constructor();
|
|
15
|
-
static connect(config: HttpClientConfig): Promise<MspClient>;
|
|
16
|
+
static connect(config: HttpClientConfig, sessionProvider: SessionProvider): Promise<MspClient>;
|
|
16
17
|
}
|
package/dist/base.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { MspClientContext } from "./context.js";
|
|
2
|
+
import type { SessionProvider } from "./types.js";
|
|
2
3
|
export declare abstract class ModuleBase {
|
|
3
4
|
protected readonly ctx: MspClientContext;
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
private readonly sessionProvider;
|
|
6
|
+
constructor(ctx: MspClientContext, sessionProvider: SessionProvider);
|
|
7
|
+
protected withAuth(headers?: Record<string, string>): Promise<Record<string, string> | undefined>;
|
|
6
8
|
/**
|
|
7
9
|
* Normalize a user-provided path for HTTP query usage.
|
|
8
10
|
* - Removes all leading '/' characters to avoid double slashes in URLs.
|
package/dist/context.d.ts
CHANGED
package/dist/index.browser.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{HttpClient as
|
|
1
|
+
import{HttpClient as M}from"@storagehub-sdk/core";import{getAddress as I}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 C=10,U=100,y=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=C,n){let s=t.account,r=typeof s=="string"?s:s?.address;if(!r||!s)throw new Error("Wallet client has no active account; set wallet.account before calling SIWE");let o=I(r),a=await t.getChainId(),{message:i}=await this.getNonce(o,a,n),l=await t.signMessage({account:s,message:i}),p;for(let h=0;h<e;h++)try{return await this.verify(i,l,n)}catch(d){p=d,await this.delay(U)}throw p instanceof Error?p:new Error("SIWE verification failed")}async delay(t){await new Promise(e=>setTimeout(e,t))}async getProfile(t){let e=await this.withAuth();return this.ctx.http.get("/auth/profile",{...e?{headers:e}:{},...t?{signal:t}:{}})}};var f=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 k,FileTrie as v,initWasm as S}from"@storagehub-sdk/core";var g=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 S();let a=`/buckets/${encodeURIComponent(t)}/upload/${encodeURIComponent(e)}`,i=await this.withAuth(),l=await this.coerceToFormPart(n),p=l.size,h=await this.computeFileFingerprint(l),d=await this.formFileMetadata(s,t,r,h,BigInt(p)),b=await this.computeFileKey(d),A=this.hexToBytes(e);if(b.length!==A.length||!b.every((P,F)=>P===A[F]))throw new Error(`Computed file key ${b.toString()} does not match provided file key ${A.toString()}`);let R=d.encode(),m=new FormData,B=new Blob([new Uint8Array(R)],{type:"application/octet-stream"});return m.append("file_metadata",B,"file_metadata"),m.append("file",l,"file"),await this.ctx.http.put(a,i?{body:m,headers:i}:{body:m})}async downloadFile(t,e){let n=`/download/${encodeURIComponent(t)}`,s={Accept:"*/*"};if(e?.range){let{start:o,end:a}=e.range,i=`bytes=${o}-${a??""}`;s.Range=i}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 a=o.headers.get("content-type"),i=o.headers.get("content-range"),l=o.headers.get("content-length"),p=l!==null?Number(l):void 0,h=typeof p=="number"&&Number.isFinite(p)?p:null;return{stream:o.body,status:o.status,contentType:a,contentRange:i,contentLength:h}}catch(o){if(o&&typeof o=="object"&&"status"in o&&typeof o.status=="number")return{stream:new ReadableStream({start(i){i.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:a,value:i}=await e.read();if(a)break;i&&(n.push(i),s+=i.length)}}finally{e.releaseLock()}let r=new Uint8Array(s),o=0;for(let a of n)r.set(a,o),o+=a.length;return new Blob([r],{type:"application/octet-stream"})}return new Blob([t],{type:"application/octet-stream"})}async computeFileFingerprint(t){let e=new v,n=new Uint8Array(await t.arrayBuffer()),s=1024,r=0;for(;r<n.length;){let o=Math.min(r+s,n.length),a=n.slice(r,o);e.push_chunk(a),r=o}return e.get_root()}async formFileMetadata(t,e,n,s,r){let o=this.hexToBytes(t),a=this.hexToBytes(e),i=new TextEncoder().encode(n);return await S(),new k(o,a,i,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 S(),t.getFileKey()}};var w=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 x=class u 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 y(this.context,n),this.buckets=new f(this.context,n),this.files=new g(this.context,n),this.info=new w(this.context,n)}static async connect(t,e){if(!t?.baseUrl)throw new Error("MspClient.connect: baseUrl is required");let n=new M({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 u(t,n,e)}};export{x as MspClient};
|
|
2
2
|
//# sourceMappingURL=index.browser.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/MspClient.ts", "../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\";\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(config: HttpClientConfig, http: HttpClient) {\n const context: MspClientContext = { config, http };\n super(context);\n this.config = config;\n this.context = context;\n this.auth = new AuthModule(this.context);\n this.buckets = new BucketsModule(this.context);\n this.files = new FilesModule(this.context);\n this.info = new InfoModule(this.context);\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", "import type { NonceResponse, Session, UserInfo, AuthStatus } from \"../types.js\";\nimport { AuthState } 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 this.ctx.session = session;\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<void> {\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 this.ctx.session = await this.verify(message, signature, signal);\n }\n\n /**\n * Fetch authenticated user's profile.\n * - Requires valid `session` (Authorization header added automatically).\n */\n getProfile(signal?: AbortSignal): Promise<UserInfo> {\n const headers = this.withAuth();\n return this.ctx.http.get<UserInfo>(\"/auth/profile\", {\n ...(headers ? { headers } : {}),\n ...(signal ? { signal } : {})\n });\n }\n\n /**\n * Determine auth status by checking token presence and profile reachability.\n */\n async getAuthStatus(): Promise<AuthStatus> {\n if (!this.ctx.session?.token) {\n return { status: AuthState.NotAuthenticated };\n }\n const profile = await this.getProfile().catch((err: any) =>\n err?.response?.status === 401 ? null : Promise.reject(err)\n );\n return profile ? { status: AuthState.Authenticated } : { status: AuthState.TokenExpired };\n }\n}\n", "import type { MspClientContext } from \"./context.js\";\n\nexport abstract class ModuleBase {\n protected readonly ctx: MspClientContext;\n\n constructor(ctx: MspClientContext) {\n this.ctx = ctx;\n }\n\n protected withAuth(headers?: Record<string, string>): Record<string, string> | undefined {\n const token = this.ctx.session?.token;\n return token ? { ...(headers ?? {}), Authorization: `Bearer ${token}` } : headers;\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 listBuckets(signal?: AbortSignal): Promise<Bucket[]> {\n const headers = 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 getBucket(bucketId: string, signal?: AbortSignal): Promise<Bucket> {\n const headers = 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 getFiles(bucketId: string, options?: GetFilesOptions): Promise<FileListResponse> {\n const headers = 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 getFileInfo(bucketId: string, fileKey: string, signal?: AbortSignal): 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.ctx.http\n .get<FileInfoWire>(path, {\n ...(headers ? { headers } : {}),\n ...(signal ? { signal } : {})\n })\n .then(\n (wire): FileInfo => ({\n ...wire,\n uploadedAt: new Date(wire.uploadedAt)\n })\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 = 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 const headers = this.withAuth(baseHeaders);\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 }\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 getPaymentStreams(signal?: AbortSignal): Promise<PaymentStreamsResponse> {\n const headers = this.withAuth();\n return this.ctx.http.get<PaymentStreamsResponse>(\"/payment_streams\", {\n ...(headers ? { headers } : {}),\n ...(signal ? { signal } : {})\n });\n }\n}\n"],
|
|
5
|
-
"mappings": "AACA,OAAS,cAAAA,MAAkB,
|
|
6
|
-
"names": ["HttpClient", "getAddress", "ModuleBase", "ctx", "headers", "token", "path", "_m", "offset", "AuthModule", "ModuleBase", "address", "chainId", "signal", "message", "signature", "
|
|
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\nconst DEFAULT_SIWE_VERIFY_RETRY_ATTEMPS = 10;\nconst DEFAULT_SIWE_VERIFY_BACKOFF_MS = 100;\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(\n wallet: WalletClient,\n retry = DEFAULT_SIWE_VERIFY_RETRY_ATTEMPS,\n signal?: AbortSignal\n ): 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 // TODO: remove the retry logic once the backend is fixed.\n let lastError: unknown;\n for (let attemptIndex = 0; attemptIndex < retry; attemptIndex++) {\n try {\n return await this.verify(message, signature, signal);\n } catch (err) {\n lastError = err;\n await this.delay(DEFAULT_SIWE_VERIFY_BACKOFF_MS);\n }\n }\n throw lastError instanceof Error ? lastError : new Error(\"SIWE verification failed\");\n }\n\n private async delay(ms: number): Promise<void> {\n await new Promise((resolve) => setTimeout(resolve, ms));\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": "AACA,OAAS,cAAAA,MAAkB,uBCA3B,OAAS,cAAAC,MAAqC,OCEvC,IAAeC,EAAf,KAA0B,CACZ,IACF,gBAEjB,YAAYC,EAAuBC,EAAkC,CACnE,KAAK,IAAMD,EACX,KAAK,gBAAkBC,CACzB,CAEA,MAAgB,SACdC,EAC6C,CAE7C,IAAMC,GADU,MAAM,KAAK,gBAAgB,IACpB,MACvB,OAAKA,EACED,EACH,CAAE,GAAGA,EAAS,cAAe,UAAUC,CAAK,EAAG,EAC/C,CAAE,cAAe,UAAUA,CAAK,EAAG,EAHpBD,CAIrB,CAcU,cAAcE,EAAsB,CAE5C,OAAOA,EAAK,QAAQ,eAAgB,CAACC,EAAIC,IAAoBA,IAAW,EAAI,GAAK,GAAI,CACvF,CACF,EDnCA,IAAMC,EAAoC,GACpCC,EAAiC,IAE1BC,EAAN,cAAyBC,CAAW,CAMjC,SAASC,EAAiBC,EAAiBC,EAA8C,CAC/F,OAAO,KAAK,IAAI,KAAK,KAAoB,cAAe,CACtD,KAAM,CAAE,QAAAF,EAAS,QAAAC,CAAQ,EACzB,QAAS,CAAE,eAAgB,kBAAmB,EAC9C,GAAIC,EAAS,CAAE,OAAAA,CAAO,EAAI,CAAC,CAC7B,CAAC,CACH,CAMA,MAAc,OAAOC,EAAiBC,EAAmBF,EAAwC,CAO/F,OANgB,MAAM,KAAK,IAAI,KAAK,KAAc,eAAgB,CAChE,KAAM,CAAE,QAAAC,EAAS,UAAAC,CAAU,EAC3B,QAAS,CAAE,eAAgB,kBAAmB,EAC9C,GAAIF,EAAS,CAAE,OAAAA,CAAO,EAAI,CAAC,CAC7B,CAAC,CAGH,CAMA,MAAM,KACJG,EACAC,EAAQV,EACRM,EACkB,CAIlB,IAAMK,EAAUF,EAAO,QACjBG,EAAkB,OAAOD,GAAY,SAAWA,EAAUA,GAAS,QACzE,GAAI,CAACC,GAAmB,CAACD,EACvB,MAAM,IAAI,MACR,6EACF,EAGF,IAAMP,EAAUS,EAAWD,CAAe,EACpCP,EAAU,MAAMI,EAAO,WAAW,EAClC,CAAE,QAAAF,CAAQ,EAAI,MAAM,KAAK,SAASH,EAASC,EAASC,CAAM,EAG1DE,EAAY,MAAMC,EAAO,YAAY,CAAE,QAAAE,EAAS,QAAAJ,CAAQ,CAAC,EAG3DO,EACJ,QAASC,EAAe,EAAGA,EAAeL,EAAOK,IAC/C,GAAI,CACF,OAAO,MAAM,KAAK,OAAOR,EAASC,EAAWF,CAAM,CACrD,OAASU,EAAK,CACZF,EAAYE,EACZ,MAAM,KAAK,MAAMf,CAA8B,CACjD,CAEF,MAAMa,aAAqB,MAAQA,EAAY,IAAI,MAAM,0BAA0B,CACrF,CAEA,MAAc,MAAMG,EAA2B,CAC7C,MAAM,IAAI,QAASC,GAAY,WAAWA,EAASD,CAAE,CAAC,CACxD,CAMA,MAAM,WAAWX,EAAyC,CACxD,IAAMa,EAAU,MAAM,KAAK,SAAS,EACpC,OAAO,KAAK,IAAI,KAAK,IAAc,gBAAiB,CAClD,GAAIA,EAAU,CAAE,QAAAA,CAAQ,EAAI,CAAC,EAC7B,GAAIb,EAAS,CAAE,OAAAA,CAAO,EAAI,CAAC,CAC7B,CAAC,CACH,CACF,EEvFO,IAAMc,EAAN,cAA4BC,CAAW,CAE5C,MAAM,YAAYC,EAAyC,CACzD,IAAMC,EAAU,MAAM,KAAK,SAAS,EACpC,OAAO,KAAK,IAAI,KAAK,IAAc,WAAY,CAC7C,GAAIA,EAAU,CAAE,QAAAA,CAAQ,EAAI,CAAC,EAC7B,GAAID,EAAS,CAAE,OAAAA,CAAO,EAAI,CAAC,CAC7B,CAAC,CACH,CAGA,MAAM,UAAUE,EAAkBF,EAAuC,CACvE,IAAMC,EAAU,MAAM,KAAK,SAAS,EAC9BE,EAAO,YAAY,mBAAmBD,CAAQ,CAAC,GACrD,OAAO,KAAK,IAAI,KAAK,IAAYC,EAAM,CACrC,GAAIF,EAAU,CAAE,QAAAA,CAAQ,EAAI,CAAC,EAC7B,GAAID,EAAS,CAAE,OAAAA,CAAO,EAAI,CAAC,CAC7B,CAAC,CACH,CAGA,MAAM,SAASE,EAAkBE,EAAsD,CACrF,IAAMH,EAAU,MAAM,KAAK,SAAS,EAC9BE,EAAO,YAAY,mBAAmBD,CAAQ,CAAC,SACrD,OAAO,KAAK,IAAI,KAAK,IAAsBC,EAAM,CAC/C,GAAIF,EAAU,CAAE,QAAAA,CAAQ,EAAI,CAAC,EAC7B,GAAIG,GAAS,OAAS,CAAE,OAAQA,EAAQ,MAAO,EAAI,CAAC,EACpD,GAAIA,GAAS,KAAO,CAAE,MAAO,CAAE,KAAM,KAAK,cAAcA,EAAQ,IAAI,CAAE,CAAE,EAAI,CAAC,CAC/E,CAAC,CACH,CACF,ECzBA,OAAS,gBAAAC,EAAc,YAAAC,EAAU,YAAAC,MAAgB,uBAE1C,IAAMC,EAAN,cAA0BC,CAAW,CAE1C,MAAM,YAAYC,EAAkBC,EAAiBC,EAAyC,CAC5F,IAAMC,EAAU,MAAM,KAAK,SAAS,EAC9BC,EAAO,YAAY,mBAAmBJ,CAAQ,CAAC,SAAS,mBAAmBC,CAAO,CAAC,GAEnFI,EAAO,MAAM,KAAK,IAAI,KAAK,IAAkBD,EAAM,CACvD,GAAID,EAAU,CAAE,QAAAA,CAAQ,EAAI,CAAC,EAC7B,GAAID,EAAS,CAAE,OAAAA,CAAO,EAAI,CAAC,CAC7B,CAAC,EACD,MAAO,CACL,GAAGG,EACH,WAAY,IAAI,KAAKA,EAAK,UAAU,CACtC,CACF,CAGA,MAAM,WACJL,EACAC,EACAK,EACAC,EACAC,EACAC,EACwB,CAGxB,MAAMZ,EAAS,EAEf,IAAMa,EAAc,YAAY,mBAAmBV,CAAQ,CAAC,WAAW,mBAAmBC,CAAO,CAAC,GAC5FU,EAAc,MAAM,KAAK,SAAS,EAGlCC,EAAW,MAAM,KAAK,iBAAiBN,CAAI,EAC3CO,EAAWD,EAAS,KAGpBE,EAAc,MAAM,KAAK,uBAAuBF,CAAQ,EAGxDG,EAAW,MAAM,KAAK,iBAC1BR,EACAP,EACAQ,EACAM,EACA,OAAOD,CAAQ,CACjB,EAGMG,EAAkB,MAAM,KAAK,eAAeD,CAAQ,EACpDE,EAAuB,KAAK,WAAWhB,CAAO,EACpD,GACEe,EAAgB,SAAWC,EAAqB,QAChD,CAACD,EAAgB,MAAM,CAACE,EAAMC,IAAUD,IAASD,EAAqBE,CAAK,CAAC,EAE5E,MAAM,IAAI,MACR,qBAAqBH,EAAgB,SAAS,CAAC,qCAAqCC,EAAqB,SAAS,CAAC,EACrH,EAIF,IAAMG,EAAkBL,EAAS,OAAO,EAGlCM,EAAO,IAAI,SACXC,EAAmB,IAAI,KAAK,CAAC,IAAI,WAAWF,CAAe,CAAC,EAAG,CACnE,KAAM,0BACR,CAAC,EACD,OAAAC,EAAK,OAAO,gBAAiBC,EAAkB,eAAe,EAC9DD,EAAK,OAAO,OAAQT,EAAU,MAAM,EAExB,MAAM,KAAK,IAAI,KAAK,IAC9BF,EACAC,EACI,CAAE,KAAMU,EAA6B,QAASV,CAAY,EAC1D,CAAE,KAAMU,CAA4B,CAC1C,CAEF,CAGA,MAAM,aAAapB,EAAiBsB,EAAoD,CACtF,IAAMnB,EAAO,aAAa,mBAAmBH,CAAO,CAAC,GAC/CuB,EAAsC,CAAE,OAAQ,KAAM,EAC5D,GAAID,GAAS,MAAO,CAClB,GAAM,CAAE,MAAAE,EAAO,IAAAC,CAAI,EAAIH,EAAQ,MACzBI,EAAa,SAASF,CAAK,IAAIC,GAAO,EAAE,GAC9CF,EAAY,MAAQG,CACtB,CAEA,IAAMxB,EAAU,MAAM,KAAK,SAASqB,CAAW,EAE/C,GAAI,CACF,IAAMI,EAAM,MAAM,KAAK,IAAI,KAAK,OAAOxB,EAAM,CAC3C,GAAID,EAAU,CAAE,QAAAA,CAAQ,EAAI,CAAC,EAC7B,GAAIoB,GAAS,OAAS,CAAE,OAAQA,EAAQ,MAAO,EAAI,CAAC,CACtD,CAAC,EAED,GAAI,CAACK,EAAI,KACP,MAAM,IAAI,MAAM,iDAAiD,EAGnE,IAAMC,EAAcD,EAAI,QAAQ,IAAI,cAAc,EAC5CE,EAAeF,EAAI,QAAQ,IAAI,eAAe,EAC9CG,EAAsBH,EAAI,QAAQ,IAAI,gBAAgB,EACtDI,EAAeD,IAAwB,KAAO,OAAOA,CAAmB,EAAI,OAC5EE,EACJ,OAAOD,GAAiB,UAAY,OAAO,SAASA,CAAY,EAAIA,EAAe,KAErF,MAAO,CACL,OAAQJ,EAAI,KACZ,OAAQA,EAAI,OACZ,YAAAC,EACA,aAAAC,EACA,cAAAG,CACF,CACF,OAASC,EAAO,CAEd,GACEA,GACA,OAAOA,GAAU,UACjB,WAAYA,GACZ,OAAOA,EAAM,QAAW,SASxB,MAAO,CACL,OAPkB,IAAI,eAA2B,CACjD,MAAMC,EAAY,CAChBA,EAAW,MAAM,CACnB,CACF,CAAC,EAIC,OAAQD,EAAM,OACd,YAAa,KACb,aAAc,KACd,cAAe,IACjB,EAGF,MAAMA,CACR,CACF,CAGA,MAAc,iBACZ5B,EACe,CACf,GAAI,OAAO,KAAS,KAAeA,aAAgB,KAAM,OAAOA,EAChE,GAAIA,aAAgB,WAAY,OAAO,IAAI,KAAK,CAACA,EAAK,MAAqB,CAAC,EAC5E,GAAI,OAAO,YAAgB,KAAeA,aAAgB,YAAa,OAAO,IAAI,KAAK,CAACA,CAAI,CAAC,EAG7F,GAAIA,aAAgB,eAAgB,CAClC,IAAM8B,EAAS9B,EAAK,UAAU,EACxB+B,EAAuB,CAAC,EAC1BC,EAAc,EAElB,GAAI,CACF,OAAa,CACX,GAAM,CAAE,KAAAC,EAAM,MAAAC,CAAM,EAAI,MAAMJ,EAAO,KAAK,EAC1C,GAAIG,EAAM,MACNC,IACFH,EAAO,KAAKG,CAAK,EACjBF,GAAeE,EAAM,OAEzB,CACF,QAAE,CACAJ,EAAO,YAAY,CACrB,CAGA,IAAMK,EAAW,IAAI,WAAWH,CAAW,EACvCI,EAAS,EACb,QAAWC,KAASN,EAClBI,EAAS,IAAIE,EAAOD,CAAM,EAC1BA,GAAUC,EAAM,OAGlB,OAAO,IAAI,KAAK,CAACF,CAAQ,EAAG,CAAE,KAAM,0BAA2B,CAAC,CAClE,CAEA,OAAO,IAAI,KAAK,CAACnC,CAAgB,EAAG,CAAE,KAAM,0BAA2B,CAAC,CAC1E,CAEA,MAAc,uBAAuBM,EAAqC,CACxE,IAAMgC,EAAO,IAAIhD,EACXiD,EAAY,IAAI,WAAW,MAAMjC,EAAS,YAAY,CAAC,EAGvDkC,EAAa,KACfJ,EAAS,EAEb,KAAOA,EAASG,EAAU,QAAQ,CAChC,IAAMnB,EAAM,KAAK,IAAIgB,EAASI,EAAYD,EAAU,MAAM,EACpDF,EAAQE,EAAU,MAAMH,EAAQhB,CAAG,EACzCkB,EAAK,WAAWD,CAAK,EACrBD,EAAShB,CACX,CAEA,OAAOkB,EAAK,SAAS,CACvB,CAEA,MAAc,iBACZrC,EACAP,EACAQ,EACAM,EACAiC,EACuB,CACvB,IAAMC,EAAa,KAAK,WAAWzC,CAAK,EAClC0C,EAAgB,KAAK,WAAWjD,CAAQ,EACxCkD,EAAgB,IAAI,YAAY,EAAE,OAAO1C,CAAQ,EACvD,aAAMX,EAAS,EACR,IAAIF,EAAaqD,EAAYC,EAAeC,EAAeH,EAAMjC,CAAW,CACrF,CAEQ,WAAWqC,EAAyB,CAC1C,GAAI,CAACA,EACH,MAAM,IAAI,MAAM,4BAA4B,EAG9C,IAAMC,EAAWD,EAAI,WAAW,IAAI,EAAIA,EAAI,MAAM,CAAC,EAAIA,EAEvD,GAAIC,EAAS,OAAS,IAAM,EAC1B,MAAM,IAAI,MAAM,mDAAmD,EAGrE,GAAI,CAAC,iBAAiB,KAAKA,CAAQ,EACjC,MAAM,IAAI,MAAM,wCAAwC,EAG1D,OAAO,IAAI,WAAWA,EAAS,MAAM,OAAO,GAAG,IAAKlC,GAAS,OAAO,SAASA,EAAM,EAAE,CAAC,GAAK,CAAC,CAAC,CAC/F,CAEA,MAAc,eAAemC,EAAiD,CAC5E,aAAMxD,EAAS,EACRwD,EAAa,WAAW,CACjC,CACF,EChPO,IAAMC,EAAN,cAAyBC,CAAW,CACzC,UAAUC,EAA6C,CACrD,OAAO,KAAK,IAAI,KAAK,IAAkB,UAAW,CAChD,GAAIA,EAAS,CAAE,OAAAA,CAAO,EAAI,CAAC,CAC7B,CAAC,CACH,CAGA,QAAQA,EAA6C,CACnD,OAAO,KAAK,IAAI,KAAK,IAAkB,QAAS,CAC9C,GAAIA,EAAS,CAAE,OAAAA,CAAO,EAAI,CAAC,CAC7B,CAAC,CACH,CAGA,SAASA,EAA8C,CACrD,OAAO,KAAK,IAAI,KAAK,IAAmB,SAAU,CAChD,GAAIA,EAAS,CAAE,OAAAA,CAAO,EAAI,CAAC,CAC7B,CAAC,CACH,CAGA,qBAAqBA,EAA4C,CAC/D,OAAO,KAAK,IAAI,KAAK,IAAiB,eAAgB,CACpD,GAAIA,EAAS,CAAE,OAAAA,CAAO,EAAI,CAAC,CAC7B,CAAC,CACH,CAGA,MAAM,kBAAkBA,EAAuD,CAC7E,IAAMC,EAAU,MAAM,KAAK,SAAS,EACpC,OAAO,KAAK,IAAI,KAAK,IAA4B,mBAAoB,CACnE,GAAIA,EAAU,CAAE,QAAAA,CAAQ,EAAI,CAAC,EAC7B,GAAID,EAAS,CAAE,OAAAA,CAAO,EAAI,CAAC,CAC7B,CAAC,CACH,CACF,ELnCO,IAAME,EAAN,MAAMC,UAAkBC,CAAW,CACxB,OACC,QACD,KACA,QACA,MACA,KAER,YACNC,EACAC,EACAC,EACA,CACA,IAAMC,EAA4B,CAAE,OAAAH,EAAQ,KAAAC,CAAK,EACjD,MAAME,EAASD,CAAe,EAC9B,KAAK,OAASF,EACd,KAAK,QAAUG,EACf,KAAK,KAAO,IAAIC,EAAW,KAAK,QAASF,CAAe,EACxD,KAAK,QAAU,IAAIG,EAAc,KAAK,QAASH,CAAe,EAC9D,KAAK,MAAQ,IAAII,EAAY,KAAK,QAASJ,CAAe,EAC1D,KAAK,KAAO,IAAIK,EAAW,KAAK,QAASL,CAAe,CAC1D,CAEA,aAAa,QACXF,EACAE,EACoB,CACpB,GAAI,CAACF,GAAQ,QAAS,MAAM,IAAI,MAAM,wCAAwC,EAE9E,IAAMC,EAAO,IAAIO,EAAW,CAC1B,QAASR,EAAO,QAChB,GAAIA,EAAO,YAAc,QAAa,CAAE,UAAWA,EAAO,SAAU,EACpE,GAAIA,EAAO,iBAAmB,QAAa,CACzC,eAAgBA,EAAO,cACzB,EACA,GAAIA,EAAO,YAAc,QAAa,CAAE,UAAWA,EAAO,SAAU,CACtE,CAAC,EAED,GAAI,CAACE,EAAiB,MAAM,IAAI,MAAM,gDAAgD,EACtF,OAAO,IAAIJ,EAAUE,EAAQC,EAAMC,CAAe,CACpD,CACF",
|
|
6
|
+
"names": ["HttpClient", "getAddress", "ModuleBase", "ctx", "sessionProvider", "headers", "token", "path", "_m", "offset", "DEFAULT_SIWE_VERIFY_RETRY_ATTEMPS", "DEFAULT_SIWE_VERIFY_BACKOFF_MS", "AuthModule", "ModuleBase", "address", "chainId", "signal", "message", "signature", "wallet", "retry", "account", "resolvedAddress", "getAddress", "lastError", "attemptIndex", "err", "ms", "resolve", "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, AuthState, AuthStatus, UserInfo, PaymentStreamInfo, PaymentStreamsResponse, StatsResponse, UploadOptions, UploadReceipt, ValueProp, Session } 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";
|
package/dist/index.node.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{HttpClient as
|
|
1
|
+
import{HttpClient as M}from"@storagehub-sdk/core";import{getAddress as I}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 C=10,U=100,y=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=C,n){let s=t.account,r=typeof s=="string"?s:s?.address;if(!r||!s)throw new Error("Wallet client has no active account; set wallet.account before calling SIWE");let o=I(r),a=await t.getChainId(),{message:i}=await this.getNonce(o,a,n),l=await t.signMessage({account:s,message:i}),p;for(let h=0;h<e;h++)try{return await this.verify(i,l,n)}catch(d){p=d,await this.delay(U)}throw p instanceof Error?p:new Error("SIWE verification failed")}async delay(t){await new Promise(e=>setTimeout(e,t))}async getProfile(t){let e=await this.withAuth();return this.ctx.http.get("/auth/profile",{...e?{headers:e}:{},...t?{signal:t}:{}})}};var f=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 k,FileTrie as v,initWasm as S}from"@storagehub-sdk/core";var g=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 S();let a=`/buckets/${encodeURIComponent(t)}/upload/${encodeURIComponent(e)}`,i=await this.withAuth(),l=await this.coerceToFormPart(n),p=l.size,h=await this.computeFileFingerprint(l),d=await this.formFileMetadata(s,t,r,h,BigInt(p)),b=await this.computeFileKey(d),A=this.hexToBytes(e);if(b.length!==A.length||!b.every((P,F)=>P===A[F]))throw new Error(`Computed file key ${b.toString()} does not match provided file key ${A.toString()}`);let R=d.encode(),m=new FormData,B=new Blob([new Uint8Array(R)],{type:"application/octet-stream"});return m.append("file_metadata",B,"file_metadata"),m.append("file",l,"file"),await this.ctx.http.put(a,i?{body:m,headers:i}:{body:m})}async downloadFile(t,e){let n=`/download/${encodeURIComponent(t)}`,s={Accept:"*/*"};if(e?.range){let{start:o,end:a}=e.range,i=`bytes=${o}-${a??""}`;s.Range=i}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 a=o.headers.get("content-type"),i=o.headers.get("content-range"),l=o.headers.get("content-length"),p=l!==null?Number(l):void 0,h=typeof p=="number"&&Number.isFinite(p)?p:null;return{stream:o.body,status:o.status,contentType:a,contentRange:i,contentLength:h}}catch(o){if(o&&typeof o=="object"&&"status"in o&&typeof o.status=="number")return{stream:new ReadableStream({start(i){i.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:a,value:i}=await e.read();if(a)break;i&&(n.push(i),s+=i.length)}}finally{e.releaseLock()}let r=new Uint8Array(s),o=0;for(let a of n)r.set(a,o),o+=a.length;return new Blob([r],{type:"application/octet-stream"})}return new Blob([t],{type:"application/octet-stream"})}async computeFileFingerprint(t){let e=new v,n=new Uint8Array(await t.arrayBuffer()),s=1024,r=0;for(;r<n.length;){let o=Math.min(r+s,n.length),a=n.slice(r,o);e.push_chunk(a),r=o}return e.get_root()}async formFileMetadata(t,e,n,s,r){let o=this.hexToBytes(t),a=this.hexToBytes(e),i=new TextEncoder().encode(n);return await S(),new k(o,a,i,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 S(),t.getFileKey()}};var w=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 x=class u 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 y(this.context,n),this.buckets=new f(this.context,n),this.files=new g(this.context,n),this.info=new w(this.context,n)}static async connect(t,e){if(!t?.baseUrl)throw new Error("MspClient.connect: baseUrl is required");let n=new M({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 u(t,n,e)}};export{x as MspClient};
|
|
2
2
|
//# sourceMappingURL=index.node.js.map
|
package/dist/index.node.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
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\";\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(config: HttpClientConfig, http: HttpClient) {\n const context: MspClientContext = { config, http };\n super(context);\n this.config = config;\n this.context = context;\n this.auth = new AuthModule(this.context);\n this.buckets = new BucketsModule(this.context);\n this.files = new FilesModule(this.context);\n this.info = new InfoModule(this.context);\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", "import type { NonceResponse, Session, UserInfo, AuthStatus } from \"../types.js\";\nimport { AuthState } 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 this.ctx.session = session;\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<void> {\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 this.ctx.session = await this.verify(message, signature, signal);\n }\n\n /**\n * Fetch authenticated user's profile.\n * - Requires valid `session` (Authorization header added automatically).\n */\n getProfile(signal?: AbortSignal): Promise<UserInfo> {\n const headers = this.withAuth();\n return this.ctx.http.get<UserInfo>(\"/auth/profile\", {\n ...(headers ? { headers } : {}),\n ...(signal ? { signal } : {})\n });\n }\n\n /**\n * Determine auth status by checking token presence and profile reachability.\n */\n async getAuthStatus(): Promise<AuthStatus> {\n if (!this.ctx.session?.token) {\n return { status: AuthState.NotAuthenticated };\n }\n const profile = await this.getProfile().catch((err: any) =>\n err?.response?.status === 401 ? null : Promise.reject(err)\n );\n return profile ? { status: AuthState.Authenticated } : { status: AuthState.TokenExpired };\n }\n}\n", "import type { MspClientContext } from \"./context.js\";\n\nexport abstract class ModuleBase {\n protected readonly ctx: MspClientContext;\n\n constructor(ctx: MspClientContext) {\n this.ctx = ctx;\n }\n\n protected withAuth(headers?: Record<string, string>): Record<string, string> | undefined {\n const token = this.ctx.session?.token;\n return token ? { ...(headers ?? {}), Authorization: `Bearer ${token}` } : headers;\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 listBuckets(signal?: AbortSignal): Promise<Bucket[]> {\n const headers = 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 getBucket(bucketId: string, signal?: AbortSignal): Promise<Bucket> {\n const headers = 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 getFiles(bucketId: string, options?: GetFilesOptions): Promise<FileListResponse> {\n const headers = 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 getFileInfo(bucketId: string, fileKey: string, signal?: AbortSignal): 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.ctx.http\n .get<FileInfoWire>(path, {\n ...(headers ? { headers } : {}),\n ...(signal ? { signal } : {})\n })\n .then(\n (wire): FileInfo => ({\n ...wire,\n uploadedAt: new Date(wire.uploadedAt)\n })\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 = 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 const headers = this.withAuth(baseHeaders);\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 }\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 getPaymentStreams(signal?: AbortSignal): Promise<PaymentStreamsResponse> {\n const headers = this.withAuth();\n return this.ctx.http.get<PaymentStreamsResponse>(\"/payment_streams\", {\n ...(headers ? { headers } : {}),\n ...(signal ? { signal } : {})\n });\n }\n}\n"],
|
|
5
|
-
"mappings": "AACA,OAAS,cAAAA,MAAkB,
|
|
6
|
-
"names": ["HttpClient", "getAddress", "ModuleBase", "ctx", "headers", "token", "path", "_m", "offset", "AuthModule", "ModuleBase", "address", "chainId", "signal", "message", "signature", "
|
|
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\nconst DEFAULT_SIWE_VERIFY_RETRY_ATTEMPS = 10;\nconst DEFAULT_SIWE_VERIFY_BACKOFF_MS = 100;\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(\n wallet: WalletClient,\n retry = DEFAULT_SIWE_VERIFY_RETRY_ATTEMPS,\n signal?: AbortSignal\n ): 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 // TODO: remove the retry logic once the backend is fixed.\n let lastError: unknown;\n for (let attemptIndex = 0; attemptIndex < retry; attemptIndex++) {\n try {\n return await this.verify(message, signature, signal);\n } catch (err) {\n lastError = err;\n await this.delay(DEFAULT_SIWE_VERIFY_BACKOFF_MS);\n }\n }\n throw lastError instanceof Error ? lastError : new Error(\"SIWE verification failed\");\n }\n\n private async delay(ms: number): Promise<void> {\n await new Promise((resolve) => setTimeout(resolve, ms));\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", "DEFAULT_SIWE_VERIFY_RETRY_ATTEMPS", "DEFAULT_SIWE_VERIFY_BACKOFF_MS", "AuthModule", "ModuleBase", "address", "chainId", "signal", "message", "signature", "wallet", "retry", "account", "resolvedAddress", "getAddress", "lastError", "attemptIndex", "err", "ms", "resolve", "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/modules/auth.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Session, UserInfo } from "../types.js";
|
|
2
2
|
import { type WalletClient } from "viem";
|
|
3
3
|
import { ModuleBase } from "../base.js";
|
|
4
4
|
export declare class AuthModule extends ModuleBase {
|
|
@@ -17,14 +17,11 @@ export declare class AuthModule extends ModuleBase {
|
|
|
17
17
|
* Full SIWE flow using a `WalletClient`.
|
|
18
18
|
* - Derives address, fetches nonce, signs message, verifies and stores session.
|
|
19
19
|
*/
|
|
20
|
-
SIWE(wallet: WalletClient, signal?: AbortSignal): Promise<
|
|
20
|
+
SIWE(wallet: WalletClient, retry?: number, signal?: AbortSignal): Promise<Session>;
|
|
21
|
+
private delay;
|
|
21
22
|
/**
|
|
22
23
|
* Fetch authenticated user's profile.
|
|
23
24
|
* - Requires valid `session` (Authorization header added automatically).
|
|
24
25
|
*/
|
|
25
26
|
getProfile(signal?: AbortSignal): Promise<UserInfo>;
|
|
26
|
-
/**
|
|
27
|
-
* Determine auth status by checking token presence and profile reachability.
|
|
28
|
-
*/
|
|
29
|
-
getAuthStatus(): Promise<AuthStatus>;
|
|
30
27
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -79,6 +79,7 @@ export interface Session {
|
|
|
79
79
|
};
|
|
80
80
|
[k: string]: unknown;
|
|
81
81
|
}
|
|
82
|
+
export type SessionProvider = () => Promise<Readonly<Session> | undefined>;
|
|
82
83
|
export interface DownloadOptions {
|
|
83
84
|
range?: {
|
|
84
85
|
start: number;
|
|
@@ -102,12 +103,14 @@ export interface Bucket {
|
|
|
102
103
|
valuePropId: string;
|
|
103
104
|
fileCount: number;
|
|
104
105
|
}
|
|
106
|
+
export type FileStatus = "inProgress" | "ready" | "expired" | "deletionInProgress";
|
|
105
107
|
export type FileTree = {
|
|
106
108
|
name: string;
|
|
107
109
|
} & ({
|
|
108
110
|
type: "file";
|
|
109
111
|
sizeBytes: number;
|
|
110
112
|
fileKey: string;
|
|
113
|
+
status: FileStatus;
|
|
111
114
|
} | {
|
|
112
115
|
type: "folder";
|
|
113
116
|
children: FileTree[];
|
|
@@ -158,6 +161,7 @@ export interface FileInfo {
|
|
|
158
161
|
size: number;
|
|
159
162
|
isPublic: boolean;
|
|
160
163
|
uploadedAt: Date;
|
|
164
|
+
status: FileStatus;
|
|
161
165
|
}
|
|
162
166
|
export type PaymentProviderType = "msp" | "bsp";
|
|
163
167
|
export interface PaymentStreamInfo {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storagehub-sdk/msp-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
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",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"viem": "2.37.6",
|
|
31
|
-
"@storagehub-sdk/core": "0.
|
|
31
|
+
"@storagehub-sdk/core": "0.3.1"
|
|
32
32
|
},
|
|
33
33
|
"engines": {
|
|
34
34
|
"node": ">=22"
|