Code Monkey home page Code Monkey logo

freecdn's Introduction

freecdn

npm version

freecdn 是一个纯前端的 CDN 解决方案,用于降低网站流量成本,同时提高网站稳定性、安全性,并且无需修改现有的业务逻辑。

最近更新

新增文件合并功能,可将大文件切割成多个小文件,运行时自动合并。从而能突破免费 CDN 单个文件体积限制。

新增资源包功能,可将多个小文件合并成一个资源包,运行时从中提取。查看更多

功能亮点

自动选择公共库

互联网上有很多免费的公共库 CDN,例如 cdnjsjsdelivrunpkg,但哪个最稳定,始终没有明确的答案。

现在你无需纠结这个问题,随意选择即可。freecdn 可根据用户的网络状况,实时切换到合适的 CDN。查看更多

全站 Hash 校验

现代浏览器可通过 SRI 校验资源完整性,降低第三方站点的风险。但 SRI 也存在一些不足,例如支持的类型太少、需要修改 HTML、缺少备用机制。

freecdn 能校验任意类型的资源,例如图片是否被篡改,从而提升内容安全;无需修改 HTML,并且所有 Hash 统一维护,方便使用和更新;即使校验失败,自动切换到备用资源,避免损坏业务。查看更多

充分利用图床

网站图片很耗流量,不少人将图片上传到图床、相册等第三方站点,充当免费 CDN。但这也存在诸多难以预测的情况,例如图片被删、限制外链、添加水印、有损压缩、限速等等。

现在你无需担心这些问题。你只需将图片备份到多个图床,freecdn 会依次尝试,直到获得预期内容。此外,不仅是图片,任意类型的文件都可通过图床加速!查看更多

网站离线运行

任何网站都无法避免网络故障,例如机房故障、DNS 故障、运营商丢包、DDOS 攻击,导致网站无法稳定访问。

freecdn 支持网站离线运行。用户只有首次访问依赖你的服务器,之后即使服务器关机,前端程序也能从备用站点加载最新的页面和资源,成为一个不依赖中心的网站。查看更多

WebP 无缝兼容

相比 JPG/PNG/GIF 等格式,WebP 有着更高的压缩率,但并非所有浏览器都支持,因此需处理兼容性。传统的无缝兼容需要后端支持,服务器根据 Accept 请求头决定是否返回 WebP 格式,从而实现同个 URL 返回不同格式的效果。

freecdn 可在前端实现这个功能,无需后端支持。查看更多

资源快速更新

HTTP 的缓存时间,一直是个头疼的问题。时间太短,性能不够好;时间太长,更新不及时。因此很多网站都有自己的资源更新方案。

freecdn 使用独特的更新机制,只需更新一个清单文件,就能更新所有资源。查看更多

功能演示

常用文档

兼容问题

freecdn 前端脚本依赖 Service Worker API,并使用了 ES2020 语法和特性。不过即使浏览器不支持也没问题,页面仍从原始 URL 加载资源。

如果你的网站本身也有 Service Worker,请参考 共享接入模式

由于 Service Worker 只能在安全环境中开启,因此你的站点必须是 HTTPS。本地测试(127.0.0.1/localhost)时可以使用 HTTP。

相关组件

前端脚本

freecdn-js:https://github.com/EtherDream/freecdn-js

页面引用的脚本体积极小,压缩后只有几百字节,最大程度减少你的网站流量。

公共库

freecdn-publib:https://github.com/EtherDream/freecdn-publib

公共库收集了十几个 CDN 站点 1000 多万条 URL 记录。

更新服务

freecdn-update-svc:https://github.com/EtherDream/freecdn-update-svc

使用更新推送服务,长缓存资源也能快速更新。

项目进展

目前基本功能已实现,处于公开测试阶段。如果你对该项目感兴趣,可以在个人博客、特效演示等站点试验,期待反馈存在的问题,并提供更好的建议。

本项目将长期维护。

新功能...

目前开发中的功能:

  • 更智能的站点选择算法(目前规则还很简单,需进一步完善)

  • 纯前端日志(通过 Service Worker 采集用户访问日志,发送到开发者提供的接口。适用于 GitHub Pages 等无法查看详细日志的站点)

  • 子集搜索(如果待搜索文件是公共资源的一部分,工具生成裁剪范围;如果公共资源是待搜索文件的绝大部分,补充缺失的前缀或后缀数据)

  • HTTP over WebRTC(将内网中的设备作为网站节点,分担流量和计算量)

