Giter Club home page Giter Club logo

ipfs-deploy's Introduction

ipfs-deploy

standard-readme compliant

Upload static websites to IPFS pinning services and optionally update the DNS.

The goal of ipfs-deploy is to make it as easy as possible to deploy a static website to IPFS.

Maintainer

This package is community-maintaned. Current go-to maintainer is @websoftwares.

Install

npm install -g ipfs-deploy

You can call it either as ipd or as ipfs-deploy:

ipd public/
ipfs-deploy public/

You can run it directly with npx so you don't need to install it:

npx ipfs-deploy _site

It will deploy to a public pinning service and give you a link to ipfs.io/ipfs/QmHash so you can check it out.

Usage

Please check md/usage.md for further information!

API

There is an API so, however it is somewhat unstable and subject to change.

Please check md/api.md for further information!

Contributing

Please check md/contributing.md for further information!

Users

If you use this package to deploy your website, please send a pull request so I can add it to the Users section in the README. (I reserve the right to exercise discretion.)

License

BlueOak-1.0.0 OR BSD-2-Clause-Patent OR MIT Β© Agent of User

(The first two are the most permissive possible ever, more than MIT, which doesn't have a patent waiver. Use whichever satisfies your lawyer better.)

ipfs-deploy's People

Contributors

agentofuser avatar austinhuang0131 avatar banciur avatar btimby avatar davidburela avatar davidhidvegi avatar dependabot-preview[bot] avatar dependabot[bot] avatar dholms avatar hacdias avatar ijaack avatar jtsmedley avatar kanej avatar kevincox avatar lidel avatar mikeshultz avatar olizilla avatar semantic-release-bot avatar shalokshalom avatar sixcorners avatar staikosi avatar tcrowe avatar triplespeeder avatar web-flow avatar websoftwares avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ipfs-deploy's Issues

ipfs-cluster deployment seems to be broken?

I can't seem to deploy to my ipfs-cluster instance. I'm not sure if it's related to the cluster using https, or if it's just broken in general.

The error that it spits out is: SyntaxError: Unexpected token C in JSON at position 0

Problem with Cloudflare DNS beaming

I got this error after configuring the .env file.
Am I making it wrong, or is it a bug?

β„Ή πŸ“‘ Beaming new hash to DNS provider Cloudflare…
(node:12729) [DEP0066] DeprecationWarning: OutgoingMessage.prototype._headers is deprecated
βœ– πŸ’” Updating Cloudflare DNS didn't work.

HTTPError: Response code 400 (Bad Request)

NAT Traversal

Having to manually forward a port just so that a disposable IPFS node can be reached by Pinata is a lot of trouble.

Maybe there is an easy-to-use npm package to do some basic UPnP port forwarding without being as comprehensive as perhaps libp2p wants to do it so we don't have to block on that.

Keeping an eye on this anyway:
libp2p/js-libp2p#104

Why Prettier

@agentofuser I was wondering why did you choose prettier to lint the code. Just wondering since most of the time I've seen standard.js being used which seems much more cleaner (less commas, more spaces, etc).

Support all the pinning services

Tracking issue for pinning service support. Going to prioritize those that have some kind of free-forever tier. And among those, the ones that require no signup or have the least friction to signup.

  • Infura.io
  • Pinata.cloud
  • Temporal.cloud
  • Eternum.io

Better error messages

 βœ– πŸ’”  Updating Cloudflare DNS didn't work.
32   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
33   β”‚                                                                          β”‚
34   β”‚ HTTPError: Response code 400 (Bad Request)                               β”‚
35   β”‚                                                                          β”‚
36   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

I really wish this included why.
The 403 error has an errors key in the json response that contains error messages that say why it didn't work.
I bet this 400 Bad Request response also had error messages.
This cli should output those messages.

Update required version of node to 12

Right now ipfs-deploy only builds on node 10.15.3 whereas the stable version of node is 12.1.0

I'd appreciate it if I didn't also need nvm to install this and could just use stable node

Refactor files for more modularity and easy of adding more

As I was making #73, I had the idea of restructuring the directories in the following way:

  • bin/ - as it is right now.
  • src/
    • index.js - move from the root.
    • dnslink/
      • cloudflare.js
      • sth-else.js
    • pinners/
      • infura.js
      • pinata.js
      • ipfs-cluster.js
      • sth-else.js

From dnslink, all files would export the same api:

updateSomethingDns(options)

From pinners, all files would export the same api:

// Setup Pinner
const pinner = setupPinnerName(options)

// Pin from file system
const hash = await pinner.pin(dir, options)

// Pin hash
await pinner.pinHash(hash, options)

Otherwise:

// Setup Pinner
const pin = setupPinnerName(options)

// Pin from file system
const hash = await pin(dir, options)

Just pin

Some pinning services (such as PiΓ±ata or IPFS Cluster) allow us to just pin the hash instead of 'uploading' everything at the time. For instance, if I'd like to be able to 'upload' my files to IPFS Cluster and then use that hash to pin on PiΓ±ata via their API instead of 'uploading' to both services.

Perhaps we could use a different flag. Maybe, by default, we'd 'upload' it to all services and if we set --hash-from <service> it would use the hash given by that service and then pin on the others.

Deploy via IPFSpin

For people wanting to try out IPFS-deploy, a second, account-free default in addition to Infura can be IPFSpin twitter bot. Its IPFS cluster may seem to be overloaded but it still makes big promises to the world. Lately it always replys:

"IPFS Cluster has been pinning this for 10 mins. This is normal for big files. Otherwise, make sure there are providers for it. Don't worry, Cluster will keep at it for a week before giving up."

But as far as I can tell, you never know whether the pin was successful, as I never have seen a follow-up tweet to the above upon success (or failure). So a quiet request to ipfspin bot (or directly to its cluster?) via some less-noisy API might be a good thing.

Some details of the experience:
https://js.ipfs.io/ipns/QmZJBQBXX98AuTcoR1HBGdbe5Gph74ZBWSgNemBcqPNv1W/Zero-Config-CLI-to-Deploy-Static-Websites-to-IPFS.html

Split CLI and API into separate packages

A more long-term idea: at some point it would be nice to have a monorepo with separate npm packages for the cli and the api. That way, heavier dependencies needed for better usability (say, CLI React?) can be segregated to the cli, leaving a lean, tree-shakeable api for other types of integrations.

Feature request: "health check" to test availability

Idea by @daviddias on scuttlebutt:

Are you planning on integrating a "health check" in which users can check the number of replicas available and confirm if it is still pinned?

It would be nice to have a way to monitor latency and uptime for pinning services, don't know how that would fit within ipfs-deploy yet or if a different service.

I do want to add more "deployment management" things like CID history, easy rollbacks, etc.

πŸ“Œ Pinning Services Support

Summary

This is a revamp of issue #12 about supporting more pinning services. Here, we will track the support for all pinning services across many issues and PRs.

Status

Pinning Service Status
Infura πŸ“
Pinata πŸ“
Fission No longer supported as per #222
IPFS Cluster πŸ“
DAppNode πŸ“
Temporal.cloud ❔
Eternum.io ❔
IPFS Pin Bot (#23) ❔
  • ❔ : Status unknown
  • 🐣 : Work in progress
  • πŸ“ : Published

relative links in files not added

If I upload a dir of a basic static website (a basic html, js and css file) with ipfs-deploy, even though the js/css files are relatively linked I get this error when viewing the resulting hash:

The resource from β€œhttps://ipfs.infura.io/ipfs/style.css” was blocked due to MIME type (β€œtext/plain”) mismatch (X-Content-Type-Options: nosniff).

Same website works fine when I do ipfs add -r ..

Not same hash on pin service

I am using Infura and Pinata for my pin service.
But it always said "Found inconsistency in pinned hashes"
Pinata folder name is always Cloudflare DNS Record, or /Users/myusername/.config/yarn/global/node_modules/ipfs-deploy/src
Not the real folder name.

πŸ”— Naming Providers Support

Summary

This is the main tracking issue for Naming Providers support. Naming providers can be either DNS - for use with DNSLink - or even naming services such as ENS and IPNS.

Ideally even doing things like detecting the provider from the domain name and doing the right thing.

Status

Naming Service Status
CloudFlare πŸ“
DNSimple πŸ“
DreamHost πŸ“
Digital Ocean ❔
AWS πŸ“
Azure ❔
Zeit ❔
IPNS ❔
ENS ❔
  • ❔ : Status unknown
  • 🐣 : Work in progress
  • πŸ“ : Published

Move to React for the CLI (Pastel)

Using Pastel (an Ink framework by Ink's author) would make it easier to add interactivity, like asking for credentials when they are missing, allowing the user to pick services from a list, and in general allowing an alternative UX to command line parameters. Also a great source of snazzy pretty things :)

https://github.com/vadimdemedes/pastel

A first PR would be to just set up a skeleton and convert what's already there to Pastel without changing anything user-facing.

Use the git data structure directly rather than importing to IPFS

As an idea for consideration: What if you don't have to convert your files into MerkleDAGs and just used the git structure directly?

One of the cool tricks that IPFS can do is resolve any kind of hash linked data structure. This means that you can save the users a step by avoiding the ipfs add and instead just resolve the git hash as a file system and resolve the published version of the website as the git hash.

With this, then to load any website, effectively you would just need to do something such as ipfs.io/git/<githash> and IPFS would resolve and distribute it through the network.

If this all sounds like jibba jabba at first glance, I do have some urls to clarify, namely:

  • The IPLD Talk that explains the idea beyond the InterPlanetary Linked Data
  • Traversing IPLD graphs examples. These examples show how to pick traverse nodes from a graph, including git graphs.
  • IGiS, the InterPlanetary Git Service. A Web app that allows you to traverse git repos that are fetched through IPFS (note: apparently the version that is published has some spider webs to be cleaned up, I've ping @magik6k (the author) to see if he can check in) (note2: I also know that @magik6k has multiple recorded demos, once I find them, I'll link them here :))

Demo SVG is making README very CPU heavy

I recorded it with asciinema and converted to svg with svg-term. For some reason it's making the page very slow to respond for me. There's gotta be some parameter that can be tuned to improve that.

Warn user when multiple paths exist

A standard create-react-app has both a "public" and "build" folder. The build folder is the one containing the actual build, while the "public" folder should be ignored.

Gguess-path.js just returns the first found matching folder, so you end up deploying the wrong "public" folder.

I think one possibility would be to just bail out when more than one candidate is found with an according message to the user. Something like "Found more than one path candidate to deploy, please specify the path to use.

Add support for DNSimple

Allow ipfs-deploy to update a DNSLink TXT record via the DNSimple api.

I'm happy to work on this as we need it for deploying sites for IPFS and Protocol Labs.

I'll add more details here when dig into it.

Write end-to-end tests

I don't have much experience testing JS, so suggestions of tools and practices are welcome.

Show upload progress, speed, and ETA

The ora spinner is fine for things that take a couple of seconds, but when it's spinning for a minute it's hard to tell if things are still alive.

@olizilla, would you like to take this on? For Infura it seems simpler, as it's pure HTTP, but for Pinata it involves getting networking info from the in-process IPFS daemon and I don't know much about libp2p atm.

Silently ignores partly failing uploads, providing wrong results

After experiencieng issues with infura hashes not matching the hashes from other pinners i looked a bit deeper into the issue to understand what is happening.

Test case is the following directory structure:

public/:
root.txt  sub

public/sub:
sub1.txt  sub2.txt  subsub

public/sub/subsub:
subsub1.txt  subsub2.txt

Then i tried to upload with infura and dappnode pinner, and check the response from api.addFromFs in pinners/infura.js and pinners/dappnode.js (via debugger):

Response from dappnode pinner:

[ { path: 'public/root.txt',
    hash: 'QmQJRuzSnVj3ZtyTkuLqdhBL7QBzpDMwUSsuX2ZvvZqK9X',
    size: 34 },
  { path: 'public/sub/sub1.txt',
    hash: 'QmYXAWSYoyDVAwGuUe5X9B4F9e4r1G8E66meVk12U6MZSQ',
    size: 44 },
  { path: 'public/sub/sub2.txt',
    hash: 'Qma8NBewQpX2GZafD71zYUjVX7Ve5LZ44bYaEcTCHYY3hE',
    size: 44 },
  { path: 'public/sub/subsub/subsub1.txt',
    hash: 'QmZxES63SLnndw2vASMwYpXggqpUJu1wNhbNLmMti1GGq1',
    size: 53 },
  { path: 'public/sub/subsub/subsub2.txt',
    hash: 'Qma9Y3hHFb7Xt1kYoLZ6emtGRZym8wooTHokJ9NYhD2DDN',
    size: 53 },
  { path: 'public/sub/subsub',
    hash: 'QmUnRVEPAPnsm3cTWLy6cA9RrgdHajSbuuVaJb8WRpvyXr',
    size: 216 },
  { path: 'public/sub',
    hash: 'Qmf9EDMfav9WBmM5reng5H4igoMnkFJP5eiN4pTUD12hV5',
    size: 457 },
  { path: 'public',
    hash: 'QmWJJU8G7SDBYvu3dYZ9qdDHNgLkbQLGwAREEx3Djc5LFd',
    size: 591 } ]

Response from infura pinner:

[ { path: 'public/root.txt',
    hash: 'QmQJRuzSnVj3ZtyTkuLqdhBL7QBzpDMwUSsuX2ZvvZqK9X',
    size: 34 } ]

Obviously, the responses are different. Dappnode response object contains all files, with the root folder element being the last element. Infura response object contains just one (random?) file from the directory.

While the root cause of this issue might be instability of Infura service (it also took about 20 seconds to get a response from infura API at all, so they clearly had problems at this time), this error needs to result in ipfs-deploy exiting with an error instead of providing wrong results.

In only had a brief look so far at the implementation, but I'm wondering if this needs to be investigated more inside ipfs-http-client? After all we're just using their "addFromFs" API.

Edit/addendum: Looking at https://github.com/ipfs/interface-js-ipfs-core/blob/master/SPEC/FILES.md#addfromfs there is no mention of a specifc ordering of the results. So the current approach of assuming that the last array element is the root node seems quite dangerous.

Cloudflare dns

How do I setup my domain to resolve when using www? This is probably a simple fix but I'm not super well versed in DNS. My site is currently working when using the root domain, but the www 404s. I tried to follow along with these instructions but might have missed something. Here are my current DNS records:

CNAME www cloudflare-ipfs.com DNS Only 
CNAME tatecarson.com cloudflare-ipfs.com DNS Only 
TXT _dnslink dnslink=/ipfs/QmRFciUgPdWYpvFWcMWHEhcMndSeACMUhwVgZVcAmd5RF3 DNS only

and the env setup. I see that I have it going to the root here but not sure what else to put.

IPFS_DEPLOY_CLOUDFLARE__ZONE=tatecarson.com
IPFS_DEPLOY_CLOUDFLARE__RECORD=_dnslink.tatecarson.com

Any ideas?

Thank you, and I realize this isn't the best place to ask but I have no other idea where to go.

Works for me!

Hi! Author here. πŸ‘‹

This issue is where you can attest that ipfs-deploy was useful to you, if you so wish :) It makes a huge difference to my motivation.

Feel free to leave any kind of positive comment here, and please open a different issue for bug reports, feature requests, and telling me off for being a n00b. I will do my best to help <3

Could not reach port xxxx from outside

I'm getting a port unreachable error when trying to use the the api. Looking for some pointers rather than reporting an issue here.

This is the call I'm making from a nodejs app:

async function puttoifps() {
    try {
      const deployOptions = {
        publicDirPath: 'public',
        // siteDomain: 'ipfs.infura.io/ipfs/',
        remotePinners: 'pinata',
       // port: '8080',
        credentials: {
          pinata: {
            apiKey: 'my api key',
            secretApiKey: 'my secret',
          },
        },
      }
      return deploy(deployOptions)
    } catch (e) { console.log('error wtf happened?', e)}
  };

Here is there error message:
βœ” 🚚 public weighs 25.0 MiB.
βœ” ☎️ Connected to temporary local IPFS node.
βœ– πŸ’” Could not reach port 4002 from the outside :(
β„Ή πŸ’‘ Please forward it or try a different one with the --port option.

Error: Could not reach port 4002 from the outside

  • index.js:203 IPFS.node.on
    [gatsby-plugin-crypowerk-blockchain]/[@agentofuser]/ipfs-deploy/index.js:203 :16

  • next_tick.js:81 processTicksAndRejections
    internal/process/next_tick.js:81:5

I've tried changing the port in the config but that hasn't worked. I don't think it's a firewall issue on my side as those ports are open.

Any suggestions or pointers would be appreciated. - Kevin

Error 400 (Payload too large) when deploying

$ ipd
β„Ή πŸ€” No path argument specified. Looking for common ones…
βœ” πŸ“‚ Found local public directory. Deploying that.
βœ” 🚚 public weighs 133.3 MiB.
βœ– πŸ’” Uploading to Infura didn't work.

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚                                                                                         β”‚
  β”‚ {                                                                                       β”‚
  β”‚    "name": "Error",                                                                     β”‚
  β”‚    "className": "Error",                                                                β”‚
  β”‚    "message": "payload too large\n",                                                    β”‚
  β”‚    "superclasses": ["Object"],                                                          β”‚
  β”‚    "enumerableFields": { "statusCode": 400 },                                           β”‚
  β”‚    "stack": [                                                                           β”‚
  β”‚      "Error: payload too large",                                                        β”‚
  β”‚      "at parseError                                                                     β”‚
  β”‚ (/usr/local/share/.config/yarn/global/node_modules/ipfs-http-client/src/utils/send-requ β”‚
  β”‚ est.js:22:17)",                                                                         β”‚
  β”‚      "at ClientRequest.<anonymous>                                                      β”‚
  β”‚ (/usr/local/share/.config/yarn/global/node_modules/ipfs-http-client/src/utils/send-requ β”‚
  β”‚ est.js:64:14)",                                                                         β”‚
  β”‚      "at Object.onceWrapper (events.js:288:20)",                                        β”‚
  β”‚      "at ClientRequest.emit (events.js:200:13)",                                        β”‚
  β”‚      "at HTTPParser.parserOnIncomingClient [as onIncoming] (_http_client.js:565:23)",   β”‚
  β”‚      "at HTTPParser.parserOnHeadersComplete (_http_common.js:116:17)",                  β”‚
  β”‚      "at TLSSocket.socketOnData (_http_client.js:452:22)",                              β”‚
  β”‚      "at TLSSocket.emit (events.js:200:13)",                                            β”‚
  β”‚      "at addChunk (_stream_readable.js:294:12)",                                        β”‚
  β”‚      "at readableAddChunk (_stream_readable.js:275:11)"                                 β”‚
  β”‚    ]                                                                                    β”‚
  β”‚ }                                                                                       β”‚
  β”‚                                                                                         β”‚
  β”‚                                                                                         β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚                                                                                         β”‚
  β”‚ Failed to deploy.                                                                       β”‚
  β”‚                                                                                         β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Also happens with pinata.

Cloudflare tokens

Cloudflare supports generating tokens with specific set of permissions to be used instead of the global api key. Perhaps a separate IPFS_DEPLOY_CLOUDFLARE__API_TOKEN variable?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.