nft.storage
Version:
A client library for the https://nft.storage/ service. It provides a convenient interface for working with the HTTP API from a web browser or Node.js
419 lines (376 loc) • 12 kB
text/typescript
import type { CID } from 'multiformats'
export type { CID }
import type { BlockDecoder } from 'multiformats/block'
export type { BlockDecoder }
import type { CarReader } from '@ipld/car/api'
export type { CarReader }
/**
* Define nominal type of U based on type of T. Similar to Opaque types in Flow
*/
export type Tagged<T, Tag> = T & { tag?: Tag }
export interface Service {
endpoint: URL
token: string
did?: string
rateLimiter?: RateLimiter
}
export interface PublicService {
endpoint: URL
rateLimiter?: RateLimiter
}
export interface FileObject {
name: string
size: number
stream: () => AsyncIterable<any>
}
export type FilesSource =
| Iterable<File>
| Iterable<FileObject>
| AsyncIterable<File>
| AsyncIterable<FileObject>
/**
* CID in string representation
*/
export type CIDString = Tagged<string, CID>
export interface API {
/**
* Encodes the given token and all resources it references (in the form of a
* File or a Blob) along with a metadata JSON as specificed in ERC-1155 to a
* CAR file. The `token.image` must be either a `File` or a `Blob` instance,
* which will be stored and the corresponding content address URL will be
* saved in the metadata JSON file under `image` field.
*
* If `token.properties` contains properties with `File` or `Blob` values,
* those also get stored and their URLs will be saved in the metadata JSON
* file in their place.
*
* Note: URLs for `File` objects will retain file names e.g. in case of
* `new File([bytes], 'cat.png', { type: 'image/png' })` will be transformed
* into a URL that looks like `ipfs://bafy...hash/image/cat.png`. For `Blob`
* objects, the URL will not have a file name name or mime type, instead it
* will be transformed into a URL that looks like
* `ipfs://bafy...hash/image/blob`.
*/
encodeNFT<T extends TokenInput>(
input: T
): Promise<{ token: Token<T>; car: CarReader }>
/**
* Encodes a single file to a CAR file and also returns it's root CID.
*/
encodeBlob(
service: Service,
content: Blob | File
): Promise<{ cid: CID; car: CarReader }>
/**
* Encodes a directory of files to a CAR file and also returns the root CID.
* Provided files **MUST** be within the same directory, otherwise error is
* raised e.g. `foo/bar.png`, `foo/bla/baz.json` is ok but `foo/bar.png`,
* `bla/baz.json` is not.
*/
encodeDirectory(files: Iterable<File>): Promise<{ cid: CID; car: CarReader }>
/**
* Stores the given token and all resources it references (in the form of a
* File or a Blob) along with a metadata JSON as specificed in ERC-1155. The
* `token.image` must be either a `File` or a `Blob` instance, which will be
* stored and the corresponding content address URL will be saved in the
* metadata JSON file under `image` field.
*
* If `token.properties` contains properties with `File` or `Blob` values,
* those also get stored and their URLs will be saved in the metadata JSON
* file in their place.
*
* Note: URLs for `File` objects will retain file names e.g. in case of
* `new File([bytes], 'cat.png', { type: 'image/png' })` will be transformed
* into a URL that looks like `ipfs://bafy...hash/image/cat.png`. For `Blob`
* objects, the URL will not have a file name name or mime type, instead it
* will be transformed into a URL that looks like
* `ipfs://bafy...hash/image/blob`.
*/
store<T extends TokenInput>(service: Service, token: T, options?: RequestOptions): Promise<Token<T>>
/**
* Stores a single file and returns it's CID.
*/
storeBlob(service: Service, content: Blob | File, options?: RequestOptions): Promise<CIDString>
/**
* Stores a CAR file and returns it's root CID.
*/
storeCar(
service: Service,
content: Blob | CarReader,
options?: CarStorerOptions
): Promise<CIDString>
/**
* Stores a directory of files and returns a CID. Provided files **MUST**
* be within the same directory, otherwise error is raised e.g. `foo/bar.png`,
* `foo/bla/baz.json` is ok but `foo/bar.png`, `bla/baz.json` is not.
*/
storeDirectory(service: Service, files: FilesSource, options?: RequestOptions): Promise<CIDString>
/**
* Returns current status of the stored NFT by its CID. Note the NFT must
* have previously been stored by this account.
*/
status(service: Service, cid: string, options?: RequestOptions): Promise<StatusResult>
/**
* Removes stored content by its CID from this account. Please note that
* even if content is removed from the service other nodes that have
* replicated it might still continue providing it.
*/
delete(service: Service, cid: string, options?: RequestOptions): Promise<void>
/**
* Check if a CID of an NFT is being stored by NFT.Storage.
*/
check(service: PublicService, cid: string, options?: RequestOptions): Promise<CheckResult>
}
export interface RequestOptions {
/**
* A signal that can be used to abort the request.
*/
signal?: AbortSignal
}
export interface CarStorerOptions extends RequestOptions {
/**
* Callback called after each chunk of data has been uploaded. By default,
* data is split into chunks of around 10MB. It is passed the actual chunk
* size in bytes.
*/
onStoredChunk?: (size: number) => void
/**
* Maximum times to retry a failed upload. Default: 5
*/
maxRetries?: number
/**
* Maximum chunk size to upload in bytes. Default: 52,428,800
*/
maxChunkSize?: number
/**
* Additional IPLD block decoders. Used to interpret the data in the CAR
* file and split it into multiple chunks. Note these are only required if
* the CAR file was not encoded using the default encoders: `dag-pb`,
* `dag-cbor` and `raw`.
*/
decoders?: BlockDecoder<any, any>[]
}
export interface CheckResult {
cid: string
pin: { status: PinStatus }
deals: Deal[]
}
export interface StatusResult {
cid: string
size: number
deals: Deal[]
pin: Pin
created: Date
}
export type Deal =
| QueuedDeal
| PendingDeal
| FailedDeal
| PublishedDeal
| FinalizedDeal
export interface DealInfo {
lastChanged: Date
/**
* Miner ID
*/
miner: string
/**
* Filecoin network for this Deal
* TODO this needs to be removed
*/
network?: 'nerpanet' | 'mainnet'
/**
* Piece CID string
*/
pieceCid: CIDString
/**
* CID string
*/
batchRootCid: CIDString
}
export interface QueuedDeal {
status: 'queued'
/**
* Timestamp in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format: YYYY-MM-DDTHH:MM:SSZ.
*/
lastChanged: Date
}
export interface PendingDeal extends DealInfo {
status: 'proposing' | 'accepted'
}
export interface FailedDeal extends DealInfo {
status: 'failed'
/**
* Reason deal failed.
*/
statusText: string
}
export interface PublishedDeal extends DealInfo {
status: 'published'
/**
* Identifier for the deal stored on chain.
*/
chainDealID: number
}
export interface FinalizedDeal extends DealInfo {
status: 'active' | 'terminated'
/**
* Identifier for the deal stored on chain.
*/
chainDealID: number
/**
* Selector for extracting stored data from the batch root.
*/
datamodelSelector: string
/**
* Deal Activation
*/
dealActivation: Date
/**
* Deal Expiraction
*/
dealExpiration: Date
}
export interface Pin {
cid: CIDString
name?: string
status: PinStatus
created: Date
}
export type PinStatus = 'queued' | 'pinning' | 'pinned' | 'failed'
/**
* This is an input used to construct the Token metadata as per EIP-1155
* @see https://eips.ethereum.org/EIPS/eip-1155#metadata
*/
export interface TokenInput {
/**
* Identifies the asset to which this token represents
*/
name: string
/**
* Describes the asset to which this token represents
*/
description: string
/**
* A `File` with mime type `image/*` representing the asset this
* token represents. Consider creating images with width between `320` and
* `1080` pixels and aspect ratio between `1.91:1` and `4:5` inclusive.
*
* If a `File` object is used, the URL in the metadata will include a filename
* e.g. `ipfs://bafy...hash/cat.png`. If a `Blob` is used, the URL in the
* metadata will not include filename or extension e.g. `ipfs://bafy...img/`
*/
image: Blob | File
/**
* The number of decimal places that the token amount should display - e.g.
* `18`, means to divide the token amount by `1000000000000000000` to get its
* user representation.
*/
decimals?: number
/**
* Arbitrary properties. Values may be strings, numbers, nested objects or
* arrays of values. It is possible to provide `File` or `Blob` instances
* as property values, which will be stored on IPFS, and metadata will
* contain URLs to them in form of `ipfs://bafy...hash/name.png` or
* `ipfs://bafy...file/` respectively.
*/
properties?: Object
localization?: Localization
}
interface Localization {
/**
* The URI pattern to fetch localized data from. This URI should contain the
* substring `{locale}` which will be replaced with the appropriate locale
* value before sending the request.
*/
uri: string
/**
* The locale of the default data within the base JSON
*/
default: string
/**
* The list of locales for which data is available. These locales should
* conform to those defined in the Unicode Common Locale Data Repository
* (http://cldr.unicode.org/).
*/
locales: string[]
}
export interface Token<T extends TokenInput> {
/**
* CID for the token that encloses all of the files including metadata.json
* for the stored token.
*/
ipnft: CIDString
/**
* URL like `ipfs://bafy...hash/meta/data.json` for the stored token metadata.
*/
url: EncodedURL
/**
* Actual token data in ERC-1155 format. It matches data passed as `token`
* argument except Files/Blobs are substituted with corresponding `ipfs://`
* URLs.
*/
data: Encoded<T, [[Blob, URL]]>
/**
* Token data just like in `data` field except urls corresponding to
* Files/Blobs are substituted with IPFS gateway URLs so they can be
* embedded in browsers that do not support `ipfs://` protocol.
*/
embed(): Encoded<T, [[Blob, URL]]>
}
export type EncodedError = {
message: string
}
export type EncodedURL = Tagged<string, URL>
export type Result<X, T> = { ok: true; value: T } | { ok: false; error: X }
export interface EncodedToken<T extends TokenInput> {
ipnft: CIDString
url: EncodedURL
data: Encoded<T, [[Blob, EncodedURL]]>
}
export type StoreResponse<T extends TokenInput> = Result<
EncodedError,
EncodedToken<T>
>
/**
* Represents `T` encoded with a given `Format`.
* @example
* ```ts
* type Format = [
* [URL, { type: 'URL', href: string }]
* [CID, { type: 'CID', cid: string }]
* [Blob, { type: 'Blob', href: string }]
* ]
*
* type Response<T> = Encoded<T, Format>
* ```
*/
export type Encoded<T, Format extends Pattern<any, any>[]> = MatchRecord<
T,
Rule<Format[number]>
>
/**
* Format consists of multiple encoding defines what input type `I` maps to what output type `O`. It
* can be represented via function type or a [I, O] tuple.
*/
type Pattern<I, O> = ((input: I) => O) | [I, O]
export type MatchRecord<T, R extends Rule<any>> = {
[K in keyof T]: MatchRule<T[K], R> extends never // R extends {I: T[K], O: infer O} ? O : MatchRecord<T[K], R> //Match<T[K], R>
? MatchRecord<T[K], R>
: MatchRule<T[K], R>
}
type MatchRule<T, R extends Rule<any>> = R extends (input: T) => infer O
? O
: never
type Rule<Format extends Pattern<any, any>> = Format extends [infer I, infer O]
? (input: I) => O
: Format extends (input: infer I) => infer O
? (input: I) => O
: never
/**
* RateLimiter returns a promise that resolves when it is safe to send a request
* that does not exceed the rate limit.
*/
export interface RateLimiter {
(): Promise<void>
}