License

MIT

freecdn's People

Contributors

etherdream 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

freecdn's Issues

对于没有后缀名的 Url 支持?

需求:使用 freecdn 加速自建图床的图片
当前遇到的问题:freecdn 无法生成指定 url 的 manifest 文件
使用环境:Fedora35 x64 npm 8.3.1 node v16.14.0
期望的结果:生成如下形式的 manifest 文件

https://wdv2.luckykeeper.site:44443/api/v3/file/get/15210/Grafana01.3xocpg8qrvg0.png?sign=AAGdAq59LROvoKi0nO6SEvHFnkLvVWfx8DSm5QlEGVI%3D%3A0
https://raw.github……
https://jsdevlir……
hash=……
实际的结果:没有生成 https://wdv2.luckykeeper.site:44443 对应的结果
生成使用的脚本如下:

# 自动生成 Freecdn By Luckykeeper For LuckyBlogV2
# Last Update 2022-03-03

# 删除以前数据
freecdn db --del-all

# 导入比对 Urls
files=$(find * -type f ! -path "freecdn-*" ! -name ".*")
list=""

for file in $files; do
  hash=$(openssl dgst -sha256 -binary $file | openssl base64 -A)
  list="$list

$hash https://cdn.jsdelivr.net/gh/luckykeeper/[email protected]/$file
$hash https://raw.githubusercontent.com/luckykeeper/LuckyBlog_RS/1.028/$file"

done

# 输出比对列表

echo "$list"

# 导入比对列表到数据库
freecdn db --import <<< "$list"

# 输出当前数据库内容
freecdn db --list

# 把 wdv2 的列表和上面的列表比对,生成加速清单
freecdn find --save --with-urls urls.txt

# 生成加速 js
freecdn js --make

echo "生成成功!"

进行的其他尝试:已经按照 https://github.com/EtherDream/freecdn/tree/master/examples/cdn-fallback#搜索资源 提供的方法进行尝试,将 freecdn 此示例提供示例 js 文件 url 放到 urls.txt 最前面,发现能够正常生成 https://ajax.cdnjs.com/ajax/libs/jquery/3.2.1/jquery.js 的加速清单文件,但是不能正常生成自建图床 https://wdv2.luckykeeper.site:44443 的图片加速文件
合理怀疑:由于生成清单脚本运行速度过快,猜测对于这个没有后缀名的链接,形如 https://wdv2.luckykeeper.site:44443/api/v3/file/get/15210/Grafana01.3xocpg8qrvg0.png?sign=AAGdAq59LROvoKi0nO6SEvHFnkLvVWfx8DSm5QlEGVI%3D%3A0 ,freecdn 直接进行了跳过处理,没有访问计算哈希

附:生成用的 urls.txt 文件
urls.txt

图床支持不太友好

我看目前提供的方案都是将本地图片上传到 GitHub,然后使用公共 CDN 链接。如果我的博客中的图片已经使用了图床(例如 jsdelivr)该怎么办?该怎么实现自动选择?

在 freecdn-manifest.txt 使用真实url

希望以种类似这种方式加载文件, 因为我图片是和主站分离的, 这样即使用户javascript没开,那么像 <img src="url"> 还可以正常加载

https://cdn.jsdelivr.net/npm/[email protected]/lib/typed.min.js
	https://ajax.cdnjs.com/ajax/libs/typed.js/2.0.12/typed.min.js
	https://cdnjs.cloudflare.com/ajax/libs/typed.js/2.0.12/typed.min.js
	hash=UVQ5HtHTmtqBT3KY5cd4AvI47pp0gJwIM+q45HD9oLk=

但是目前好像只能以如下方式加载文件

/js/main.js
         https://example.com/test/a.js
         hash= ......

关于使用方式与教程?

翻看了下文档,没有太搞明白如何部署到生产环境中,作者啥时候搞个详细教程或者tg群啥的?

一些小问题

