├── .vscode └── settings.json ├── README.md ├── _vendor ├── ipfs-core-types.bitswap.d.ts └── ipfs-utils.types.d.ts ├── bitswap └── stat.ts ├── deno.json ├── deno.lock ├── deps.ts ├── lib ├── configure.ts ├── core.ts └── to-url-search-params.ts ├── mod.ts └── types.d.ts /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ipfs (W.I.P.) 2 | 3 | Since ipfs-http-client got deprecated, I'll turn it into a general ipfs client soon. 4 | -------------------------------------------------------------------------------- /_vendor/ipfs-core-types.bitswap.d.ts: -------------------------------------------------------------------------------- 1 | import type { CID } from "https://esm.sh/multiformats@11.0.2/cid" 2 | import type { PeerId } from "https://esm.sh/@libp2p/interface-peer-id@2.0.1" 3 | import type { AbortOptions } from "../types.d.ts"; 4 | 5 | 6 | export interface API { 7 | /** 8 | * Returns the wantlist for your node 9 | * 10 | * @example 11 | * ```js 12 | * const list = await ipfs.bitswap.wantlist() 13 | * console.log(list) 14 | * // [ CID('QmHash') ] 15 | * ``` 16 | */ 17 | wantlist: (options?: AbortOptions & OptionExtension) => Promise 18 | /** 19 | * Returns the wantlist for a connected peer 20 | * 21 | * @example 22 | * ```js 23 | * const list = await ipfs.bitswap.wantlistForPeer(peerId) 24 | * console.log(list) 25 | * // [ CID('QmHash') ] 26 | * ``` 27 | */ 28 | wantlistForPeer: (peerId: PeerId, options?: AbortOptions & OptionExtension) => Promise 29 | /** 30 | * Removes one or more CIDs from the wantlist 31 | * 32 | * @example 33 | * ```JavaScript 34 | * let list = await ipfs.bitswap.wantlist() 35 | * console.log(list) 36 | * // [ CID('QmHash') ] 37 | * 38 | * await ipfs.bitswap.unwant(cid) 39 | * 40 | * list = await ipfs.bitswap.wantlist() 41 | * console.log(list) 42 | * // [] 43 | * ``` 44 | */ 45 | unwant: (cids: CID | CID[], options?: AbortOptions & OptionExtension) => Promise 46 | /** 47 | * Show diagnostic information on the bitswap agent. 48 | * Note: `bitswap.stat` and `stats.bitswap` can be used interchangeably. 49 | * 50 | * @example 51 | * ```js 52 | * const stats = await ipfs.bitswap.stat() 53 | * console.log(stats) 54 | * ``` 55 | */ 56 | stat: (options?: AbortOptions & OptionExtension) => Promise 57 | } 58 | export interface Stats { 59 | provideBufLen: number 60 | wantlist: CID[] 61 | peers: PeerId[] 62 | blocksReceived: bigint 63 | dataReceived: bigint 64 | blocksSent: bigint 65 | dataSent: bigint 66 | dupBlksReceived: bigint 67 | dupDataReceived: bigint 68 | } -------------------------------------------------------------------------------- /_vendor/ipfs-utils.types.d.ts: -------------------------------------------------------------------------------- 1 | import type { MtimeLike } from 'https://esm.sh/ipfs-unixfs@11.0.0/dist/src/index.d.ts' 2 | interface ProgressStatus { 3 | total: number 4 | loaded: number 5 | lengthComputable: boolean 6 | } 7 | export interface ProgressFn { 8 | (status: ProgressStatus): void 9 | } 10 | type Override = Omit & R 11 | export type FetchOptions = Override 35 | export interface HTTPOptions extends FetchOptions { 36 | json?: any 37 | /** 38 | * The base URL to use in case url is a relative URL 39 | */ 40 | base?: string 41 | /** 42 | * Throw not ok responses as Errors 43 | */ 44 | throwHttpErrors?: boolean 45 | /** 46 | * Transform search params 47 | */ 48 | transformSearchParams?: (params: URLSearchParams) => URLSearchParams 49 | /** 50 | * When iterating the response body, transform each chunk with this function. 51 | */ 52 | transform?: (chunk: any) => any 53 | /** 54 | * Handle errors 55 | */ 56 | handleError?: (rsp: Response) => Promise 57 | } 58 | export interface ExtendedResponse extends Response { 59 | iterator: () => AsyncGenerator 60 | ndjson: () => AsyncGenerator 61 | } 62 | export interface GlobSourceOptions { 63 | /** 64 | * Include .dot files in matched paths 65 | */ 66 | hidden?: boolean 67 | /** 68 | * follow symlinks 69 | */ 70 | followSymlinks?: boolean 71 | /** 72 | * Preserve mode 73 | */ 74 | preserveMode?: boolean 75 | /** 76 | * Preserve mtime 77 | */ 78 | preserveMtime?: boolean 79 | /** 80 | * mode to use - if preserveMode is true this will be ignored 81 | */ 82 | mode?: number 83 | /** 84 | * mtime to use - if preserveMtime is true this will be ignored 85 | */ 86 | mtime?: MtimeLike 87 | } 88 | export interface GlobSourceResult { 89 | path: string 90 | content: AsyncIterable | undefined 91 | mode: number | undefined 92 | mtime: MtimeLike | undefined 93 | } 94 | -------------------------------------------------------------------------------- /bitswap/stat.ts: -------------------------------------------------------------------------------- 1 | import { CID } from "https://esm.sh/multiformats@11.0.2/cid" 2 | import { configure } from '../lib/configure.ts' 3 | import { toUrlSearchParams } from '../lib/to-url-search-params.ts' 4 | import { peerIdFromString } from "https://esm.sh/@libp2p/peer-id@2.0.3" 5 | import type { API as BitswapAPI } from "../_vendor/ipfs-core-types.bitswap.d.ts" 6 | import type { HTTPClientExtraOptions } from "../types.d.ts"; 7 | 8 | 9 | 10 | export const createStat = configure(api => { 11 | const stat: BitswapAPI['stat'] = async (options = {}) => { 12 | const res = await api.post('bitswap/stat', { 13 | searchParams: toUrlSearchParams(options), 14 | signal: options.signal, 15 | headers: options.headers 16 | }) 17 | 18 | return toCoreInterface(await res.json()) 19 | } 20 | return stat 21 | }) 22 | 23 | function toCoreInterface (res: any) { 24 | return { 25 | provideBufLen: res.ProvideBufLen, 26 | wantlist: (res.Wantlist || []).map((k: { '/': string }) => CID.parse(k['/'])), 27 | peers: (res.Peers || []).map((str: string) => peerIdFromString(str)), 28 | blocksReceived: BigInt(res.BlocksReceived), 29 | dataReceived: BigInt(res.DataReceived), 30 | blocksSent: BigInt(res.BlocksSent), 31 | dataSent: BigInt(res.DataSent), 32 | dupBlksReceived: BigInt(res.DupBlksReceived), 33 | dupDataReceived: BigInt(res.DupDataReceived) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "fmt": { 3 | "options": { 4 | "useTabs": false, 5 | "lineWidth": 120, 6 | "indentWidth": 2, 7 | "singleQuote": true, 8 | "semiColons": false 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /deno.lock: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2", 3 | "remote": { 4 | "https://esm.sh/@ipld/dag-cbor@9.0.0": "32ef443f47acd32723bd35c4687427ba6808c0eba225f966e0f3699ad67be45b", 5 | "https://esm.sh/@ipld/dag-json@10.0.1": "e352cef3eeb169a132f56394e8a31de4a7cf9408b84855004e8e98302cf66f34", 6 | "https://esm.sh/@ipld/dag-pb@4.0.2": "780d8652665b67626dcc8c9de39cc2360d8a6746782976af851459a339284ef3", 7 | "https://esm.sh/@multiformats/multiaddr@12.1.0": "7900c525592e90a9efbbc50554dd166c3998f8c556f257965c95557184add558", 8 | "https://esm.sh/dag-jose@4.0.0": "3ed2bf60f4e381bab822742dff5c632da854747a698804da1cec9bac0a2a823f", 9 | "https://esm.sh/ipfs-core-utils@0.18.0/multibases": "a4c4a055c417d33eacea44bba49bd4ed86206bacee935deb91b0234707d83262", 10 | "https://esm.sh/ipfs-core-utils@0.18.0/multicodecs": "f64c64eb1f28e88395c0036f94fc7def75966f852d328492731c147a1b8b7314", 11 | "https://esm.sh/ipfs-core-utils@0.18.0/multihashes": "e439db5445cad4e0ee91cafadb3c5363daf2b3fca3c995083114b18764048023", 12 | "https://esm.sh/ipfs-http-client@60.0.0/dist/src/lib/mode-to-string.d.ts": "f5b4ea617a868db5b189d154a5a7098984243c15583432c532d40fe42f6ef28e", 13 | "https://esm.sh/ipfs-http-client@60.0.0/src/lib/mode-to-string.js": "82fb38fa8682cf9ab3f2142ff77722095a693c53c69a925d75194599bcdfac95", 14 | "https://esm.sh/ipfs-http-client@60.0.0/src/lib/parse-mtime.js": "28fc090f839217923ec9243a4998aceaa6e401bd9a1072a1a8a0e2116b9aa100", 15 | "https://esm.sh/ipfs-http-client@60.0.0/src/types.ts": "5bbaf4e028a7f254bf3e72d9422c0a44f152525aed481b66ca4d59bd8c63dc76", 16 | "https://esm.sh/ipfs-utils@9.0.14/src/files/glob-source.js": "6f4e42001fb4d72839e4a7bc58bc693e57dcb144470afae5b24f0a447b7c3d31", 17 | "https://esm.sh/multiformats@11.0.2/bases/interface": "5d861d072456a6f80aea29aeed1637dc6519943db8558380d7498891ceae05db", 18 | "https://esm.sh/multiformats@11.0.2/basics": "bd9aaccb27ac6138f652340fa4ca8f490742b65386320a1a8b8a4a7b1331f3b6", 19 | "https://esm.sh/multiformats@11.0.2/codecs/interface": "fd2a7a23b4bc32cc207fafe9b9501b72e58e459233630f4d6d8a66fa2a41c10b", 20 | "https://esm.sh/multiformats@11.0.2/hashes/identity": "e3ba551e3fccacb7f9caa1790d432e671728e4334b1bc77bd21e93b8ba9f95a7", 21 | "https://esm.sh/multiformats@11.0.2/hashes/interface": "7c244882f740e8228476fc7d87b91fbe4bd8305c9a3d6681f7b208d5daca9829", 22 | "https://esm.sh/v112/@chainsafe/is-ip@2.0.1/deno/is-ip.mjs": "dbefcd0a93685cda859f72ba78ef9d91f16f0a324b998880c3e118c3ead67e10", 23 | "https://esm.sh/v112/@chainsafe/is-ip@2.0.1/deno/parse.js": "714edfab3990b95af4cf265579b23404dcd8fa75c5877535e2fbbc47adce7990", 24 | "https://esm.sh/v112/@chainsafe/netmask@2.0.0/deno/netmask.mjs": "534cea06c92270321f288afaf2bfd578be75d0e85a733161b744bb9a0b31f938", 25 | "https://esm.sh/v112/@ipld/dag-cbor@9.0.0/deno/dag-cbor.mjs": "618b0cad7ba49fea3b922bc0c4444ce6abd7548f8600b710cdd1cfdd42b8483b", 26 | "https://esm.sh/v112/@ipld/dag-cbor@9.0.0/dist/src/index.d.ts": "5b10039ee2ab43639a957a4532841a99a5330bed0a7bdadf3f778f7f01baf048", 27 | "https://esm.sh/v112/@ipld/dag-json@10.0.1/deno/dag-json.mjs": "28997514ba330aaf077453c64bec0cd7b98c9d6300b04f533f652b5b7c8b39a3", 28 | "https://esm.sh/v112/@ipld/dag-json@10.0.1/dist/src/index.d.ts": "61c11ab20b2e72b80055b4870aab9d328420657a9ab8837eb6674e6deca50f2c", 29 | "https://esm.sh/v112/@ipld/dag-pb@4.0.2/deno/dag-pb.mjs": "0cdd620b83a6748542f6879c547a1ea3d2d9b1f33f3f56006dd5b9421e385d33", 30 | "https://esm.sh/v112/@ipld/dag-pb@4.0.2/dist/src/index.d.ts": "46dfb6e1427df0f6ffe363fe48a2462f67788ab1a4e856d2b440bdea8ab5ad90", 31 | "https://esm.sh/v112/@ipld/dag-pb@4.0.2/dist/src/interface.d.ts": "83fd411c723e1a4d5b8ef84c4c80392b6a3a63b72cbd8bc04e94ac36706fc1dc", 32 | "https://esm.sh/v112/@ipld/dag-pb@4.0.2/dist/src/util.d.ts": "83cfff5daca722a2d3ad5af54acac4135060e3782902074b77972c09e7c583ee", 33 | "https://esm.sh/v112/@multiformats/multiaddr@12.1.0/deno/multiaddr.mjs": "3036d9a5e2834465032f473c279408f38e30621025128e344122458e7145d28e", 34 | "https://esm.sh/v112/@multiformats/multiaddr@12.1.0/dist/src/filter/multiaddr-filter.d.ts": "83d9efffd3a90ecb1eb9d6bca82afc06df8b43f03782c23f63daaf7db020f1d3", 35 | "https://esm.sh/v112/@multiformats/multiaddr@12.1.0/dist/src/index.d.ts": "1aeb6c24e4bc99de10e1950a8f1ff7285b80e39781edf8527070073049f431c2", 36 | "https://esm.sh/v112/@multiformats/multiaddr@12.1.0/dist/src/protocols-table.d.ts": "4c73cbb637d9b060207c7784d2a73bb9bd1f88d4ece1f62dbe2b4da22fa08685", 37 | "https://esm.sh/v112/cborg@1.10.1/deno/cborg.mjs": "0f4824896f14c983fa1f527a1bc69f7561b9fde50a22655be7a5ae6a82cda1f8", 38 | "https://esm.sh/v112/cborg@1.10.1/deno/json.js": "1179669b8b183f86ed20d6f907d7640679acf8439aa2986137d7d20c4a66f855", 39 | "https://esm.sh/v112/dag-jose@4.0.0/deno/dag-jose.mjs": "231a1cd46ec128c90d05a4e134edcee3ecd762f398b7aa054c90eeeaa00f3e89", 40 | "https://esm.sh/v112/dag-jose@4.0.0/lib/encryption.d.ts": "f3693b3f321502def019d8c21bcc42d7f73b439ce826897bd56154827855378b", 41 | "https://esm.sh/v112/dag-jose@4.0.0/lib/index.d.ts": "351bd269095e46302ebe63fee0523b1e83b955051d8af7ba6e9e02ae592f55d9", 42 | "https://esm.sh/v112/dag-jose@4.0.0/lib/signing.d.ts": "21bc525e44b2e907ebbcbf7811c55345957f949b7cb0256fc8ed3aabe785256e", 43 | "https://esm.sh/v112/err-code@3.0.1/deno/err-code.mjs": "62e69b386fad59aac6e9fbbb308809825a9eec3772f1219008176a33dc8c3b73", 44 | "https://esm.sh/v112/ipfs-core-utils@0.18.0/deno/multibases.js": "6893b7da2fa719aa9e3ab19f3864eb9aa3cc0bcbc15a33dc89dec5479d41245b", 45 | "https://esm.sh/v112/ipfs-core-utils@0.18.0/deno/multicodecs.js": "2fd46f240c3f402e63ba39d228e824f8ba2897f03a5c88d44f96bce11c20025d", 46 | "https://esm.sh/v112/ipfs-core-utils@0.18.0/deno/multihashes.js": "fbbd823f9771e202aecc2a1d70fb90b6a2137d9720ed32d1d085d42dfa0f79a5", 47 | "https://esm.sh/v112/ipfs-http-client@60.0.0/deno/src/lib/mode-to-string.js": "03e2742a877f287d6c9837775e7042f6c948836802bbb8b3292d07e5ffc11f5e", 48 | "https://esm.sh/v112/ipfs-http-client@60.0.0/deno/src/lib/parse-mtime.js": "ff6ece34f38c4df8375ade407595eab8a9ac1f6d9bba71a1e7aa7223f29adc9f", 49 | "https://esm.sh/v112/ipfs-utils@9.0.14/deno/src/files/glob-source.js": "fda8d953ff6e766dc7722b178508721d80ac56407186a6bc5cb2e556571c6ca6", 50 | "https://esm.sh/v112/multiformats@11.0.2/deno/bases/base32.js": "828a0787538d6aaa6b9be3696b3e208e808f5abd666fd24a5ff3b249178c954e", 51 | "https://esm.sh/v112/multiformats@11.0.2/deno/bases/base58.js": "67fa6b4f2f9cdf7c14cf3975d96bfdd8f280fef5b837c17c7513da3f7e13910a", 52 | "https://esm.sh/v112/multiformats@11.0.2/deno/bases/base64.js": "95101acc66bdb2f44bf9217b9a826aa3a705c949ef8539c8128ca52debe4ed07", 53 | "https://esm.sh/v112/multiformats@11.0.2/deno/bases/interface.js": "219cb3085118cdc2787067b34233121ff58d792de291d3af06d66304ca753eb8", 54 | "https://esm.sh/v112/multiformats@11.0.2/deno/basics.js": "773c6f381a31cd5a10d67411c40508997de56429512a436930ad9cf63efaefd6", 55 | "https://esm.sh/v112/multiformats@11.0.2/deno/cid.js": "de092c30a9c789354e3281852089a851d2d094b3015d757ec7f65075b4f79df2", 56 | "https://esm.sh/v112/multiformats@11.0.2/deno/codecs/interface.js": "92be2650969feff20ebc5f9a7c64f863a39ad51abac0325467810f0f54d181ec", 57 | "https://esm.sh/v112/multiformats@11.0.2/deno/hashes/digest.js": "95c76d6c855047cb04c567cf20704d219b87d84b2409c46547c3e6c9156702e9", 58 | "https://esm.sh/v112/multiformats@11.0.2/deno/hashes/identity.js": "b883813ffbf9f43d6c3a5dfb885002ff165f23982f907c6686a7b1f954d2444e", 59 | "https://esm.sh/v112/multiformats@11.0.2/deno/hashes/interface.js": "64cc7a364d1514deb2f407a22b211294275acf8d533bfaafa2b50d9da877a161", 60 | "https://esm.sh/v112/multiformats@11.0.2/deno/multiformats.mjs": "d48258538eb8c4eb6432e23c9580a9aac64618ba5f77c69b554993ed073736aa", 61 | "https://esm.sh/v112/multiformats@11.0.2/dist/types/src/bases/base.d.ts": "ffc557e7c5a76df4842fabca797d748c502e3407d6eac0393d0c4c8bece744a7", 62 | "https://esm.sh/v112/multiformats@11.0.2/dist/types/src/bases/interface.d.ts": "963c8941e7ee7da789bc4e1725c31dd35b0656c4d7dab4180b1f1ce2ceedf9e5", 63 | "https://esm.sh/v112/multiformats@11.0.2/dist/types/src/basics.d.ts": "af1c4d027ae5e319a1ae7b23e302b1c501bd03c8fd6dc76c80f522c9d7085f53", 64 | "https://esm.sh/v112/multiformats@11.0.2/dist/types/src/block/interface.d.ts": "64c7594a582d8a8f84dbec709cca6f642bca2ff969d70a394c367467dd707d11", 65 | "https://esm.sh/v112/multiformats@11.0.2/dist/types/src/bytes.d.ts": "99bfb6b5870f9f098194d6bfb6cb168f95d768d71069aa368a993aa9f01fba16", 66 | "https://esm.sh/v112/multiformats@11.0.2/dist/types/src/cid.d.ts": "0ae355421e2a5f2f534e570b222dcb8d06fca94ca62c0e9b7fd2c8850f13ab50", 67 | "https://esm.sh/v112/multiformats@11.0.2/dist/types/src/codecs/interface.d.ts": "61ecd42a22f9684e29b13b1b0c1ad9ac03dec11b090c42df364e8fb703bda033", 68 | "https://esm.sh/v112/multiformats@11.0.2/dist/types/src/codecs/json.d.ts": "e05b405095ef50be6d6bf544dc7868cf532b9269e2fd1ecb36fd5332b891757a", 69 | "https://esm.sh/v112/multiformats@11.0.2/dist/types/src/codecs/raw.d.ts": "33060f6555fdaef32ba2116a9eb1f181f7939020aeaaed53500d933806447e79", 70 | "https://esm.sh/v112/multiformats@11.0.2/dist/types/src/hashes/digest.d.ts": "66a09c8d8f87499b6c88bc6ce8e473d1b0b85bf333c62a3aab2a8d0310e1a8a3", 71 | "https://esm.sh/v112/multiformats@11.0.2/dist/types/src/hashes/hasher.d.ts": "2af22de699998f27869aad6e74d99561e663c00cc91990125d94633011cbc4cc", 72 | "https://esm.sh/v112/multiformats@11.0.2/dist/types/src/hashes/identity.d.ts": "55d3c93fc89afef02ca1c01ebefc7f77b2c3ceb516bb44c15605b0c4872e730f", 73 | "https://esm.sh/v112/multiformats@11.0.2/dist/types/src/hashes/interface.d.ts": "2489793504b2dd1fabcfb94e51dcd94d54b7d8253e85b003863c90e7b5ae763f", 74 | "https://esm.sh/v112/multiformats@11.0.2/dist/types/src/index.d.ts": "3763c42154cdd740aa1b92c6241f7c75b7e282a50adbd9f6274ff5be93f26506", 75 | "https://esm.sh/v112/multiformats@11.0.2/dist/types/src/interface.d.ts": "68ec2cae2a7fbbcc36f8a15c0a29f422cba7daca9e916e08e1c37ea94af78e5d", 76 | "https://esm.sh/v112/multiformats@11.0.2/dist/types/src/link/interface.d.ts": "622ab5c916dddd804c5b5342a2bcfa32671f8614cb731a437a1c274007a6e7b5", 77 | "https://esm.sh/v112/multiformats@11.0.2/dist/types/src/varint.d.ts": "f5954134730b0548f8e59d99bae7837bf925be391e24b406699423b4c0dccd6d", 78 | "https://esm.sh/v112/uint8arrays@4.0.3/deno/concat.js": "3e827b82f0215a42091105e76233055d72f8cec59a38f0e56d3033f024e1617e", 79 | "https://esm.sh/v112/uint8arrays@4.0.3/deno/equals.js": "3d6f95846dae7e98ba201bb3916380fd831c7c03da07c05ea8cd0da3d6a4bcf5", 80 | "https://esm.sh/v112/uint8arrays@4.0.3/deno/from-string.js": "eb670e861d31734e125ee903657d304ccbe26248fe47afe60efdb3b8accc3921", 81 | "https://esm.sh/v112/uint8arrays@4.0.3/deno/to-string.js": "086c9a1c1149948ec1beb274a5fc8bdd537c6d648cfe3510c92b00d19686d8ce", 82 | "https://esm.sh/v112/varint@6.0.0/deno/varint.mjs": "7f595b37abaa62f18793f5d0615b6cb23d11d7d3af3dce11590ed70a67649093" 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /deps.ts: -------------------------------------------------------------------------------- 1 | export { Multibases } from 'https://esm.sh/ipfs-core-utils@0.18.0/multibases' 2 | export { Multicodecs } from 'https://esm.sh/ipfs-core-utils@0.18.0/multicodecs' 3 | export { Multihashes } from 'https://esm.sh/ipfs-core-utils@0.18.0/multihashes' 4 | export * as dagPB from 'https://esm.sh/@ipld/dag-pb@4.0.2' 5 | export * as dagCBOR from 'https://esm.sh/@ipld/dag-cbor@9.0.0' 6 | export * as dagJSON from 'https://esm.sh/@ipld/dag-json@10.0.1' 7 | export * as dagJOSE from 'https://esm.sh/dag-jose@4.0.0' 8 | export { identity } from 'https://esm.sh/multiformats@11.0.2/hashes/identity' 9 | export { bases, codecs, hashes } from 'https://esm.sh/multiformats@11.0.2/basics' 10 | export { isMultiaddr } from 'https://esm.sh/@multiformats/multiaddr@12.1.0' 11 | 12 | export { default as globSourceImport } from 'https://esm.sh/ipfs-utils@9.0.14/src/files/glob-source.js' 13 | 14 | export type { 15 | HTTPClientExtraOptions, 16 | } from 'https://esm.sh/ipfs-http-client@60.0.0/src/types.ts' 17 | export type { Multiaddr } from 'https://esm.sh/@multiformats/multiaddr@12.1.0' 18 | export type { BlockCodec } from 'https://esm.sh/multiformats@11.0.2/codecs/interface' 19 | export type { MultihashHasher } from 'https://esm.sh/multiformats@11.0.2/hashes/interface' 20 | export type { MultibaseCodec } from 'https://esm.sh/multiformats@11.0.2/bases/interface' 21 | -------------------------------------------------------------------------------- /lib/configure.ts: -------------------------------------------------------------------------------- 1 | import { Client } from './core.ts' 2 | import type { Options } from '../types.d.ts' 3 | 4 | type Fn = (client: Client, clientOptions: Options) => T 5 | 6 | type Factory = (clientOptions: Options) => T 7 | 8 | export const configure = (fn: Fn): Factory => { 9 | return (options) => { 10 | return fn(new Client(options), options) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/core.ts: -------------------------------------------------------------------------------- 1 | import { isMultiaddr } from '../deps.ts' 2 | import parseDuration from 'https://esm.sh/parse-duration@1.0.3' 3 | import { logger } from 'https://esm.sh/@libp2p/logger@2.0.6' 4 | import HTTP from 'https://esm.sh/ipfs-utils@9.0.14/src/http.js' 5 | import mergeOpts from 'https://esm.sh/merge-options@3.0.4' 6 | import { toUrlString } from 'https://esm.sh/ipfs-core-utils@0.18.0/to-url-string' 7 | import type { Multiaddr, Options } from '../deps.ts' 8 | import type { HTTPOptions } from '../_vendor/ipfs-utils.types.d.ts' 9 | 10 | const log = logger('ipfs-http-client:lib:error-handler') 11 | const merge = mergeOpts.bind({ ignoreUndefined: true }) 12 | 13 | const DEFAULT_PROTOCOL = location.protocol 14 | const DEFAULT_HOST = location.hostname 15 | const DEFAULT_PORT = location.port 16 | 17 | const normalizeOptions = (options: Options | URL | Multiaddr | string = {}): Options => { 18 | let url 19 | let opts: Options = {} 20 | 21 | if (typeof options === 'string' || isMultiaddr(options)) { 22 | url = new URL(toUrlString(options)) 23 | } else if (options instanceof URL) { 24 | url = options 25 | } else if (typeof options.url === 'string' || isMultiaddr(options.url)) { 26 | url = new URL(toUrlString(options.url)) 27 | opts = options 28 | } else if (options.url instanceof URL) { 29 | url = options.url 30 | opts = options 31 | } else { 32 | opts = options || {} 33 | 34 | const protocol = (opts.protocol || DEFAULT_PROTOCOL).replace(':', '') 35 | const host = (opts.host || DEFAULT_HOST).split(':')[0] 36 | const port = opts.port || DEFAULT_PORT 37 | 38 | url = new URL(`${protocol}://${host}:${port}`) 39 | } 40 | 41 | if (opts.apiPath) { 42 | url.pathname = opts.apiPath 43 | } else if (url.pathname === '/' || url.pathname === undefined) { 44 | url.pathname = 'api/v0' 45 | } 46 | 47 | return { 48 | ...opts, 49 | host: url.host, 50 | protocol: url.protocol.replace(':', ''), 51 | port: Number(url.port), 52 | apiPath: url.pathname, 53 | url, 54 | } 55 | } 56 | 57 | export const errorHandler = async (response: Response) => { 58 | let msg 59 | 60 | try { 61 | if ((response.headers.get('Content-Type') || '').startsWith('application/json')) { 62 | const data = await response.json() 63 | log(data) 64 | msg = data.Message || data.message 65 | } else { 66 | msg = await response.text() 67 | } 68 | } catch (/** @type {any} */ err) { 69 | log('Failed to parse error response', err) 70 | // Failed to extract/parse error message from response 71 | msg = err.message 72 | } 73 | 74 | let error = new HTTP.HTTPError(response) 75 | 76 | if (msg) { 77 | // This is what rs-ipfs returns where there's a timeout 78 | if (msg.includes('deadline has elapsed')) { 79 | error = new HTTP.TimeoutError() 80 | } 81 | 82 | // This is what go-ipfs returns where there's a timeout 83 | if (msg && msg.includes('context deadline exceeded')) { 84 | error = new HTTP.TimeoutError() 85 | } 86 | } 87 | 88 | // This also gets returned 89 | if (msg && msg.includes('request timed out')) { 90 | error = new HTTP.TimeoutError() 91 | } 92 | 93 | // If we managed to extract a message from the response, use it 94 | if (msg) { 95 | error.message = msg 96 | } 97 | 98 | throw error 99 | } 100 | 101 | const KEBAB_REGEX = /[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g 102 | 103 | const kebabCase = (str: string) => { 104 | return str.replace(KEBAB_REGEX, function (match) { 105 | return '-' + match.toLowerCase() 106 | }) 107 | } 108 | 109 | const parseTimeout = (value: string | number) => { 110 | return typeof value === 'string' ? parseDuration(value) : value 111 | } 112 | 113 | export class Client extends HTTP { 114 | constructor(options: Options | URL | Multiaddr | string = {}) { 115 | const opts = normalizeOptions(options) 116 | 117 | super({ 118 | timeout: parseTimeout(opts.timeout || 0) || undefined, 119 | headers: opts.headers, 120 | base: `${opts.url}`, 121 | handleError: errorHandler, 122 | transformSearchParams: (search: [string, string | number][]) => { 123 | const out = new URLSearchParams() 124 | 125 | for (const [key, value] of search) { 126 | if ( 127 | value !== 'undefined' && 128 | value !== 'null' && 129 | key !== 'signal' 130 | ) { 131 | out.append(kebabCase(key), value as string) 132 | } 133 | if (key === 'timeout' && !isNaN(value as number)) { 134 | out.append(kebabCase(key), value as string) 135 | } 136 | } 137 | 138 | return out 139 | }, 140 | }) 141 | 142 | delete this.get 143 | delete this.put 144 | delete this.delete 145 | delete this.options 146 | 147 | this.fetch = (resource: string | Request, options: HTTPOptions = {}) => { 148 | if (typeof resource === 'string' && !resource.startsWith('/')) { 149 | resource = `${opts.url}/${resource}` 150 | } 151 | 152 | return fetch.call( 153 | this, 154 | resource, 155 | merge(options, { 156 | method: 'POST', 157 | }), 158 | ) 159 | } 160 | } 161 | } 162 | 163 | export const HTTPError = HTTP.HTTPError 164 | -------------------------------------------------------------------------------- /lib/to-url-search-params.ts: -------------------------------------------------------------------------------- 1 | // @deno-types="https://esm.sh/ipfs-http-client@60.0.0/dist/src/lib/mode-to-string.d.ts" 2 | import { modeToString } from 'https://esm.sh/ipfs-http-client@60.0.0/src/lib/mode-to-string.js' 3 | import { parseMtime } from 'https://esm.sh/ipfs-http-client@60.0.0/src/lib/parse-mtime.js' 4 | 5 | export function toUrlSearchParams( 6 | { arg, searchParams, hashAlg, ...options }: Partial< 7 | { arg: any; searchParams: unknown; hashAlg: string; mtime: any; mode: string; hash: string; mtimeNsecs: number; timeout: number | string; } 8 | > = {}, 9 | ) { 10 | if (searchParams) { 11 | options = { 12 | ...options, 13 | ...searchParams, 14 | } 15 | } 16 | 17 | if (hashAlg) { 18 | options.hash = hashAlg 19 | } 20 | 21 | if (options.mtime != null) { 22 | const mtime = parseMtime(options.mtime)! 23 | 24 | options.mtime = mtime.secs 25 | options.mtimeNsecs = mtime.nsecs 26 | } 27 | 28 | if (options.mode != null) { 29 | options.mode = modeToString(options.mode) 30 | } 31 | 32 | if (options.timeout && !isNaN(options.timeout as number)) { 33 | // server API expects timeouts as strings 34 | options.timeout = `${options.timeout}ms` 35 | } 36 | 37 | if (arg === undefined || arg === null) { 38 | arg = [] 39 | } else if (!Array.isArray(arg)) { 40 | arg = [arg] 41 | } 42 | 43 | const urlSearchParams = new URLSearchParams(options as Record) 44 | 45 | arg.forEach((arg: string) => urlSearchParams.append('arg', arg)) 46 | 47 | return urlSearchParams 48 | } 49 | -------------------------------------------------------------------------------- /mod.ts: -------------------------------------------------------------------------------- 1 | import { 2 | bases, 3 | codecs, 4 | dagCBOR, 5 | dagJOSE, 6 | dagJSON, 7 | dagPB, 8 | globSourceImport, 9 | hashes, 10 | identity, 11 | Multibases, 12 | Multicodecs, 13 | Multihashes, 14 | } from './deps.ts' 15 | import type { BlockCodec, MultibaseCodec, MultihashHasher } from './deps.ts' 16 | import type { Options, IPFSHTTPClient } from "./types.d.ts"; 17 | 18 | export function create(options: Options = {}) { 19 | // deno-lint-ignore no-explicit-any 20 | const id: BlockCodec = { 21 | name: identity.name, 22 | code: identity.code, 23 | encode: (id) => id, 24 | decode: (id) => id, 25 | } 26 | 27 | const multibaseCodecs: MultibaseCodec[] = Object.values(bases) 28 | ;(options.ipld && options.ipld.bases ? options.ipld.bases : []).forEach((base) => multibaseCodecs.push(base)) 29 | 30 | const multibases = new Multibases({ 31 | bases: multibaseCodecs, 32 | loadBase: options.ipld && options.ipld.loadBase, 33 | }) 34 | 35 | // deno-lint-ignore no-explicit-any 36 | const blockCodecs: BlockCodec[] = Object.values(codecs) 37 | ;[dagPB, dagCBOR, dagJSON, dagJOSE, id].concat((options.ipld && options.ipld.codecs) || []).forEach((codec) => 38 | blockCodecs.push(codec) 39 | ) 40 | 41 | const multicodecs = new Multicodecs({ 42 | codecs: blockCodecs, 43 | loadCodec: options.ipld && options.ipld.loadCodec, 44 | }) 45 | 46 | const multihashHashers: MultihashHasher[] = Object.values(hashes) 47 | ;(options.ipld && options.ipld.hashers ? options.ipld.hashers : []).forEach((hasher) => multihashHashers.push(hasher)) 48 | 49 | const multihashes = new Multihashes({ 50 | hashers: multihashHashers, 51 | loadHasher: options.ipld && options.ipld.loadHasher, 52 | }) 53 | 54 | const client: IPFSHTTPClient = { 55 | // add: createAdd(options), 56 | // addAll: createAddAll(options), 57 | // bitswap: createBitswap(options), 58 | // block: createBlock(options), 59 | // bootstrap: createBootstrap(options), 60 | // cat: createCat(options), 61 | // commands: createCommands(options), 62 | // config: createConfig(options), 63 | // dag: createDag(multicodecs, options), 64 | // dht: createDht(options), 65 | // diag: createDiag(options), 66 | // dns: createDns(options), 67 | // files: createFiles(options), 68 | // get: createGet(options), 69 | // getEndpointConfig: createGetEndpointConfig(options), 70 | // id: createId(options), 71 | // isOnline: createIsOnline(options), 72 | // key: createKey(options), 73 | // log: createLog(options), 74 | // ls: createLs(options), 75 | // mount: createMount(options), 76 | // name: createName(options), 77 | // object: createObject(multicodecs, options), 78 | // pin: createPin(options), 79 | // ping: createPing(options), 80 | // pubsub: createPubsub(options), 81 | // refs: createRefs(options), 82 | // repo: createRepo(options), 83 | // resolve: createResolve(options), 84 | // start: createStart(options), 85 | // stats: createStats(options), 86 | // stop: createStop(options), 87 | // swarm: createSwarm(options), 88 | // version: createVersion(options), 89 | bases: multibases, 90 | codecs: multicodecs, 91 | hashers: multihashes, 92 | } 93 | 94 | return client 95 | } 96 | 97 | export const globSource = globSourceImport 98 | -------------------------------------------------------------------------------- /types.d.ts: -------------------------------------------------------------------------------- 1 | import type { Multiaddr, BlockCodec, MultihashHasher, MultibaseCodec } from './deps.ts' 2 | import type { IPFS } from "https://esm.sh/ipfs-core-types@0.14.0" 3 | 4 | export interface AbortOptions { 5 | /** 6 | * Can be provided to a function that starts a long running task, which will 7 | * be aborted when signal is triggered. 8 | */ 9 | signal?: AbortSignal 10 | /** 11 | * Can be provided to a function that starts a long running task, which will 12 | * be aborted after provided timeout (in ms). 13 | */ 14 | timeout?: number 15 | } 16 | 17 | export interface Options { 18 | host?: string 19 | port?: number 20 | protocol?: string 21 | headers?: Headers | Record 22 | timeout?: number | string 23 | apiPath?: string 24 | url?: URL|string|Multiaddr 25 | ipld?: Partial 26 | } 27 | 28 | export interface LoadBaseFn { (codeOrName: number | string): Promise> } 29 | export interface LoadCodecFn { (codeOrName: number | string): Promise> } 30 | export interface LoadHasherFn { (codeOrName: number | string): Promise } 31 | 32 | export interface IPLDOptions { 33 | loadBase: LoadBaseFn 34 | loadCodec: LoadCodecFn 35 | loadHasher: LoadHasherFn 36 | bases: Array> 37 | codecs: Array> 38 | hashers: MultihashHasher[] 39 | } 40 | 41 | export interface HTTPClientExtraOptions { 42 | headers?: Record 43 | searchParams?: URLSearchParams 44 | } 45 | 46 | export interface EndpointConfig { 47 | host: string 48 | port: string 49 | protocol: string 50 | pathname: string 51 | 'api-path': string 52 | } 53 | 54 | export interface IPFSHTTPClient extends IPFS { 55 | getEndpointConfig: () => EndpointConfig 56 | } 57 | --------------------------------------------------------------------------------