GithubHelp home page GithubHelp logo

libgrabber's Introduction

jsDelivr - Open Source CDN

jsdelivr-logo
A global super-fast and production-focused CDN, tightly integrated with NPM and GitHub
with support for on-the-fly optimizations, ES modules, detailed download stats and more

www.jsdelivr.com

Website Repo · Public API · Blog · @jsDelivr · Discord


We are looking for contributors. Please check open issues in the above repos if you think you could help, or open a new one if you have an idea you'd like to discuss.

jsDelivr is a free CDN for open-source files. We are tightly integrated with Github and npm, allowing us to automatically provide a reliable CDN service to almost every open-source project out there.

We offer a stable CDN that can be used in production on popular websites with huge amounts of traffic. There are no bandwidth limits or premium features, and it's completely free to use.

Why jsDelivr?

Ready for production

Our public CDN is built to be used in production by even the largest websites. Everything is optimized and constantly improved to offer all users maximum speed and uptime. Performance is monitored at all times, and we are always looking into new technologies and providers that may further improve our CDN. Downtime, timeouts, or slow responses are simply unacceptable.

We do everything possible to ensure our CDN will NEVER break any websites, regardless of the use case. If a file is available via our CDN, we assume it's used in production and ensure it will continue to work no matter what.

This includes dynamic endpoints such as /npm/, /gh/ and /combine/. When a file is first accessed, it gets permanently stored in a reliable file system. This means that even if a npm package gets deleted or an existing file gets removed by a developer, jsDelivr will continue to serve the stored copy forever without breaking any websites or causing any issues.

On top of that, we also do version fallback. This means that if a file used in version 1.0.1 is no longer available in 1.0.2 and a user requests the non-existent file, we will fall back to the previous 1.0.1 version and serve it instead of failing with a 404 error.

Multi-CDN

Unlike the competition, jsDelivr uses multiple CDN providers, resulting in the best possible uptime and performance. We currently use CloudFlare, and Fastly.

If a CDN goes down, websites that use jsDelivr won't have any issues because all traffic will be instantly redirected to remaining operational providers.

Smart Load Balancing

jsDelivr uses real user performance data (also known as RUM) to make its routing decisions. These metrics are gathered from hundreds of websites and are used in our load-balancing algorithm to make accurate decisions for serving content.

All providers (CDNs and custom servers) are tested millions of times per day by real users from all over the world. Based on this information, jsDelivr knows what provider is the fastest for each user. Each user gets a unique response based on his or her location, ISP, and the providers' uptime in real-time.

This system also responds immediately to performance degradation and downtime of providers. If a CDN is under a DDoS attack, and its performance drops in some locations, in a matter of seconds, the algorithm will pick up the change and start serving a different provider to all affected users.

Failover

We have multiple layers of failover to protect our users from any downtime.

We use 2 DNS providers at the same time. For jsDelivr to go down, both of these companies would have to go down at the same time.

Both of our DNS providers monitor our load-balanced endpoint, and if they detect problems, they will automatically switch all traffic to a single CDN provider.

Our load balancer monitors the uptime of all CDN providers using both RUM and synthetic data. If any of those detect downtime or performance degradation, that CDN provider will be removed immediately without any impact on our users.

Our origin consists of multiple servers in different data centers. If a server goes down, the CDNs will automatically switch to using the remaining healthy servers.

In total, we have one of the most resilient systems out there, ready to be used in production by even the biggest companies.

China

jsDelivr has multiple locations close to Chinese urban centers to ensure low latency and high performance for all Chinese users.

Usage Documentation

jsDelivr provides mirrors for npm, GitHub, WordPress plugins, and custom endpoints for several other projects with special requirements. If our regular endpoints don't work for your use case, let us know and we'll figure something out!

If you are a package author, check our tips for package authors to make using your package as easy as possible.

Root endpoint is always https://cdn.jsdelivr.net

npm

jsDelivr can instantly serve any file from any npm package in the public registry. New versions pushed to npm are instantly available via our CDN as well. No maintenance is required.