首先是npm上传脚本似乎不能识别已经上传了的文件, 就算服务端返回404还是提示files existed.
同时建议添加npm.elemecdn.com.
再有, 我利用freecdn打包了一批静态文件, 然后把生成的js和清单文件都复制到另外一个站点使用, 我不清楚是不是有限制, 一部分由静态文件内的js发起的请求并没有被serviceworker过滤而是直接访问了实际站点. 请问有什么解决方案.
注: 假设js请求的站点是A, 我实际访问的站点是B, js请求A获取同样已经通过freecdn处理上传到npm库了的css文件等没有被过滤
又注: 我希望打包好文件后只需要复制生成的js文件和清单文件就可以复用, 因为jsdelivr众所周知挂了, 我在用live2d, 模型文件比较耗流量. 发现npm.elemecdn.com直接反向代理了unpkg.com, 如果能解决掉这个问题就好了

图示
B站点
image
A站点直接访问A站点测试live2d的页面
image

bundle新功能pack资源包格式和原理

资源包格式很简单:

一个 JSON,记录每个文件的 HTTP 响应头(其中 content-length 字段必选,记录文件长度)

分隔符 \r (0x0D)

所有文件合并后的数据(按 JSON key 的顺序)
,"img/emoji/1f301.png":{"content-length":1089},"
如果打包的目录为 assets,那么文件 assets/1.gif 在资源包中的路径为 1.gif(无 / 前缀)。

看了一遍文档资源合并就是sw解析json对象
合并后的资源包json文件格式

{
"img/emoji/1f301.png":{"content-length":1089},"
…
1f648.png
1f649.png
…
二进制
…
}

还不如以下k:v格式简单粗暴来的好

{
"img/emoji/1f301.png":{"二进制"}
…
}

而且js css html这些文件是源代码,并没有压缩,传输方面也不合适吧

/emoji-icons/assets/
	bundle=/icons.fcpkg

/icons.fcpkg
	https://npm.elemecdn.com/[email protected]/index.js
	https://unpkg.com/[email protected]/index.js
	https://cdn.jsdelivr.net/npm/[email protected]/index.js
	hash=1048576;onDac6KIBKj+JIjn026vj8rI6SqPI1fCGcTRZUExqJk=,sXv/uBmv4WzCCIjfaCCXccQRkwG6i3/GodF8l9El6SY=
	size=1090870

当然文档没说.fcpkg文件支不支持br=on
如果支持br=on倒是合并什么文件都没关系

但就这个json格式的设计感觉原本简单的k:v可以胜任为何要复杂化

{
k:文件长度
…
文件名
…
文件二进制
…
}

造成资源计算浪费?

另外合并功能是
首次下载完json后一次性全部提取出来缓存浏览器,让清单原文件每次有请求时就直接返回文件?

还是

每次清单原文件请求时每次都解析json文件提取其中的文件返回?

如果每次取文件都要解析一次json
假如json文件5M
为了取一次20k的html解析一次5m的json?
为了取一次50k的css又解析一次5m的json?
为了取一次10k的js又解析一次5m的json?

该功能没发布前我还以为会采用类似JSZip tar.gz等通用的压缩功能编译成WebAssembly,这样就可以使用阿里淘宝/腾讯云/华为云等厂商提供的npm镜像加速服务
https://registry.npmmirror.com/
https://mirrors.cloud.tencent.com/npm/
https://mirrors.huaweicloud.com/repository/npm/

除了众多国内npm镜像仓库还有国内docker仓库镜像可以利用上

清单备用url优化

/index.html
https://cdn.jsdelivr.net/gh/zjcqoo/[email protected]/index.html

备用地址完全可以用变量优化

@jsd = https://cdn.jsdelivr.net/gh/zjcqoo/test@
@unpkg = https://unpkg.com/free-host@

/index.html
{jsd}0.0.1/index.html
{unkpg}0.0.0-1lZrk8wvUYweiDaC/index.js

视清单备用地址数量预计减少30-80%以上的代码大小

另有一个问题
hash=1lZrk8wvUYweiDaCPB/LayLG2X8jh/7ln9l66boQVMU=
哈希值文档说文件sha256算法后base64编码
为何用其他语言如php对这个哈希值进行base64解码不成功,解码后是乱码

一个文件的sha256算法后字数也不是这么短,再用base64编码后字数更长,难道freecdn cli生成的哈希值不是通用标准?

还有一个疑问
第三方图床cors跨域问题
正常一个图片大多数是检测ref来路,很少检测cors
freecdn拦截请求时比如 img src=xxx 可不发出ref来路,为什么又要求对方站点开放cors?

