Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Alan Shaw committed Apr 20, 2021
0 parents commit 2b8c753
Show file tree
Hide file tree
Showing 9 changed files with 5,382 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
5 changes: 5 additions & 0 deletions LICENCE-APACHE
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
19 changes: 19 additions & 0 deletions LICENCE-MIT
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
The MIT License (MIT)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
105 changes: 105 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# ipfs-cluster

A zero-dependency client to the [IPFS Cluster](https://cluster.ipfs.io/) HTTP API, built for the browser.

## Install

Import it from your favourite CDN (e.g. skypack.dev, unpkg.com, jsdelivr.com) or install directly from npm:

```sh
npm i @nftstorage/ipfs-cluster
```

## Usage

Example:

```js
import { Cluster } from 'https://cdn.skypack.dev/@nftstorage/ipfs-cluster'

const cluster = new Cluster('https://your-cluster-domain.com', { auth: 'TOKEN' })

const file = new File(['foo'], 'foo.txt')
const { Hash } = await cluster.add(file)
```

### Using in Node.js

This library is designed to run in the browser or in web workers but it can be run in Node.js if required web APIs are added to the global environment. For exmaple:

```js
import fetch from '@web-std/fetch'
import { FormData } from '@web-std/form-data'
import { File, Blob } from '@web-std/file'

Object.assign(global, { fetch, File, Blob, FormData })
```

## API

This library is **WIP** and not _all_ cluster HTTP API methods are available yet.

### Constructor

Create a new instance of the cluster client.

```js
import { Cluster } from '@nftstorage/ipfs-cluster'
const cluster = new Cluster('https://your-cluster-domain.com', { auth: 'TOKEN' })
```

### `add`

Import a file to the cluster. First argument must be a `File` or `Blob`.

```js
const file = new File(['foo'], 'foo.txt')
const { cid } = await cluster.add(file)
```

### `addDirectory`

Imports multiple files to the cluster. First argument must be an array of `File` or `Blob`.

```js
const files = [
new File(['foo'], 'foo.txt'),
new File(['bar'], 'bar.txt'),
]
const dir = await cluster.addDirectory(file)

for (const entry of dir) {
console.log(entry.cid)
}
```

### `pin`

Tracks a CID with the given replication factor and a name for human-friendliness.

```js
const cid = 'bafybeigpsl667todjswabhelaxvwmk7amgg3txsv5tkcpbpj5rtrf6g7mu'
const { cid } = await cluster.pin(cid)
```

### `unpin`

Untracks a CID from cluster.

```js
const cid = 'bafybeigpsl667todjswabhelaxvwmk7amgg3txsv5tkcpbpj5rtrf6g7mu'
const { cid } = await cluster.pin(cid)
```

### `status`

Returns the current IPFS state for a given CID.

```js
const cid = 'bafybeigpsl667todjswabhelaxvwmk7amgg3txsv5tkcpbpj5rtrf6g7mu'
const status = await cluster.status(cid)

for (const [clusterPeerID, pinInfo] of Object.entries(status.peerMap)) {
console.log(`${clusterPeerID}: ${pinInfo.status}`)
}
```
171 changes: 171 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
export type AddResponse = {
cid: string
name?: string
size?: number|string
bytes?: number|string
}

export type AddDirectoryResponse = AddResponse[]

export type PinOptions = {
replicationFactorMin?: number
replicationFactorMax?: number
name?: string
/**
* 0 = recursive, 1 = direct
*/
mode?: 0|1
shardSize?: number
/**
* The peers to which this pin should be allocated.
*/
userAllocations?: string[]
expireAt?: Date
metadata?: Record<string, string>
pinUpdate?: string
}

export enum PinType {
/**
* Bad type showing up anywhere indicates a bug
*/
BAD = 1,
/**
* Data is a regular, non-sharded pin. It is pinned recursively.
* It has no associated reference.
*/
DATA = 2,
/**
* Meta tracks the original CID of a sharded DAG. Its Reference points to the
* Cluster DAG CID.
*/
META = 3,
/**
* ClusterDAG pins carry the CID of the root node that points to all the
* shard-root-nodes of the shards in which a DAG has been divided. Its
* Reference carries the MetaType CID.
* ClusterDAGType pins are pinned directly everywhere.
*/
CLUSTER_DAG = 4,
/**
* Shard pins carry the root CID of a shard, which points to individual blocks
* on the original DAG that the user is adding, which has been sharded. They
* carry a Reference to the previous shard. ShardTypes are pinned with
* MaxDepth=1 (root and direct children only).
*/
SHARD = 5
}

export type PinResponse = {
replicationFactorMin: number
replicationFactorMax: number
name: string
mode: 0|1
shardSize: number
/**
* The peers to which this pin is allocated.
*/
userAllocations?: string[]
expireAt: Date
metadata?: Record<string, string>
pinUpdate?: string
cid: string
/**
* Specifies which sort of Pin object we are dealing with. In practice, the
* type decides how a Pin object is treated by the PinTracker.
*/
type: PinType
/**
* The peers to which this pin is allocated.
*/
allocations: string[]
/**
* Indicates how deep a pin should be pinned, with -1 meaning "to the bottom",
* or "recursive".
*/
maxDepth: number
/**
* We carry a reference CID to this pin. For ClusterDAGs, it is the MetaPin
* CID. For the MetaPin it is the ClusterDAG CID. For Shards, it is the
* previous shard CID. When not needed it is undefined.
*/
reference?: string
}

export type StatusOptions = {
local?: boolean
}

export type StatusResponse = {
cid: string
name: string
peerMap: Record<string, PinInfo>
}

export type PinInfo = {
peerName: string
status: TrackerStatus
timestamp: Date
error: string
}

export enum TrackerStatus {
/**
* IPFSStatus should never take this value. When used as a filter. It means "all".
*/
UNDEFINED = 'undefined',
/**
* The cluster node is offline or not responding.
*/
CLUSTER_ERROR = 'cluster_error',
/**
* An error occurred pinning.
*/
PIN_ERROR = 'pin_error',
/**
* An error occurred unpinning.
*/
UNPIN_ERROR = 'unpin_error',
/**
* The IPFS daemon has pinned the item.
*/
PINNED = 'pinned',
/**
* The IPFS daemon is currently pinning the item.
*/
PINNING = 'pinning',
/**
* The IPFS daemon is currently unpinning the item.
*/
UNPINNING = 'unpinning',
/**
* The IPFS daemon is not pinning the item.
*/
UNPINNED = 'unpinned',
/**
* The IPFS daemon is not pinning the item but it is being tracked.
*/
REMOTE = 'remote',
/**
* The item has been queued for pinning on the IPFS daemon.
*/
PIN_QUEUED = 'queued',
/**
* The item has been queued for unpinning on the IPFS daemon.
*/
UNPIN_QUEUED = 'unpin_queued',
/**
* The IPFS daemon is not pinning the item through this CID but it is tracked
* in a cluster dag
*/
SHARDED = 'sharded'
}

export class Cluster {
constructor (url: URL|string, options?: { auth: string })
add (file: File|Blob, options: PinOptions): Promise<AddResponse>
addDirectory (file: Iterable<File|Blob>, options: PinOptions): Promise<AddDirectoryResponse>
pin (cid: string, options?: PinOptions): Promise<PinResponse>
unpin (cid: string): Promise<PinResponse>
status (cid: string, options?: StatusOptions): Promise<StatusResponse>
}
Loading

0 comments on commit 2b8c753

Please sign in to comment.