If a package, version, or file gets removed from npm, then jsDelivr will continue to serve that file from our permanent storage without breaking any websites using it.

We use a permanent S3 storage to ensure all files remain available even if npm goes down or a package is deleted by its author. Files are fetched directly from npm only the first time or when S3 goes down.

Load any project hosted on npm:
/npm/package@version/file
Load exact version:
/npm/[email protected]/dist/jquery.min.js
Use a version range instead of an exact version:
/npm/jquery@3/dist/jquery.min.js
/npm/[email protected]/dist/jquery.min.js

NOTE

If you use this feature and a file you requested is not available in the newest version of the package, the link will keep working thanks to our version-fallback feature. We'll continue to serve the file from the older version of the package instead of failing with a 404 error.


Load by tag (Not recommended for production usage):
/npm/jquery@beta/dist/jquery.min.js
Omit the version completely or use "latest" to load the latest one (not recommended for production usage):
/npm/jquery@latest/dist/jquery.min.js
/npm/jquery/dist/jquery.min.js

NOTE Requesting the latest version (as opposed to "latest major" or "latest minor") is dangerous because major versions usually come with breaking changes. Only do this if you really know what you are doing.


Add ".min" to any JS/CSS/SVG file to get a minified version - if one doesn't exist, we'll generate it for you. All generated files come with source maps and can be easily used during development:
/npm/[email protected]/github-markdown.min.css

NOTE Minifying a large file can take several seconds. However, we store all generated files in our permanent storage, so this delay only applies to the first few requests.


Omit the file path to get the default file. This file is always minified:
/npm/[email protected]
/npm/jquery@3
/npm/jquery
Get a directory listing:

GitHub

We recommend using npm for projects that support it for better UX - npm packages are searchable on our website, and package pages show additional useful information, such as descriptions and links to homepages.

We use a permanent S3 storage to ensure all files remain available even if GitHub goes down or a repository or a release is deleted by its author. Files are fetched directly from GitHub only the first time or when S3 goes down.

Load any GitHub release, commit, or branch:
/gh/user/repo@version/file
Load exact version:
/gh/jquery/[email protected]/dist/jquery.min.js
/gh/jquery/jquery@32b00373b3f42e5cdcb709df53f3b08b7184a944/dist/jquery.min.js
Use a version range instead of an exact version (only works with valid semver versions):
/gh/jquery/jquery@3/dist/jquery.min.js
/gh/jquery/[email protected]/dist/jquery.min.js

NOTE If you use this feature and a file you requested is not available in the newest release, the link will keep working thanks to our version-fallback feature. We'll continue to serve the file from older release instead of failing with a 404 error.


Omit the version completely or use "latest" to load the latest one (only works with valid semver versions): (not recommended for production usage)

Falls back to the master branch if there are no tagged releases.

/gh/jquery/jquery@latest/dist/jquery.min.js
/gh/jquery/jquery/dist/jquery.min.js

NOTE Requesting the latest version (as opposed to "latest major" or "latest minor") is dangerous because major versions usually come with breaking changes. Only do this if you really know what you are doing.


Add ".min" to any JS/CSS/SVG file to get a minified version - if one doesn't exist, we'll generate it for you. All generated files come with source maps and can be easily used during development:
/gh/jquery/[email protected]/src/core.min.js

NOTE Minifying a large file can take several seconds. However, we store all generated files in our permanent storage, so this delay only applies to the first few requests.


Get a directory listing:
/gh/jquery/[email protected]/
/gh/jquery/[email protected]/dist/

Combine multiple files

Our combine endpoint allows you to load several files from npm and GitHub endpoints in one request:

/combine/url1,url2,url3

All features that work for individual files (version ranges, minification, main modules) work here as well. All combined files come with source maps and can be easily used during development.

Examples:

/combine/gh/jquery/[email protected]/dist/jquery.min.js,gh/twbs/[email protected]/dist/js/bootstrap.min.js
/combine/npm/[email protected]/dist/css/bootstrap.min.css,npm/[email protected]/dist/css/bootstrap-theme.min.css