我的个人理解是不是
freecdn可以拦截 img src=xxx 的请求和修改请求头信息比如head但无法对该请求返回的内容进行控制修改

freecdn要想达到拦截请求又可以修改返回的内容比如截取后300字节的内容,url/a.jpg?pos=300
是不是就必须要对方站点开启cors跨域共享,不然freecdn就无法获取这个返回的对象,然而就无法对这个对象进行pos截取操作?

是否有考虑过添加到构建过程中的方案?

例如支持webpack和rollup,通过构建生成的产物来生成loader

这样做有几个好处:
1、可以编辑html输出来将loader放在最开始加载
2、构建过程可以读取依赖包列表,转换成对应的cdn源

【需求建议】针对特殊图床的优化

对于某些特殊图床,导入db后 freecdn 会返回同一 hash 值,希望作者如果有精力的话能够优化一下,谢谢!
脚本参考 #5 提供的脚本,在导入数据库时增加了形如以下的内容
$hash https://oa.hbfu.edu.cn/backstage/filecenter/file/main::e3b63dc59edc4280b635fb75f6ea08cc
以这样的方式一个个导入了文件
但是在运行时得到了如图的结果,返回的 hash 值一致
多个链接返回同一哈希
该图床后端使用 hdfs ,这种情况应该会某些奇奇怪怪的情况下产生(当时我上传预览的时候发现在系统内拖动链接在浏览器新标签页打开会是一个文件,点击链接打开就不会)
给出几个链接以供测试
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::e3b63dc59edc4280b635fb75f6ea08cc
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::613aed580b8349e2ad59d360bc961622
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::3f3eabc825fe425791ad24fd38b31d45
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::38ce09a1346a4b559c52b58d824e2119
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::676fdee7ca554d17bf6502491e671ad3
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::7e1dceb62dca475da35b7f1de246ea1c
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::e19da4272f1846bc837cdc8eddee9b3b
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::675951283bb64eca89d10ae7f7a301b6
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::97cdab1bb79a4e5e844700257082a876
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::d28c77fbcc5f4cad81ed5014887129a0
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::58bb563e0e4b40208e427bf358804093
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::b785ce866f9d4325b3f99de13a698de6
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::7ba6d4fd96f24e3b909d62db72f76dfb
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::d011c5e00c0e4adab0a5fc344dbba35d
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::a6d94078520441f2a9211cb5272b613d
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::7d63963a98d7494684987d31f8a6f53f
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::96da630c08e4406695c8711b144b8d0b
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::13a1f43137ca44eba731e4863d2a7366
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::f678d24b45304822bf15991fece08b47
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::975d9f92eb8746d1bc30d5ffbd1bf44d
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::0df1f73d28c3471c9c3d194959c1fece
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::6f1c928488544fbeadcbc38460fa0b3e
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::b29bf90bf88a48a48bfc97f79733b286
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::8291ac3af736430a838ca878750a3efe
https://oa.hbfu.edu.cn/backstage/filecenter/file/main::1d965a13755a424ca809f8e639d1e229

支持更高版本的node.js

在node.js的 16.17.1 LTS 版本和 18.10.0 Current 版本中使用共享接入均会报错

node:internal/per_context/domexception:53
    ErrorCaptureStackTrace(this);
    ^
DOMException [SyntaxError]: Usages cannot be empty when creating a key.
    at new DOMException (node:internal/per_context/domexception:53:5)
    at __node_internal_ (node:internal/util:509:10)
    at SubtleCrypto.generateKey (node:internal/crypto/webcrypto:148:11)
    at async genKey (C:\Users\XCwosjw\AppData\Roaming\npm\node_modules\freecdn\lib\cmd-key.js:43:18)
    at async init (C:\Users\XCwosjw\AppData\Roaming\npm\node_modules\freecdn\lib\cmd-key.js:18:9)
    at async getPublicKey (C:\Users\XCwosjw\AppData\Roaming\npm\node_modules\freecdn\lib\cmd-key.js:91:9)
    at async getPublicKeyB64 (C:\Users\XCwosjw\AppData\Roaming\npm\node_modules\freecdn\lib\cmd-js.js:16:20)
    at async setupSwOption (C:\Users\XCwosjw\AppData\Roaming\npm\node_modules\freecdn\lib\cmd-js.js:79:23)
    at async Command.run (C:\Users\XCwosjw\AppData\Roaming\npm\node_modules\freecdn\lib\cmd-js.js:110:9)

