soveran / cuba Goto Github PK
View Code? Open in Web Editor NEWRum based microframework for web development.
Home Page: http://cuba.is
License: MIT License
Rum based microframework for web development.
Home Page: http://cuba.is
License: MIT License
Hi!
Currently, the HTTP/2 protocol specification defines secure headers, which are security-related headers, in uppercase, which is not consistent with the specification since HTTP/2 normally treats headers in lowercase.
Also, since secure headers are currently defined in uppercase, some Ruby application servers, such as Rack, treat this as a lint error or validation error, which prevents the web application from starting properly.
I would like to propose that secure headers be redefined in lowercase to match the HTTP/2 specification. I believe this will improve consistency in the context of the HTTP/2 protocol and avoid compatibility and application server launch issues.
Currently, the following workaround is available, but I believe that redefining it in lowercase would be a more desirable form.
res.headers.keys.each do |key|
res.headers[key.downcase] = res.headers.delete(key)
end
Specific changes:
I would like to make a correction to redefine the following in lower case.
https://github.com/doridoridoriand/cuba/blob/change-secure-headers-to-lowercase/lib/cuba/safe/secure_headers.rb#L29-L36
References:
https://github.com/rack/rack/blob/main/lib/rack/lint.rb#L655-L656
https://datatracker.ietf.org/doc/html/rfc9113#name-http-fields
Regards,
The setting of the 'Strict-Transport-Security' security header is by default set to:
"Strict-Transport-Security" => "max-age=631138519; includeSubdomains; preload"
The use of this header is good, but for a default setting (encouraged to use in the docs with no explanation of its repercussions) it is far to strict and could cause a developers other websites to inadvertently become inaccessible for long periods of time to any browser that visits the SecureHeader Cuba app over HTTPS.
In detail:
max-age=631138519;
This is good to set, but it is set for a very long period for a developer that may only be in testing mode. This value represents 20.013 years. That means that any browser that successfully visits that site will never allow it to be visited over a non-secure HTTP connection for at least 20 years. The only way to reset is for the server to set it to '0' explicitly and then the browser user has to visit the site again. I would suggest a more reasonable value of 1 month, or 2628000 seconds.
includeSubdomains
This is an optional value and it has potentially far-reaching effects and should not be included in a default setup. By setting this, you are telling every browser that visits this site over an HTTPS connection successfully (even once) that ALL SUBDOMAINS of this domain shall also be REQUIRED to visit over an https connection. So for example, if there is a pre-existing www.example.com
website which does not have a TLS certificate, and then the developer of this Cuba mini-app decides to setup this secure little mini-site on HTTPS. From now on every browser that visited the mini-site will refuse to load the www site over simple HTTP since it is not protected by TLS!! This (spec optional) setting should be removed from Cuba's defaults.
preload
preload is another optional part of the spec, and is dangerous to include in a default setup. By having this set you are requesting (or allowing to be requested) that this domain be permanently hard-coded into the Chrome browser (and others who use their service) that they should only be allowed to see the site over HTTPS. This setting, when present, might allow an attacker to add the site on the HSTS preload site (https://hstspreload.appspot.com) and force all Chrome users to only allow access to this domain, and all sub-domains, over HTTPS. This option should also be removed from the defaults.
All of this is well documented here:
https://developer.mozilla.org/en-US/docs/Web/Security/HTTP_strict_transport_security
Twitter Secure Headers project, also does not use the optional attributes:
https://github.com/twitter/secureheaders
You should also include instructions for how to override specific security settings as appropriate for a Cuba app in the docs. I am doing so in my Cuba app with something like this which seems to be working as expected:
require 'cuba'
require 'cuba/safe'
...
Cuba.plugin Cuba::Safe::SecureHeaders
Cuba.settings[:default_headers].merge!({"Strict-Transport-Security" => "max-age=2628000"})
...
Cuba.define do
...
end
Hi @soveran,
With the last version, I have
/usr/src/app # bundle exec puma
Puma starting in single mode...
* Puma version: 6.1.0 (ruby 3.2.1-p31) ("The Way Up")
* Min threads: 0
* Max threads: 5
* Environment: development
* PID: 7
! Unable to load application: LoadError: cannot load such file -- rack/session
bundler: failed to load command: puma (/usr/local/bundle/bin/puma)
<internal:/usr/local/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:37:in `require': cannot load such file -- rack/session (LoadError)
from <internal:/usr/local/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:37:in `require'
from /usr/local/bundle/gems/cuba-4.0.0/lib/cuba.rb:3:in `<top (required)>'
from <internal:/usr/local/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:37:in `require'
from <internal:/usr/local/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:37:in `require'
from /usr/src/app/app.rb:3:in `<top (required)>'
from config.ru:3:in `require_relative'
from config.ru:3:in `block in <main>'
from /usr/local/bundle/gems/rack-3.0.4.1/lib/rack/builder.rb:103:in `eval'
from /usr/local/bundle/gems/rack-3.0.4.1/lib/rack/builder.rb:103:in `new_from_string'
from /usr/local/bundle/gems/rack-3.0.4.1/lib/rack/builder.rb:94:in `load_file'
from /usr/local/bundle/gems/rack-3.0.4.1/lib/rack/builder.rb:64:in `parse_file'
from /usr/local/bundle/gems/puma-6.1.0/lib/puma/configuration.rb:365:in `load_rackup'
from /usr/local/bundle/gems/puma-6.1.0/lib/puma/configuration.rb:287:in `app'
from /usr/local/bundle/gems/puma-6.1.0/lib/puma/runner.rb:158:in `load_and_bind'
from /usr/local/bundle/gems/puma-6.1.0/lib/puma/single.rb:44:in `run'
from /usr/local/bundle/gems/puma-6.1.0/lib/puma/launcher.rb:189:in `run'
from /usr/local/bundle/gems/puma-6.1.0/lib/puma/cli.rb:75:in `run'
from /usr/local/bundle/gems/puma-6.1.0/bin/puma:10:in `<top (required)>'
from /usr/local/bundle/bin/puma:25:in `load'
from /usr/local/bundle/bin/puma:25:in `<top (required)>'
from /usr/local/lib/ruby/3.2.0/bundler/cli/exec.rb:58:in `load'
from /usr/local/lib/ruby/3.2.0/bundler/cli/exec.rb:58:in `kernel_load'
from /usr/local/lib/ruby/3.2.0/bundler/cli/exec.rb:23:in `run'
from /usr/local/lib/ruby/3.2.0/bundler/cli.rb:491:in `exec'
from /usr/local/lib/ruby/3.2.0/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
from /usr/local/lib/ruby/3.2.0/bundler/vendor/thor/lib/thor/invocation.rb:127:in `invoke_command'
from /usr/local/lib/ruby/3.2.0/bundler/vendor/thor/lib/thor.rb:392:in `dispatch'
from /usr/local/lib/ruby/3.2.0/bundler/cli.rb:34:in `dispatch'
from /usr/local/lib/ruby/3.2.0/bundler/vendor/thor/lib/thor/base.rb:485:in `start'
from /usr/local/lib/ruby/3.2.0/bundler/cli.rb:28:in `start'
from /usr/local/lib/ruby/gems/3.2.0/gems/bundler-2.4.6/libexec/bundle:45:in `block in <top (required)>'
from /usr/local/lib/ruby/3.2.0/bundler/friendly_errors.rb:117:in `with_friendly_errors'
from /usr/local/lib/ruby/gems/3.2.0/gems/bundler-2.4.6/libexec/bundle:33:in `<top (required)>'
from /usr/local/bin/bundle:25:in `load'
from /usr/local/bin/bundle:25:in `<main>'
seems that rack-session is missing.
Regards,
Related to the-benchmarker/web-frameworks#6148
I'm newbie for cuba framework.
Is there any api to log in log file like logger.debug in RoR?
Given I have a form on my website where I use a <select name="players" multiple>
, my browser will send a POST request with the application/x-www-form-urlencoded
request body "players=2&players=3". As you can see, it contains the key players
twice.
If I now get the value of req.POST
I get the value {"players"=>"3"}
. The second occurrence of the key overwrote the first one. The only place where I can see all selections is in req.env["rack.request.form_vars"]
. Here I will get the string "players=2&players=3"
.
So if I want to access the entire selection of the user, I will need to parse the form_vars
by hand.
Is there any other solution? This also seems to be at the Rack level, right? So this is probably not even in the source code of Cuba. But maybe someone has a solution for it?
I am trying to mount a Cuba app with Rack builder as follows:
builder = Rack::Builder.new do
use Rack::AuthMiddleware, :users => Tokens
use API
run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['All responses are OK']] }
end
But apparently, a Cuba constructor takes no arguments, and I get a:
/Users/pmu/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/cuba-3.1.0/lib/cuba.rb:102:in `initialize'
/Users/pmu/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rack-1.4.4/lib/rack/builder.rb:82:in `new'
/Users/pmu/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rack-1.4.4/lib/rack/builder.rb:82:in `block in use'
/Users/pmu/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rack-1.4.4/lib/rack/builder.rb:130:in `[]'
/Users/pmu/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rack-1.4.4/lib/rack/builder.rb:130:in `block in to_app'
/Users/pmu/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rack-1.4.4/lib/rack/builder.rb:130:in `each'
Any ideas how to get this working?
header
API was introduced in bf5f628, but with no examples or instructions on how to use it and also no tests of it.
Actually from the name header
, we assume it's used to ensure the request has some header with it, but http header in the env hash should be start with HTTP_
prefix. So the correct implementation might be
ENV_HTTP_HEADER_PREFIX = "HTTP_".freeze
def header(key)
lambda { env["#{ENV_HTTP_HEADER_PREFIX}_#{key}".upcase.tr("-","_")] }
end
Or the user has to call header('HTTP_KEYNAME')
if he/she wants to make sure the KEYNAME
exist in the header.
So, as I mentioned early, since there are no examples, instructions and tests on how to use it. Should cuba just remove it?
Or if you are concerned about breaking the backward compatibility, I will be happy to contribute some docs and tests on it.
Hi! I'm one of the many users that love Cuba. I also have the pleasure to know you and enjoy all of your work and products but now I want to show my gratitude in a public manner.
This OT and useless issue it's just my dumb way to say thank you for the work you do, the support to the community and the lesscode initiative that makes a bunch of us happy.
Next time I'll buy you a coffee and give you a hug.
I would like to extract a partial with some html from my layout.mote.
I was unable to find a easier solution than this:
layout.mote
...
{{ this.partial('mypartial') }}
...
then inside views/mypartial.mote i have some html content.
<p>This is the content of my partial.</p>
Is this the right way to do it?
BTW I like this little framework A LOT.
π Thx for the nice job π
On chrome inputting a custom res.staus stops page from rendering html/css/js properly and
just prints the html in plain text though it shows the right status.
here is some of my code that displays incorrectly on chrome (works in firefox and edge):
class Login < Cuba; end
Login.define do
on get do
on root do
res.status = 401
res.write view('/login')
end
end
I'm trying to serve static files from Cuba using Rack::Static @ ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]:
Cuba.use Rack::Static, :urls => %w("/css" "/js"), :root => "public"
But I keep on getting 404s. I tried all combinations - removing :root, moving /css from public/ to /, using :urls => { "/css/style.css" => ... using string keys instead of symbols in Rack::Static configuration ... - but nothing seams to work.
Thanks !
I just followed the instruction from The Guide to Cuba, when I do "dep install", dep only install the dependency in .gems file and the gems defined in .gems.dev are not installed, do I need to install those gems manually or I can pass some argument like "dep install --develpment"?
Hi, I have a question regarding optional query parameters. I have the following routes defined:
Cuba.define do
on get do
on 'movies' do
on param("genre"), param("offset"), param("limit") do |genre, offset, limit|
...
end
end
end
end
But this route only matches when exactly all query params are passed and I would like this route to match even if none are passed or just some. I mean, make these parameters are completely optional. Is there any way to achieve that in a simple way instead of using req.env["rack.request.query_hash"]
directly?
Thanks
Hello,
I was just playing with Cuba, and I would need to render JSON in the response for simulating API calls. However, I can't quickly see in the docs, how you responds with non-HTML.
Any pointers?
Thanks!
The Content-Type
isn't being set when a 404
occurs on our application, which means that plain text is rendered: http://cl.ly/image/1f401k340S29
Is this the intended default behavior?
I believe that the way it works is all 200's have a default Content-Type
of text/html
if not otherwise set, but other status codes have no Content-Type
at all (see here)
Hi @soveran! π
I noticed that to consume segments a regex is created each and every time the segment tries to be matched:
Line 214 in f661096
So I made this benchmark:
require_relative "./lib/cuba"
Cuba.define do
on "users" do
on ":id" do |id|
on root do
res.write "User #{id}"
end
on "projects" do
on ":project_id" do |project_id|
res.write "User #{id}, Project #{project_id}"
end
end
end
end
end
env1 = { "PATH_INFO" => "/users/1", "SCRIPT_NAME" => "/" }
env2 = { "PATH_INFO" => "/users/1/projects/2", "SCRIPT_NAME" => "/" }
time = Time.now
30_000.times do
Cuba.call(env1)
Cuba.call(env2)
end
puts Time.now - time
On my machine it takes about 4 seconds to complete.
Now I cache the regexes with this diff:
diff --git a/lib/cuba.rb b/lib/cuba.rb
index b12e9f3..b4090fe 100644
--- a/lib/cuba.rb
+++ b/lib/cuba.rb
@@ -5,6 +5,7 @@ class Cuba
EMPTY = "".freeze
SEGMENT = "([^\\/]+)".freeze
DEFAULT = "text/html; charset=utf-8".freeze
+ REGEXES = Hash.new { |h, pattern| h[pattern] = /\A\/(#{pattern})(\/|\z)/ }
class Response
LOCATION = "Location".freeze
@@ -211,7 +212,7 @@ class Cuba
private :try
def consume(pattern)
- matchdata = env[Rack::PATH_INFO].match(/\A\/(#{pattern})(\/|\z)/)
+ matchdata = env[Rack::PATH_INFO].match(REGEXES[pattern])
return false unless matchdata
I run the snippet above and it now takes 2 seconds.
Twice as fast!
Do you think this is a good change? I think it's harmless: the routes of an app are fixed (not dynamic) so that REGEXES hash will reach a reasonable maximum size. Plus it can help with routes like ":id" where they might be used in several places.
What do you think?
Hi @soveran , first congrats for all your projects, I have been used many of them for a lot of projects, cuba, ost, micromachine, etc...
I want to invite to you to add some projects to beerpay.io, so I can support them , and retribute at least some of everything you do for us
about Beerpay, it is a free plataform for support open source project and help them to grow up, I'm inviting to all people, who has projects that I'm using, for support them
thanks for all!
Publish updated README to your Github pages. I used res.write render(..)
and it double rendered my content because the docs are old.
Rails has a great CLI interface for starting off new applications and it also has support for generating controllers, models, views and schemas.
Is something like this already in plan for Cuba? I understand that it is planned for a micro-framework and it does a good job with that but something like a cuba new my-new-app
would be a great addition.
List of new commands -
cuba new path/to/my-new-app
cuba init
cuba generate my-controller
This will create a new controller and create a new test file based on the config. No views and models will be generated.If a feature of this sort is welcome, I would love to work on it.
It seems Cuba is not forwarding kargs to the app, so this is not working for me:
API.use MyMiddleware, foo, bar: 'baz'
I can create a PR to forward kargs if that makes sense. Something like this:
def self.use(middleware, *args, **karsg, &block)
app.use(middleware, *args, **karsg, &block)
end
It would be awesome to have a sort of rake routes
to have the list of the defined URL that the Cuba app can match on.
If you give me some pointers into the code I can try to code it myself and submit a PR.
Thanks!
/Library/Ruby/Site/2.0.0/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load such file -- cutest (LoadError)
from /Library/Ruby/Site/2.0.0/rubygems/core_ext/kernel_require.rb:55:in `require'
from /Library/Ruby/Gems/2.0.0/gems/cuba-3.1.1/lib/cuba/test.rb:2:in `<top (required)>'
from /Library/Ruby/Site/2.0.0/rubygems/core_ext/kernel_require.rb:135:in `require'
from /Library/Ruby/Site/2.0.0/rubygems/core_ext/kernel_require.rb:135:in `rescue in require'
from /Library/Ruby/Site/2.0.0/rubygems/core_ext/kernel_require.rb:144:in `require'
Not sure if this is the right forum for this, but The Guide to Cuba doesn't appear to be available - It looks like the domain has expired. Is this available somewhere else?
Hi, I'm having trouble using render
from Cuba::Render - I get a LocalJumpError "no block given (yield)".
My setup is as follows:
# app.rb
require 'cuba'
require 'cuba/render'
require 'haml'
require 'tilt/haml'
Cuba.plugin Cuba::Render
Cuba.settings[:render][:template_engine] = 'haml'
Cuba.define do
on root do
render 'dashboard'
end
end
-# views/layout.haml
!!!5
%html
%head
%title Title
%body
= yield
-# views/dashboard.haml
%h1 Dashboard
Accessing the root page gives the LocalJumpError as above.
Hi,
Would make sense to allow empty strings as a default param value? While it makes sense to prevent empty strings from being captured from a request, the current implementation discards both empty params and empty defaults as they pass through the control flow. See: https://github.com/soveran/cuba/blob/master/lib/cuba.rb#L264
Would you be open to a pull request addressing this or would you guys be interested in tackling this small change?
I have a PORO JSON presenter/serializer wrapping an instance of data object, which together return validation errors. I'd like Cuba to pass the empty strings to my presenter/object, and could do so if I could set empty strings as my default. Currently, I'd have to have a second on true
block that I'd rather not have cluttering up my code.
Would be nice if we had an API for sending files (using Rack::File
?) which would set proper headers and stuff.
For example I have this
on default do
run V1::Settings
run V1::Setups
end
Currently that does not work, I have a bunch of endpoint on each Cuba class and I'd like to mount them using only run
method.
I'm not pretty sure but Grape mount the endpoints like my approach using the mount
method.
/Ruby27/lib/ruby/gems/2.7.0/gems/rack-2.2.3/lib/rack/session/cookie.rb:155:in <class:Cookie>': undefined method
DelegateClass' for Rack::Session::Cookie:Class (NoMethodError)
hele_world.rb
require "cuba"
require "cuba/safe"
Cuba.use Rack::Session::Cookie, :secret => "__a_very_long_string__"
Cuba.plugin Cuba::Safe
Cuba.define do
on get do
on "hello" do
res.write "Hello world!"
end
on root do
res.redirect "/hello"
end
end
end
Create a simple generator with basics configurations, default ones commented.
cuba new <projectName>
Creates:
That's it.
It keeps it small and speed up the dev process when using cuba for most use cases.
I came to a situation where I needed to start a Cuba application by myself not using rackup
or a config.ru file at all. Would be nice if we had an option to do so.
Problem running tests is that none of my queries are all of sudden not authorized b/c of missing csrf token in headers. Is there a way to simply disable it ? Or actually set a csrf token on the header for every request made in test mode ?
Rails 5 and Rack 2 are out. Cuba is locked to Rack 1.6. Any plans for an upgrade?
will cuba support rack 3.0?
here some of the changes that effect cuba.
i've included links to rack 3.0's changelog and upgrade guide as a connivence.
change log
https://github.com/rack/rack/blob/3.0.0/CHANGELOG.md
upgrade guide
https://github.com/rack/rack/blob/3.0.0/UPGRADE-GUIDE.md
β¦and Cuba::Response
is setting it regardless of the status code.
Rack::Lint
complains rather loudly about it.
I suppose the simplest solution is to do something similar to what was done for the status_code being 404 by default, by initially not setting the Content-Type
, and only doing so when finishing the response if the status code is anything but 204/205/304.
Thoughts, @soveran @frodsan @djanowski @cyx?
I haven't ever done this for a project, and don't know if there is some internal CI for cuba, but it would be useful to have, even if just for testing pull requests. Is anyone working on this? If not, I'll take a look tomorrow.
Cuba.settings[ :state ] = Proc.new { "ok" }
class A < Cuba; end
just does not work. some with singletons, lambdas, methods, etc
I solved the problem for myself with this plugin
https://github.com/mkristian/cuba-api/blob/master/lib/cuba_api/config.rb
which does implement a configuration with inheritance on class level:
Cuba.plugin CubaApi::Config
Cuba[ :state ] = Proc.new { "ok" }
class A < Cuba; end
and the value of can be changed later and A will see the change as well.
Cuba.plugin CubaApi::Config
Cuba[ :state ] = "ok"
Cuba.settings[ :state ] = "ok"
class A < Cuba; end
Cuba[ :state ] = "not-ok"
Cuba.settings[ :state ] = "not-ok"
p A[:state]
> "not-ok"
p A.settings[:state]
> "ok"
making a deep copy during class instantiation is also tricky since order of when the class gets defined does matter.
class A < Cuba; end
Cuba.settings[ :state ] = "ok"
class B < Cuba; end
p A.settings[:state]
> nil
p B.settings[:state]
> "ok"
although A and B are essentially the same class but were defined at different states of Cuba. having those A and B in their own files and require them makes it even harder to understand what is going on
require 'a'
Cuba.settings[ :state ] = "ok"
require 'b'
p A.settings[:state]
> nil
p B.settings[:state]
> "ok"
again using the cuby_api/config plugin makes the code more intuitive.
require 'a'
Cuba[ :state ] = "ok"
require 'b'
p A[:state]
> "ok"
p B[:state]
> "ok"
since intuition varies from person to person I would like to know other opinions.
BTW I would happy to prepare pull request to get something like cuby_api/config into Cuba and deprecate the current settings.
finally need to mention that I do like this little cuba framework a lot - I am about to switch my rails-api server to cuba/cuba_api and the new setup is so much easier to understand. I can understand the whole lot (including cuba) by reading half an hour code.
on get do
on 'upload' do
res.write '<form action="/multipart" enctype="multipart/form-data" method="post" ><input name="file" type="file" /><input type="submit" value="upload" /></form>'
end
end
on post do
on 'multipart' do
tmpfile = param["file"][:tempfile]
filename = param["file"][:filename]
target="public/upload/#{filename}"
File.open(target,'wb') {
|f| f.write(tmpfile.read)
}
end
end
I was asking in #cuba.rb on this. The channel consensus seems to be that on param() matching should match blank (non-nil) param values. That is how the code behaves now.
However, the README, example, and comments seem to imply it's for checking the presence of a real, non-blank value. I'm confused which it is.
If the behavior is wrong, I can submit a pull request with tests. Thanks.
I quote:
That's the simplest kind of plugin you'll write. In fact, that's exactly how the markdown helper is written in Cuba::TextHelpers.
Where is Cuba::TextHelpers? Is it some cuba-contrib project I don't know about? Thanks
I noticed that matching for OPTIONS requests is not supported. I checked the source for Cuba and found out that matching for the existing methods is simple sugar on top of Rack (https://github.com/soveran/cuba/blob/master/lib/cuba.rb#L328-L331). I tried adding OPTIONS and the other missing HTTP methods in the same manner as the other methods have been implemented and it worked well.
I commited the new methods in my fork. There were no tests related to HTTP methods, so I didn't write any. The fork is ready for merge.
I think the added methods are a worthwhile patch. IMO, they are not a new feature but an extension to an existing one. The addition doesn't interfere with Cuba's minimalistic approach.
The app is:
on "idea" do
on get do
token = csrf.token
res.write({csrf: token}.to_json)
end
on post do
p csrf.safe?
end
end
In the tests:
test "ideas works" do
csrf_token = JSON.parse(get("idea").body)["csrf"]
post "idea", "csrf_token" => csrf_token
end
And i get false in the console
I inspected the session and every time it has different contents
So It does not get stored between requests. What am I doing wrong here?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.