NOTE Combining large/many files can take several seconds. However, we store all generated files in our permanent storage, so this delay only applies to the first few requests.


Publishing packages

All packages hosted on npm and tagged releases on GitHub are automatically available on jsDelivr. If you are a package author, here are a few tips to make using your package as easy as possible:

  • Use semver for versioning (this is enforced by npm but not by GitHub)
  • If a file listed as main in package.json isn't meant to be used in a browser, set a browser or jsdelivr field
  • If you distribute minified JS/CSS files, also include source maps for those files
  • If you don't want to provide minified files, it's fine - we'll handle that for you

Configuring a default file in package.json

For packages hosted on npm, we support serving "default" files with shorter URLs. The default file can be configured by setting one of the following fields in package.json, with jsdelivr having the highest priority:

  1. jsdelivr
  2. browser
  3. main

We will first attempt to locate a minified version of the file provided here (by removing the extension and looking for the same file .min.js). If we can't find one, we will minify ourselves.

Be advised that you must include the file extension in the values, for example:

"main": "./index" // this will NOT work
"main": "./index.js" // this is the correct way

For projects having both a JS and a CSS file, use one the above fields for JS and a style field for the CSS file (example).

Restrictions

  • Packages larger than 150 MB or single files larger than 20 MB (in the case of GitHub) are not supported by default. We recommend removing files that are not needed from your package when possible. If you need to set a higher limit for your package, open an issue in this repo.
  • HTML files are served with Content-Type: text/plain for security reasons.

WordPress

Our WordPress endpoint works for plugins and themes hosted in the WordPress.org plugin directory and Wordpress.org theme directory, and mirrors the WordPress.org plugins SVN repo.

Load any plugin from the WordPress.org plugins SVN repo:
/wp/project/tags/version/file
Load exact version:
/wp/wp-slimstat/tags/4.6.5/wp-slimstat.js
Load the latest version (not recommended for production usage):
/wp/wp-slimstat/trunk/wp-slimstat.js
Load any theme from the WordPress.org themes SVN repo:
https://cdn.jsdelivr.net/wp/themes/project/version/file
Load an exact version of a file:
https://cdn.jsdelivr.net/wp/themes/twenty-eightteen/1.7/assets/js/html5.js
Add ".min" to any JS/CSS file to get a minified version - if one doesn't exist, we'll generate it for you. All generated files come with source maps and can be easily used during development:
https://cdn.jsdelivr.net/wp/themes/twenty-eightteen/1.7/assets/js/html5.min.js

Caching

Our caching logic and headers are optimized for production use and apply to all non-custom endpoints.

  • Static Versions and commit hashes - Effectively forever. The caching headers are set for 1 year but we also permanently cache the files in our S3 storage. So all future requests that bypass the CDN will hit our S3 storage with no option or way to update the contents of that file.
  • Version aliasing - 7 days. This also includes latest versions. They are cached on our CDN for 7 days with the option to purge the cache using our API to speed up the release of your project to your users.
  • Branches - 12 hours.

In certain cases, purgeable files can get updated faster due to low-cache hit ratio or forced CDN purge from our side for maintenance reasons.

We use permanent S3 caching even with dynamic URLs, such as version aliasing, meaning once we download your tagged files, there is no way for you to update them. If there is a critical issue in your latest release the best course of action is to tag a new semver valid release with the fix and purge the CDN URLs using our purge API.

Purge cache

jsDelivr has an easy-to-use API to purge files from the cache and force the files to update. This is useful when you release a new version and want to force the update of all version-aliased users.

Please note:

  • It will not work for static files as explained above.
  • Valid semver releases must be used for purge to work
  • Rate-limiting applies to all users

To avoid abuse, access to purge is given after an email request (for now - [email protected]).

Custom CDN Hosting

We can work together and set up a custom configuration for your project. This way, you can have full control over your files and the ability to utilize the full power of jsDelivr.

This kind of custom hosting can be suitable for:

  • Binary hosting. Windows executable files and zips.
  • Frequently updated files.
  • Projects that can't follow jsDelivr file structure.
  • Some other use that will blow all of our minds.