但在node.js 16.10.0 版本不会出现此问题

提些建议

一:建议hash直接使用文件md5,32位省空间,没特殊符号 / =等,使用sha256编码再base64编码会有/ =符号,这导致本地好多图片下载时本身就以md5文件名保存,再使用freecdn编码后很不方便

二:建议清单、sw.js等在freecdn cli增加常量配置可以自定义位置,比如/sw/sw.js,因为很多开发并非有根目录权限,或与根目录的原本sw服务冲突

三:站点权重,开发时基本用什么库都是本地或引用国内公共库,字节,360, 七牛等等,国外真不建议使用,当然有人面向国外用户肯定有需要,所以建议有个站点权重由自己来选择

四:建议开发数据加密功能,比如有同行想爬取本站数据,看到请求一个html地址居然是请求一个图片,对这个图片有疑问肯定会下载图片来研究,这时图片里面的代码没加密,对方很容易获得原数据

五:希望多出一些使用案例,比如看了目前写的css或 js伪装图片合拼,但想把所有css js文件都合拼一个文件进行压缩再伪装图片
1.css取url/img.jpg#pos=555 ,
2.js取url/img.jpg#pos=888等等来减少请求

如果想实现全站html本地化,假如有1w个html,请求1w次显然用户体验不好,后台上传1w个html到图床获取1w个备用地址,或5个/10个图床备用地址,后台操作效率显然低下,再要保存这么多备用地址 清单文件肯定又大大增加
假如可以合拼1w个html为一个文件进行压缩再伪装图片上传图床,用户请求就变成1次而非1w次,请求1次后浏览器有缓存就可以拦截,对拦截的url提取这1个文件里面的指定位置即可

六:建议增加支持一些中间件数据库mongo redis等来让 (真后端) 服务器来对接freecdn使用,毕竟freecdn只算是前端工具,真正的数据管理系统还是在后端这边的,比如后端24小时爬取数据,下载图片,上传图床,爬取完成后只需把图片md5哈希和图床地址保存到redis哈希表或set表即可,效率高,跨服务器共用

七:开发用户异步定时后台脚本
比如前端爬虫,用浏览器用户ip突破目标服务器访问限制,https://juejin.cn/post/6844903767226351623
文中提到的第三种postMessage很适合用来前端爬虫,用iframe调用远程网页不跨域,但默认是获取不到dom对象,因为跨域,但可以用sw.js在远程网页拦截后插入postMessage脚本获取整个dom再post给自己

最后建议开个qq交流群

执行异常:Cannot read property 'subtle' of undefined

执行 freecdn js -save 命令抛出了异常,貌似是和 crypto 不兼容?
我的 nodejs 版本是 v14.14.0
异常内容如下:

F:\IntelliJProject\halo-theme-dream>freecdn js -save
F:\IntelliJProject\halo-theme-dream\node_modules\freecdn\lib\cmd-key.js:8
const subtle = crypto_1.webcrypto.subtle;
                                  ^

TypeError: Cannot read property 'subtle' of undefined
    at Object.<anonymous> (F:\IntelliJProject\halo-theme-dream\node_modules\freecdn\lib\cmd-key.js:8:35)
    at Module._compile (internal/modules/cjs/loader.js:1063:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
    at Module.require (internal/modules/cjs/loader.js:952:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at Object.<anonymous> (F:\IntelliJProject\halo-theme-dream\node_modules\freecdn\lib\cmd-manifest.js:5:13)
    at Module._compile (internal/modules/cjs/loader.js:1063:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)

似乎不支持火狐浏览器

我的博客地址为xingpingcn.top博客源码。大部分图片使用cdn.jsdelivr.net加速,测试中已经屏蔽cdn.jsdelivr.net,chromium内核浏览器似乎没有问题,火狐浏览器(117.1.0)在cdn.jsdelivr.net失效的情况下不能切换备用源。

关于css内引用google的问题.

我使用semantic-ui这个css框架.这个框架里默认是开启引用google的字体的.
我自己用都是关闭,再次构建出来再使用.
如果使用freecdn.这个情况会自动匹配到"坏"的cdn上吗?

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.