{"version":3,"file":"client.mjs","names":["#fetch","#timeout","#onError","#request","error"],"sources":["../../src/upload-relay/client.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { ConnectionTimeoutError, StorageNodeAPIError } from '../storage-node/error.js';\nimport { UserAbortError } from '../storage-node/error.js';\nimport type { mergeHeaders } from '../storage-node/utils.js';\nimport type {\n\tEncodingType,\n\tUploadRelayTipConfig,\n\tProtocolMessageCertificate,\n\tWalrusClientRequestOptions,\n} from '../types.js';\nimport { fromUrlSafeBase64, urlSafeBase64 } from '../utils/index.js';\n\nexport type Fetch = (url: RequestInfo, init?: RequestInit) => Promise<Response>;\n\nexport type UploadRelayClientOptions = {\n\thost: string;\n\t/**\n\t * An optional custom fetch function.\n\t *\n\t * If not provided, defaults to the global `fetch` function (`globalThis.fetch`).\n\t *\n\t * @default globalThis.fetch\n\t */\n\tfetch?: Fetch;\n\n\t/**\n\t * An optional timeout for requests.\n\t * @default 30_000ms (30 seconds)\n\t */\n\ttimeout?: number;\n\n\t/**\n\t * Callback for individual network errors.\n\t */\n\tonError?: (error: Error) => void;\n};\n\nexport type RequestOptions = {\n\tpath: string;\n\ttimeout?: number;\n\theaders?: ReturnType<typeof mergeHeaders>;\n} & Omit<RequestInit, 'headers'>;\n\nexport type WriteBlobToUploadRelayOptions = {\n\tblobId: string;\n\tnonce: Uint8Array;\n\ttxDigest: string;\n\tblob: Uint8Array;\n\tblobObjectId: string;\n\tdeletable: boolean;\n\trequiresTip: boolean;\n\tencodingType?: EncodingType;\n} & WalrusClientRequestOptions;\n\nexport class UploadRelayClient {\n\thost: string;\n\t#fetch: Fetch;\n\t#timeout: number;\n\t#onError?: (error: Error) => void;\n\tconstructor({ host, fetch: overriddenFetch, timeout, onError }: UploadRelayClientOptions) {\n\t\tthis.host = host;\n\t\tthis.#fetch = overriddenFetch ?? globalThis.fetch;\n\t\tthis.#timeout = timeout ?? 30_000;\n\t\tthis.#onError = onError;\n\t}\n\n\tasync tipConfig(): Promise<UploadRelayTipConfig | null> {\n\t\tconst response = await this.#request({\n\t\t\tmethod: 'GET',\n\t\t\tpath: '/v1/tip-config',\n\t\t});\n\n\t\tconst data = (await response.json()) as\n\t\t\t| {\n\t\t\t\t\tsend_tip: {\n\t\t\t\t\t\taddress: string;\n\t\t\t\t\t\tkind:\n\t\t\t\t\t\t\t| {\n\t\t\t\t\t\t\t\t\tconst: number;\n\t\t\t\t\t\t\t  }\n\t\t\t\t\t\t\t| {\n\t\t\t\t\t\t\t\t\tlinear: {\n\t\t\t\t\t\t\t\t\t\tbase: number;\n\t\t\t\t\t\t\t\t\t\tencoded_size_mul_per_kib: number;\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t  };\n\t\t\t\t\t};\n\t\t\t  }\n\t\t\t| 'no_tip';\n\n\t\tif (typeof data === 'string') {\n\t\t\treturn null;\n\t\t}\n\n\t\tif ('const' in data.send_tip.kind) {\n\t\t\treturn {\n\t\t\t\taddress: data.send_tip.address,\n\t\t\t\tkind: {\n\t\t\t\t\tconst: data.send_tip.kind.const,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\taddress: data.send_tip.address,\n\t\t\tkind: {\n\t\t\t\tlinear: {\n\t\t\t\t\tbase: data.send_tip.kind.linear.base,\n\t\t\t\t\tperEncodedKib: data.send_tip.kind.linear.encoded_size_mul_per_kib,\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\n\tasync writeBlob({\n\t\tblobId,\n\t\tnonce,\n\t\ttxDigest,\n\t\tblob,\n\t\tdeletable,\n\t\tblobObjectId,\n\t\trequiresTip,\n\t\tencodingType,\n\t\t...options\n\t}: WriteBlobToUploadRelayOptions): Promise<{\n\t\tblobId: string;\n\t\tcertificate: ProtocolMessageCertificate;\n\t}> {\n\t\tconst query = new URLSearchParams({\n\t\t\tblob_id: blobId,\n\t\t});\n\n\t\tif (requiresTip) {\n\t\t\tquery.set('nonce', urlSafeBase64(nonce));\n\t\t\tquery.set('tx_id', txDigest);\n\t\t}\n\n\t\tif (deletable) {\n\t\t\tquery.set('deletable_blob_object', blobObjectId);\n\t\t}\n\n\t\tif (encodingType) {\n\t\t\tquery.set('encoding_type', encodingType);\n\t\t}\n\n\t\tconst response = await this.#request({\n\t\t\tmethod: 'POST',\n\t\t\tpath: `/v1/blob-upload-relay?${query.toString()}`,\n\t\t\tbody: blob as Uint8Array<ArrayBuffer>,\n\t\t\t...options,\n\t\t});\n\n\t\tconst data: {\n\t\t\tblob_id: number[];\n\t\t\tconfirmation_certificate: {\n\t\t\t\tsigners: number[];\n\t\t\t\tserialized_message: number[];\n\t\t\t\tsignature: string;\n\t\t\t};\n\t\t} = await response.json();\n\n\t\treturn {\n\t\t\tblobId,\n\t\t\tcertificate: {\n\t\t\t\tsigners: data.confirmation_certificate.signers,\n\t\t\t\tserializedMessage: new Uint8Array(data.confirmation_certificate.serialized_message),\n\t\t\t\tsignature: fromUrlSafeBase64(data.confirmation_certificate.signature),\n\t\t\t},\n\t\t};\n\t}\n\n\tasync #request(options: RequestOptions) {\n\t\tconst { signal, timeout, ...init } = options;\n\n\t\tif (signal?.aborted) {\n\t\t\tthrow new UserAbortError();\n\t\t}\n\n\t\tconst timeoutSignal = AbortSignal.timeout(timeout ?? this.#timeout);\n\n\t\tlet response: Response | undefined;\n\n\t\ttry {\n\t\t\tconst fetch = this.#fetch;\n\t\t\tresponse = await fetch(`${this.host}${options.path}`, {\n\t\t\t\t...init,\n\t\t\t\tsignal: signal ? AbortSignal.any([timeoutSignal, signal]) : timeoutSignal,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tif (signal?.aborted) {\n\t\t\t\tthrow new UserAbortError();\n\t\t\t}\n\n\t\t\tif (error instanceof Error && error.name === 'AbortError') {\n\t\t\t\tconst error = new ConnectionTimeoutError();\n\t\t\t\tthis.#onError?.(error);\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tthis.#onError?.(error as Error);\n\n\t\t\tthrow error;\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\tconst errorText = await response.text().catch((reason) => reason);\n\t\t\tconst errorJSON = safeParseJSON(errorText);\n\t\t\tconst errorMessage = errorJSON ? undefined : errorText;\n\t\t\tconst error = StorageNodeAPIError.generate(response.status, errorJSON, errorMessage);\n\t\t\tthis.#onError?.(error);\n\t\t\tthrow error;\n\t\t}\n\n\t\treturn response;\n\t}\n}\n\nfunction safeParseJSON(value: string) {\n\ttry {\n\t\treturn JSON.parse(value);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n"],"mappings":";;;;AAwDA,IAAa,oBAAb,MAA+B;CAE9B;CACA;CACA;CACA,YAAY,EAAE,MAAM,OAAO,iBAAiB,SAAS,WAAqC;AACzF,OAAK,OAAO;AACZ,QAAKA,QAAS,mBAAmB,WAAW;AAC5C,QAAKC,UAAW,WAAW;AAC3B,QAAKC,UAAW;;CAGjB,MAAM,YAAkD;EAMvD,MAAM,OAAQ,OALG,MAAM,MAAKC,QAAS;GACpC,QAAQ;GACR,MAAM;GACN,CAAC,EAE2B,MAAM;AAkBnC,MAAI,OAAO,SAAS,SACnB,QAAO;AAGR,MAAI,WAAW,KAAK,SAAS,KAC5B,QAAO;GACN,SAAS,KAAK,SAAS;GACvB,MAAM,EACL,OAAO,KAAK,SAAS,KAAK,OAC1B;GACD;AAGF,SAAO;GACN,SAAS,KAAK,SAAS;GACvB,MAAM,EACL,QAAQ;IACP,MAAM,KAAK,SAAS,KAAK,OAAO;IAChC,eAAe,KAAK,SAAS,KAAK,OAAO;IACzC,EACD;GACD;;CAGF,MAAM,UAAU,EACf,QACA,OACA,UACA,MACA,WACA,cACA,aACA,cACA,GAAG,WAID;EACF,MAAM,QAAQ,IAAI,gBAAgB,EACjC,SAAS,QACT,CAAC;AAEF,MAAI,aAAa;AAChB,SAAM,IAAI,SAAS,cAAc,MAAM,CAAC;AACxC,SAAM,IAAI,SAAS,SAAS;;AAG7B,MAAI,UACH,OAAM,IAAI,yBAAyB,aAAa;AAGjD,MAAI,aACH,OAAM,IAAI,iBAAiB,aAAa;EAUzC,MAAM,OAOF,OAda,MAAM,MAAKA,QAAS;GACpC,QAAQ;GACR,MAAM,yBAAyB,MAAM,UAAU;GAC/C,MAAM;GACN,GAAG;GACH,CAAC,EASiB,MAAM;AAEzB,SAAO;GACN;GACA,aAAa;IACZ,SAAS,KAAK,yBAAyB;IACvC,mBAAmB,IAAI,WAAW,KAAK,yBAAyB,mBAAmB;IACnF,WAAW,kBAAkB,KAAK,yBAAyB,UAAU;IACrE;GACD;;CAGF,OAAMA,QAAS,SAAyB;EACvC,MAAM,EAAE,QAAQ,SAAS,GAAG,SAAS;AAErC,MAAI,QAAQ,QACX,OAAM,IAAI,gBAAgB;EAG3B,MAAM,gBAAgB,YAAY,QAAQ,WAAW,MAAKF,QAAS;EAEnE,IAAI;AAEJ,MAAI;GACH,MAAM,QAAQ,MAAKD;AACnB,cAAW,MAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,QAAQ;IACrD,GAAG;IACH,QAAQ,SAAS,YAAY,IAAI,CAAC,eAAe,OAAO,CAAC,GAAG;IAC5D,CAAC;WACM,OAAO;AACf,OAAI,QAAQ,QACX,OAAM,IAAI,gBAAgB;AAG3B,OAAI,iBAAiB,SAAS,MAAM,SAAS,cAAc;IAC1D,MAAMI,UAAQ,IAAI,wBAAwB;AAC1C,UAAKF,UAAWE,QAAM;AACtB,UAAMA;;AAGP,SAAKF,UAAW,MAAe;AAE/B,SAAM;;AAGP,MAAI,CAAC,SAAS,IAAI;GACjB,MAAM,YAAY,MAAM,SAAS,MAAM,CAAC,OAAO,WAAW,OAAO;GACjE,MAAM,YAAY,cAAc,UAAU;GAC1C,MAAM,eAAe,YAAY,SAAY;GAC7C,MAAM,QAAQ,oBAAoB,SAAS,SAAS,QAAQ,WAAW,aAAa;AACpF,SAAKA,UAAW,MAAM;AACtB,SAAM;;AAGP,SAAO;;;AAIT,SAAS,cAAc,OAAe;AACrC,KAAI;AACH,SAAO,KAAK,MAAM,MAAM;SACjB;AACP"}