Simply send an email to [email protected] with a request for more information.

Current OSS projects using custom configs:

Privacy Policy

cdn.jsdelivr.net

jsDelivr might use information about downloaded files to build download stats per project and per file.

jsDelivr does not store any user data and does not track any users in any way.

Here are the relevant policies of our CDN providers:

libgrabber's People

Contributors

aleksandara avatar bzmw avatar jangie avatar jimaek avatar mar10 avatar martinkolarik avatar megawac avatar thekondrashov avatar tombyrer avatar unboundev avatar

Stargazers

 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

libgrabber's Issues

Support major.minor versions

Many smaller projects, particularly those of CSS and JavaScript such as hosted on jsDelivr, utilize a major.minor structure. Despite not being SemVer compliant per-se, libgrabber should probably support them.

The way I would do this is to detect the versions with only one dot separating two integers early on, and appending a second minor version to fix the detection of new versions, but use the original for the folder names in the projects.

Example

1.2 => 1.2.0
1.4.3 => 1.4.3

Rename files

Is there any way to make the bot rename some files before pushing them?
This way we can make sure that all projects will follow our structure.

Discussion jsdelivr/jsdelivr#915

Allow the bot to merge own PRs

If its possible it would help a lot to add a parameter to update.json.
Something like "auto-merge": true.
It can be enabled per project and if the bot sees it then it will automatically merge the PR for that project without waiting for a mod.

Update projects with no local versions

If a project doesn't have any local versions then libgrabber should simply download the latest available.
Example:

files/jquery.cookie/
info.ini
update.json

This is going to be awesome as it doesnt require the author to fork jsDelivr and add an initial version. He can simply use Web GUI to add update.json and libgrabber will take care of the rest.

Version aliasing doesn't point to the latest version

Hi there,
We've recently pushed the latest version of our javascript client: v2.6.6 and we'd like to make it available to our users through jsdelivr to always provide them our latest version, so they won't have to worry about minor updates.
We just discovered that aliases point to different versions, and not to the last one.

Latest version: 2.6.6 (added 13 days ago)
http://cdn.jsdelivr.net/algoliasearch/latest/algoliasearch.min.js -> 2.6.2
http://cdn.jsdelivr.net/algoliasearch/2/algoliasearch.min.js -> 2.6.5

Sounds like a caching issue. Could you please have a look into it.
Otherwise, can you tell me how often does the libgrabber bot crawle github repos?

Thanks,

auto-update repos' README?

Idea source

Likely a v2 thing, but could work something like this:

  1. Repo managers adds the Update.json
  2. When libbrabber hits their repo, it also grabs the README.*
  3. Looks for "CDN" or "jsDelivr" keywords
  4. If doesn't exist, then a PR is prepped to add instructions on how to use jsDelivr. Best as a subsection under "Usage" or "Download" if any. If not, before the "License" at the end.
  5. PR is pushed after the repo is active on the CDN.

Unfortunately, badges can't be used anymore for notifications.

Update.json file schema

When libgrabber checks if project is updated, it reads metadata from update.json. Metadata contains information like name of package, type of package manager (npm or bower), repository url (if project is not using package manager) and a list of files to be included or excluded.

For example, for a project that uses npm or bower:

{
  "packageManager": "npm" // "bower"
  "name": "lodash",
  "files": {
    "basePath": "dist/",
    "include": ["*.js"],
    "exclude": ["lodash.underscore.js", "lodash.underscore.min.js"]
  }
}

If the project is not using package manager, then the repository value identifies project location. For example:

{
  "repository": "https://github.com/lodash/lodash"
  "files": {
    "basePath": "dist/",
    "include": ["*.js"],
    "exclude": ["lodash.underscore.js", "lodash.underscore.min.js"]
  }
}

packageManager - type of package manager (npm or bower)
name - package name
repository - required if packageManager or name are not defined, refers to the project's git repository
files.basePath (optional) - base path to use when looking for resources
files.include (optional, multiple) - files or paths to include
files.exclude (optional, multiple) - files or paths to exclude

Includes/excludes might be specified using glob (see https://github.com/isaacs/node-glob).

Use NMM & bower instead of GitHub "Release"?

Besides getting project updates from github, libgrabber supports popular package managers npm and bower.

I assume now that this means NPM or Bower may be used only to flag new releases, instead of Releases?
And the files are still uploaded directly from GitHub?

Create README

We need a detailed readme for this project describing how to install and use the tool for people that want to contribute code and for the authors how to keep their projects auto-updated.

Deprications and warnings

npm WARN deprecated [email protected]: this package has been reintegrated into npm and is now out of date with respect to npm
npm WARN deprecated [email protected]: graceful-fs version 3 and before will fail on newer node releases. Please update to graceful-fs@^4.0.0 as soon as possible.
npm WARN deprecated [email protected]: Jade has been renamed to pug, please install the latest version of pug instead of jade
npm WARN deprecated [email protected]: graceful-fs version 3 and before will fail on newer node releases. Please update to graceful-fs@^4.0.0 as soon as possible.
npm WARN deprecated [email protected]: lodash@<3.0.0 is no longer maintained. Upgrade to lodash@^4.0.0.
npm WARN deprecated [email protected]: graceful-fs version 3 and before will fail on newer node releases. Please update to graceful-fs@^4.0.0 as soon as possible.
npm WARN engine [email protected]: wanted: {"node":"0.8.x"} (current: {"node":"4.4.3","npm":"2.15.1"})
npm WARN engine [email protected]: wanted: {"node":"0.8.x"} (current: {"node":"4.4.3","npm":"2.15.1"})
npm WARN engine [email protected]: wanted: {"node":"0.8.x"} (current: {"node":"4.4.3","npm":"2.15.1"})
npm WARN engine [email protected]: wanted: {"node":"0.8.x"} (current: {"node":"4.4.3","npm":"2.15.1"})
npm WARN engine [email protected]: wanted: {"node":"0.8.x"} (current: {"node":"4.4.3","npm":"2.15.1"})

test scenario: upload fixes without SemVer update?

I noticed that project.js looks at SemVer to flag updates, which is great.

Should it be in scope to have a new update be pushed without a SemVer increase? Sometimes a dev might forget a minor change, then add it & push out an update with out feeling they need to increase the SemVer.

name field is jsDelvr's folder?

name (required) - refers to package name on npm or Bower, or repo name when GitHub is used

Seems that name really reflects jsDelvr's folder name if you look at this example? If true, then I'll update.

Does libgrabber verify if files are new?

I found a project that seemed to fail to upload the new files after bumping a new release. 2 clues:

  • file dates are 2 weeks old after the new 0.3.1
  • top-line comment still reads the old 0.3.0 version.

I'd think it would be nice to at least warn of old files, so our admins won't push a non-update update when they're multitasking.

sub-subfolders listing

  • dist/*.js - copies javascript files from dist dir. Directory structure will not be retained (e.g. dist dir will be stripped when copied). To retain directory structure prepend glob with ./ e.g. ./dist/*.js

What happens if all files for consumption are inside dist, but they have a dist/vendors folder, & they want only the /vendors folder to be a directory in jsDelivr?

Follow redirects

For example this fails

{
  "packageManager": "github",
  "name": "matreshka",
  "repo": "finom/matreshka",
  "files": {
    "include": ["matreshka.min.js", "matreshka.js", "matreshka.min.map"]
  }
}

because finom/matreshka is now matreshkajs/matreshka

The bot should be able to follow the redirects and use the new repo names.

Send an email when an update fails

For example chartist changed their structure and libgrabber stopped updating it.
The only way to see the problem is to read the logs

info: Updating chartist.js to v0.4.3
info: Copied 0 files name=chartist.js, version=v0.4.3, files=[]
warn: Something might be wrong with update.json name=chartist.js, version=v0.4.3

an other example

error: Error in reading metadata file /home/jsdelivr/files/underscore.string/update.json
warn: Failed to read metadata metadataPath=/home/jsdelivr/files/underscore.string/update.json,
error: Failed to update project /home/jsdelivr/files/underscore.string/update.json
error: Failed to update project

There needs to be a way to automatically notify the admin via email about a library that fails to update

Check if remote project has tags

We accidentally added an update.json to a project that doesnt have any tagged releases. So libgrabber crashed. Can we catch this kind of errors and simply skip these projects?

Git workflow

I'm interested in hearing your opinions about how libgrabber should approach with pushing project updates to git. My idea is that libgrabber doesn't work directly with jsdelivr/jsdelivr repo, but uses its own fork (with a separate github account). For example, let say that librabber found a new version in one of the projects, then it does following things:

  1. Creates local branch <project name>/<version>
  2. Adds project files to this branch
  3. Commits to branch
  4. Pushes branch to origin
  5. Removes local branch
  6. Creates pull request to merge remote branch <project name>/<version> into jsdelivr/jsdelivr

Every time libgrabber get scheduled to run, it merges changes from upstream (jsdelivr/jsdelivr). On the next execution it will not update same version again because the pull request is accepted by @jimaek and new version is found in the file system.

If the pull request is still not accepted, then libgrabber checks if there is a remote branch (<project name>/<version>) in its own fork and skips updating project again. This works rather well in cases when there is an error in update.json or a bug in libgrabber. You could just fix update.json and delete remote branch directly from github, libgrabber will then try to update the project again.

This is implemented as of now, with the exception of doing pull requests. There are couple of branches created in test jsdelivr fork https://github.com/aleksandarabot/jsdelivr/branches.

Please tell me your suggestions and improvements to this workflow.

Faster updates with Webhooks

Lots of big projects want instant updates on our CDN.
Any ideas how to use Github's webhooks functionality to ping libgrabber every-time there is a new release and quickly update that project?

Are all files read for SemVer?

Eg: for "include": [ "thing.css", "thing..min.css", "thing.js", "thing.min.js"], if jquery.thing.js's SemVer is changed, then all files are uploaded to a new version on jsDelivr, even if the other files' versions aren't bumped? How about if a non-mainfile version is bumped like a CSS?

Could be a race condition if a PR is accepted before the min files are updated.

Include folders

https://github.com/wavded/humane-js
How do we include the themes folder?

{
  "packageManager": "github",
  "name": "humane.js",
  "repo": "wavded/humane-js",
  "files": {
    "basePath": "/",
    "include": ["humane.min.js", "humane.js", "themes"]
  }
}

Doesnt work.

Updating using metadata/home/jsdelivr/files/humane.js/update.json
 Read metadata
 { metadata:
 { packageManager: 'github',
 name: 'humane.js',
 repo: 'wavded/humane-js',
 files:
 { basePath: '/',
 include:
 [ 'humane.min.js',
 'humane.js',
 'themes' ] },
 path: '/home/jsdelivr/files/humane.js' },
 versions:
 { localVersions: [ '3.0.6', '3.1.0' ],
 remoteVersions:
 [ '1.1.0',
 '1.2.0',
 '1.2.1',
 '1.3.0',
 '1.4.0',
 '2.0.0',
 '2.1.0',
 '2.1.1',
 '2.2.0',
 '2.2.5',
 '2.2.6',
 '2.2.7',
 '2.2.8',
 '2.5.0',
 '2.6.0',
 '2.7.0',
 '2.7.1',
 '2.7.2',
 '2.8.0',
 '2.8.1',
 '3.0.0',
 '3.0.1',
 '3.0.2',
 '3.0.3',
 '3.0.4',
 '3.0.5',
 '3.0.6',
 '3.1.0',
 '3.2.0' ],
 branchVersions: [] } }
Updating humane.js to 3.2.0
Copied 3 files
{ name: 'humane.js',
version: '3.2.0',
files:
[ '/home/jsdelivr/files/humane.js/3.2.0/humane.min.js',
'/home/jsdelivr/files/humane.js/3.2.0/humane.js',
'/home/jsdelivr/files/humane.js/3.2.0/themes' ] } 

And themes/* would simply add the contents of the folder.

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.