watson / bonjour Goto Github PK
View Code? Open in Web Editor NEWA Bonjour/Zeroconf protocol implementation in JavaScript
License: MIT License
A Bonjour/Zeroconf protocol implementation in JavaScript
License: MIT License
This would just be a nice thing. This along side the subtypes would give a great way to narrow in on your service. Here is the file where I am doing this in my thing in my project.
I resorted to this when subtypes didn't work, so if that is a better way in your opinion then feel free to close this one right up :)
In an Ionic 2 TypeScript project, I've installed the bonjour npm module, then imported it like this:
import bonjour from 'bonjour';
That doesn't throw errors. Bt as soon as I refer to bonjour, I get the runtime error
Cannot find module "dgram"
Has anyone successfully used this plugin with Ionic 2?
Line 11 in bdc467a
The marked lib has a transient dep to a vulnerable package which has been fixed. Is it possible to upgrade to a later version like "^7.0.0"
As with many of the unresolved discovery issues, I as well am experiencing issues discovering services across different devices. This is consistent with windows and mac. I'm able to publish services from each device and discover each devices' service, however once the process is restarted, the 'up' event listener is not triggered again (or at least not consistently).
Anyone have a solution of any kind, or point me in the direction of another package with similar functionality?
I would like to have a raspberry pi with a service announced in a continuous way, in that way an app could detect the service and start operating with it.
Digging inside registry.js
I've seen that the delay is autocalculated with fixed constants: REANNOUNCE_FACTOR
and REANNOUNCE_MAX_MS
(https://github.com/watson/bonjour/blob/master/lib/registry.js#L147)
I suggest to make it configurable via publish options. Does it make sense?
I think I could implement this feature and I'm open for pull requests, are they welcome?
My proposal would be to pass those parameters via publish
opts and then fetch them inside registry.js
.
Something like this:
Registry.prototype.publish = function (opts) {
var service = new Service(opts)
service.start = start.bind(service, this)
service.stop = stop.bind(service, this)
service.start({
probe: opts.probe !== false,
reannounceFactor: opts.reannounceFactor || REANNOUNCE_FACTOR,
reannounceMaxMs: opts.reannounceFactor || REANNOUNCE_MAX_MS
})
return service
}
function start (registry, opts) {
if (this._activated) return
this._activated = true
registry._services.push(this)
if (opts.probe) {
var service = this
probe(registry._server.mdns, this, function (exists) {
if (exists) {
service.stop()
service.emit('error', new Error('Service name is already in use on the network'))
return
}
announce(registry._server, service, opts)
})
} else {
announce(registry._server, this, opts)
}
}
function announce (server, service, opts) {
var delay = 1000
var packet = service._records()
server.register(packet)
;(function broadcast () {
// abort if the service have or is being stopped in the meantime
if (!service._activated || service._destroyed) return
server.mdns.respond(packet, function () {
// This function will optionally be called with an error object. We'll
// just silently ignore it and retry as we normally would
if (!service.published) {
service._activated = true
service.published = true
service.emit('up')
}
delay = delay * opts.reannounceFactor
if (delay < opts.reannounceMaxMs && !service._destroyed) {
setTimeout(broadcast, delay).unref()
}
})
})()
}
I have an application that use your module like this:
bonjour.publish({
name: 'LISA', type: 'http', port: app.config.web.port
})
On my Android application, I use the standard API (https://developer.android.com/training/connect-devices-wirelessly/nsd.html), the service is found but in order to have the host/port we need to call mNsdManager.resolveService
and pass the retrieved service. The problem is nothing append at all no onResolveFailed
or onServiceResolved
... just nothing.
Did I miss something ?
Running the following in my app
const service = bonjour.publish({
type: 'GPMDP',
name: os.hostname(),
port: 5672,
txt: {
API_VERSION,
},
});
service.start();
The up
event is fired for service
but when I attempt to find the published service with this chrome app
https://chrome.google.com/webstore/detail/mdns-browser/kipighjpklofchgbdgclfaoccdlghidp
It is not there. Is there some configuration step I am missing, or is there a trick to getting this to run on a device that already has Apple Bonjour running?
Bonjour is still used by people but as it looks like not maintained. Wouldn't it be good to refer to archive/deprecate this project in favor of https://github.com/onlxltd/bonjour-service?
I'm playing with a very simple bonjour.js
// browse for all http services
bonjour.find({ type: 'http' }, function (service) {
console.log('Found service %s on %s:%d', service.name, service.referer.address, service.port)
})
Most of the time, all my http services are properly found (dns-sd -B confirm)
I stop stop Apple bonjour service net stop "Bonjour Service"
, node bonjour.js
still works , dns-sd
is down.
I re-start Apple bonjour service net start "Bonjour Service"
, dns-sd -B
works, node bonjour.js
do NOT find anything anymore, until i reboot my computer...
Tested with node 4.4 & 5.1 (& nw) => same results.
Is there any "lost udp datagram" somewhere you think i should look for ?
Any idea on how to fix this ?
Thank you for your help !
Does filtering on txt object works?
Thank you for this capability!
I'm trying to pick up a service on an audio interface box that publishes. I have a wired connection to the box. I'm using a MacBook Pro.
When I turn off wifi on the MacBook Pro, I get the service returned from a Find.
If I have wifi on, I don't get the service returned. I do need to have wifi on.
Any suggestions would be much appreciated!
I want to publish/discover services from a browser (bonjour services) running on mobile apps. Should this pkg support running in a web browser?
From RFC 6762 section 10.2:
The simple rules for case-insensitivity in Unicast DNS [RFC1034] [RFC1035] also apply in Multicast DNS; that is to say, in name comparisons, the lowercase letters "a" to "z" (0x61 to 0x7A) match their uppercase equivalents "A" to "Z" (0x41 to 0x5A). Hence, if a querier issues a query for an address record with the name "myprinter.local.", then a responder having an address record with the name "MyPrinter.local." should issue a response. No other automatic equivalences should be assumed. In particular, all UTF-8 multibyte characters (codes 0x80 and higher) are compared by simple binary comparison of the raw byte values. Accented characters are not defined to be automatically equivalent to their unaccented counterparts. Where automatic equivalences are desired, this may be achieved through the use of programmatically generated CNAME records. For example, if a responder has an address record for an accented name Y, and a querier issues a query for a name X, where X is the same as Y with all the accents removed, then the responder may issue a response containing two resource records: a CNAME record "X CNAME Y", asserting that the requested name X (unaccented) is an alias for the true (accented) name Y, followed by the address record for Y.
Everything is working fine but in certain situations I would like to reset and begin the search again.
How can I do that?
Good job, so far. Thank you.
On a machine with multiple network interfaces (ethernet, wlan, mobile broadband) a service is pulished with a- and aaaa-records for all IP adresses on any interface.
RFC 6762, Chapter 14. Considerations for Multiple Interfaces states:
Except in the case of proxying and other similar specialized uses,
addresses in IPv4 or IPv6 address records in Multicast DNS responses
MUST be valid for use on the interface on which the response is being
sent.
There should be a configurable filter or a separation by interface/IP address.
Either it's starting up, but can't find the udp service, or it says it's starting up but it's not. no error messages are occurring. Here is the code:
var bonjour = require('bonjour')();
var name = 'brad bonjour';
var type = 'a cool type';
var service = bonjour.publish({
name,
type,
port: 3030,
protocol: 'udp'
});
service.start();
service.on('error', error =>{
console.log('error', error);
})
service.on('up', () => {
console.log('it is now up');
})
bonjour.find({ type }, function (service) {
if (service.name === name){
console.log('found the service', service);
} else {
console.log('.');
}
})
If the protocol is tcp
, then i see the log event "found the service", but if i set the protocol to udp
, there is no log event, but I would expect there to be one. Either way, i do see the "it is now up" log event
Having a long running process like https://github.com/auchenberg/dinero-printer/, and changing network causes the following error:
It looks like it's trying to bind to a previous available IP, but the network has changed.
Hi All!
I'm trying to stop service using:
this.logger.info('Stopping P2P mDNS annoucement...'); this.service.stop(); this.bonjour.unpublishAll(); this.bonjour.destroy()
;
But unfortinately my browser still have a service in the service's list after that call, so looks like it doesn't receive a goodbye message.
Am I doing something wrong?
First thanks for this module ! Very useful !
I have a little problem when I set a name A.B.C.
the final name of the service is A
:
Found an HTTP server: { addresses: [ 'fe80::caf1:350c:e53a:cd3a', '192.168.1.21' ],
rawTxt: <Buffer 00>,
txt: {},
name: 'L',
fqdn: 'L.I.S.A.._http._tcp.local',
host: 'mylisabox.local',
referer: { address: '192.168.1.21', family: 'IPv4', port: 5353, size: 138 },
port: 4321,
type: 'I',
protocol: 'S',
subtypes: [ 'A', '', 'http', 'tcp' ] }
Maybe this is normal into bonjour protocol but as I don't know it a lot I prefer report this.
the current multicast-dns dependency version relies on an ancient version of dns-packet that is vulnerable to CVE-2021-23386
https://github.com/watson/bonjour/blob/master/package.json#L11
RELATED:
I'm trying to create a bonjour service which advertises a specific name, in order to make my node server discoverable from other devices
console.log("Starting bonjour service...");
var bonjourService = bonjour.publish({ name: 'agendaserver', type: 'http', port: 8080 });
bonjourService.start();
bonjourService.on('up', function () {
console.log("Started bonjour service on " + bonjourService.fqdn + ":" + bonjourService.port);
// => Started bonjour service on agendaserver._http._tcp.local:8080
console.log("published = " + bonjourService.published) // => true
});
When I try to discover the service with avahi-browse -rk _http._tcp
on the same machine, I get the following error:
Failed to resolve service 'agendaserver' of type '_http._tcp' in domain 'local': DNS failure: NXDOMAIN
What am I doing wrong?
Do I need to configure the underlying multicast-dns server?
Hi All!
It's not clear what function update do.
Can you provide an example pls how to use that?
What it will do? Will it re-discover all mdns service? Or will it check that all already caught messages are still present? Or what?
Best regards,
Sergey
Hi,
Would it be possible to set a different protocol type instead of the http or anything else on the default list? I am trying to create a bonjour service discovery for a service like this : _oca._tcp instead of _http._tcp
I tried doing this but the service doesn't seem te be registered..
the bonjour services crashes, with multicast turned on:
var options = {
multicast: true, // use udp multicasting
interface: 'eth', // explicitly specify a network interface. defaults to all
port: 5353, // set the udp port
ip: '10.0.1.130', // set the udp ip
ttl: 255, // set the multicast ttl
//loopback: true, // receive your own packets
//reuseAddr: true // set the reuseAddr option when creating the socket (requires node >=0.11.13)
}
var bonjour = require('bonjour')(options);
ERROR:
gram.js:438
throw errnoException(err, 'addMembership');
^
Error: addMembership EINVAL
at exports._errnoException (util.js:870:11)
at Socket.addMembership (dgram.js:438:11)
at Socket.<anonymous> (/path/to/project/node_modules/multicast-dns/index.js:51:14)
at emitNone (events.js:72:20)
at Socket.emit (events.js:166:7)
at startListening (dgram.js:121:10)
at dgram.js:220:7
at nextTickCallbackWith3Args (node.js:452:9)
at process._tickCallback (node.js:358:17)
but turning multicast off, results in not publishing service.
just calling
var bonjour = require('bonjour')(); // here without the options
seems to work, BUT, address is not published.
Output with find look like this:
Found a PLIIGO-BOX server:
{ addresses: [],
name: 'pliigo',
fqdn: 'pliigo._pliigobox._tcp.local',
host: 'pliigo',
port: 5002,
type: 'pliigobox',
protocol: 'tcp',
subtypes: [],
rawTxt: <Buffer 00>,
txt: {} }
When publishing the service with avahi-daemon, then the address is published on the same machine.
Sometimes when a service is up (often after the service has just restarted) it has no IP address associated: service.addresses is empty.
Is there an easy way to workaround this issue?
I'm using this cordova plugin to discover the service.
If i use a macOS machine to announce the service it works on both Android and iOS, while, if i use Windows 10 to annunce the service the iPhone isn't able to discover the service, and after several minutes i get a "service removed" message.
This library knows about the _services._dns-sd._udp.
meta-query for browsing, but it doesn't handle that query when acting as a server. That means it doesn't conform to the DNS-SD spec, and therefore isn't Bonjour.
I haven't actually verified this but I assume this issue is why Homebridge doesn't handle _services._dns-sd._udp.
(as this library appears to be an indirect dependency of Homebridge).
The bonjour implementation appears to works totally fine from the UI thread in my electron app except it throws exceptions due to the browser implementation of timers not having an unref
method.
Adding a simple test if unref is defined to the two lines where it's called resolves this.
Would you take this patch?
How to simulate:
ip link set ??? down
ip addr
will still show loopback interface 127.0.0.1require('bonjour')({loopback: true})
to set socket.setMulticastLoopback(true)
in multicast-dns
libraryWhat will happen:
What should happen:
127.0.0.1
Hi, I'm totally unable to discover the service published via this module when using different machines under the same local network:
Server on machine 1:
console.log('start');
var bonjour = require('bonjour')({multicast: true, ttl: 255});
var service = bonjour.publish({ name: 'My Web Server', type: 'http', port: 3000 })
service.start();
Client on machine 2:
console.log('start finder');
var bonjour = require('bonjour')({multicast: true, ttl: 255});
var service = bonjour.find({ type: 'http' }, function (service) {
console.log('Found HTTP server:' + JSON.stringify(service))
});
service.start();
Note that when server and client run on the same machine, it works as expected.
I'm not sure if this issue is due to implementation, or router configuration. My network consists from a MikroTik router and Ubiquiti UniFi Access point. Other ssdp works like a charm. Any ideas are welcome.
Hi,
i'm using your package in a small app i'm working on, in order to discover Bose Soundtouch speakers. All works fine when I run it on my pc, but Bonjour doesn't seem to work when I run the same app packaged as a Hass.io addon (ResinOS based Home-assistant distro).
By experimeting with console.logs I can deduct the bonjour.find function never executes.
I've searched for similar issues and was wondering if something similar to this issue might be happening:
https://stackoverflow.com/questions/24934596/cant-run-node-js-module-mdns-in-node-webkit-application/24934597#24934597
I don't know if I'm on the right track here, maybe something else is going on that i'm not seeing. Would appreciate if you could have a look.
cheers,
Kris
This package is unmaintained and has a number of outstanding issues that are unlikely to be addressed.
After doing research I believe @homebridge/ciao is a suitable - if not much improved - replacement. Sadly when searching bonjour on npm it does not come up so I think users are largely unaware that there is a much better alternative out there. For comparison, the bonjour package has 8.4M downloads per week on npmjs.com whereas ciao has 10-20k.
If nothing else, this issue will give some visibility when users of this package come to the project's issue tracker looking for answers.
I see another user has suggested bonjour-service as an alternative in #69 as well.
EDIT: as @Lesik mentioned in the comment, bonjour-service is a better solution and nearly a drop-in replacement.
Hi there,
Hoping someone may be able to provide some assistance. I'm trying to have my Windows bonjour service discovered on an iOS/MacOSx Bonjour browser.
I am using the basic startup code given in the module:
var bonjour = require('bonjour')()
// advertise an HTTP server on port 3000
bonjour.publish({ name: 'immersivedsp-server', type: 'http', port: 8001 })
So far, it seems to discover that the service is on the network, but is unable to resolve the service. My MacOSx bonjour browser just shows the name but no other information (immersivedsp-server):
When I run start the same Bonjour service on MacOSx it resolves and the MacOSx browser shows the correct information:
This is mirrored in my cordova iOS app that uses cordova-zeroconf-plugin. It shows that the service has been added but I never get the resolved message. This doesn't happen on Android.
Checking Wireshark, I see that the messages are definitely going across, so it seems to be an issue with only with the Mac/iOS.
Any guidance would be greatly appreciated!!
Hi, I have created a server application, that uses bonjour.publish(ops).
I build the application with the help of webpack. The application runs fine, but I get this error message logged about every second:
internal/process/task_queues.js:80 Uncaught TypeError: undefined is not a function
at
at processTicksAndRejections (internal/process/task_queues.js:80)
Any ideas about what this message might derive from? When I comment out the bonjour.publish(opt) statement, the message does not appear.
Hi,
I just tried to use the txt records to share a PGP public key.
It all gone very bad.
The packets received was malformed, see (i will deliberately put \n for readability)
announce sent
bonjour-publish -H 127.0.0.1 -P 8081 -T http "Bonjour chat" --txt \
'{"name":"somccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc \
ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc \
ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc \
cccccccccccccccccce"}'
announce received
{"addresses":[],"name":"Bonjour chat","fqdn": \
"Bonjour chat._http._tcp.local","host":"127.0.0.1", \
"port":8081,"type":"http","protocol":"tcp","subtypes":[], \
"rawTxt":{"type":"Buffer","data": \
[5,110,97,109,101,61,115,111,109,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99, \
99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99, \
99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99, \
99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99, \
99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99 \
,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99, \
99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99, \
99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99, \
99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99, \
99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,101]},"txt": \
{"name":"","omcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc \
ccccccccccccccccccccccccccccccccccccccccccccccc":true,"ccccccccccccccccccccccccc \
cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc \
cccc":true,"cccccccccccccccccccccccccccccccccccccce":true}}
As you can see the txt record is kind of weird. Also i notice it s missing the very first character.
Maybe it s only my router. I m unsure where that come from.
Greetings ๐
A CVE was recently opened for dns-packet
: https://nvd.nist.gov/vuln/detail/CVE-2021-23386. dns-packet
is a dependency of multicast-dns
. multicast-dns
uses the fixed version of dns-packet
as of version 7.2.3: https://github.com/mafintosh/multicast-dns/blob/309a1aa77fc85a81f04117ca16350b87a26faba1/package.json#L11, however bonjour
currently only uses multicast-dns
versions from the 6.x series: https://github.com/watson/bonjour/blob/master/package.json#L11; multicast-dns
will need to be upgraded to pull in the fix for this CVE
Hi,
Small typo at
Line 176 in b0c7842
I d say it should be fixed like this
if (records.length === 0) return cb && cb();
How can I get the ip of a hostname?
I have a linux machine running the avahi-daemon service that I am trying to discover with my windows 10 machines using this bonjour plugin. I seem to be having issues when using a windows 10 tablet (Surface Pro). The bonjour plugin isn't able to find any services whereas if I run bonjour browser (https://hobbyistsoftware.com/bonjourbrowser) the bonjour services are visible. I originally thought this might be a firewall issue but if the bonjour browser is discovering them I would think otherwise.
It also doesn't seem to be specifically windows 10 related as I have other windows 10 machines that are working just fine. This happens on multiple different Surface Pro tablets as well.
Any ideas?
This actually crashes my node process by way of an unhandled error
event. Because it's an event, it's not immediately apparent where it's thrown, probably has to do with multicast registration.
Aug 11 14:20:41 myhost node[1382]: events.js:160
Aug 11 14:20:41 myhost node[1382]: throw er; // Unhandled 'error' event
Aug 11 14:20:41 myhost node[1382]: ^
Aug 11 14:20:41 myhost node[1370]: Error: addMembership ENODEV
I encountered it in my own project which uses bonjour but it looks like it has turned up in webpack as well: webpack/webpack-dev-server#979. Unfortunately they "fixed" it by simply not using bonjour ๐
Looks like this is maybe subtypes are not implemented? The service has a property, but it isn't used.
According to this I think it should register an entry for each subtype. I THINK that I can do this despite this being my first time working with the bonjour protocol. But I dont know why there are the three types of records in the first place (PTR
, SVR
, TXT
). If you can point me in the right direction for that I think I will understand enough to get this working.
I'm trying to detect an IKEA TRADFRI gateway using this library without any success. When inspecting the traffic with Wireshark, I do see a response from that gateway but it seems to be the only one using IPv6 addresses. As a result it doesn't show up in the detected services.
Here's the dissected packet from Wireshark:
Frame 5023: 769 bytes on wire (6152 bits), 769 bytes captured (6152 bits) on interface 0
Ethernet II, Src: MurataMa_25:7a:41 (b0:72:bf:25:7a:41), Dst: IPv6mcast_fb (33:33:00:00:00:fb)
Internet Protocol Version 6, Src: 2003:e6:c3f6:f311:b272:bfff:fe25:7a41, Dst: ff02::fb
User Datagram Protocol, Src Port: 5353, Dst Port: 5353
Multicast Domain Name System (response)
Transaction ID: 0x0000
Flags: 0x8400 Standard query response, No error
Questions: 0
Answer RRs: 8
Authority RRs: 0
Additional RRs: 2
Answers
_services._dns-sd._udp.local: type PTR, class IN, _coap._udp.local
_coap._udp.local: type PTR, class IN, gw-b072bf257a41._coap._udp.local
gw-b072bf257a41._coap._udp.local: type TXT, class IN, cache flush
gw-b072bf257a41._coap._udp.local: type SRV, class IN, cache flush, priority 0, weight 0, port 5684, target TRADFRI-Gateway-b072bf257a41.local
_services._dns-sd._udp.local: type PTR, class IN, _hap._tcp.local
_hap._tcp.local: type PTR, class IN, TRADFRI gateway._hap._tcp.local
TRADFRI gateway._hap._tcp.local: type TXT, class IN, cache flush
TRADFRI gateway._hap._tcp.local: type SRV, class IN, cache flush, priority 0, weight 0, port 80, target TRADFRI-Gateway-b072bf257a41.local
Additional records
TRADFRI-Gateway-b072bf257a41.local: type A, class IN, cache flush, addr 192.168.2.102
TRADFRI-Gateway-b072bf257a41.local: type AAAA, class IN, cache flush, addr fe80::b272:bfff:fe25:7a41
Is there any way to make bonjour listen to the IPv6 addresses too?
Hello,
Multicastdns from Server module (mdns-server.js) is throwing an error (EADDRNOTAVAIL ) coming from inner function setMulticastInterface as specified in dgram's documentation
https://nodejs.org/api/dgram.html#dgram_call_results
I can't find a way to catch it from my own code except with process.on('uncaughtExcetion') which i find pretty ugly
Is there a way to catch this exception properly ? Am i missing something here or is there no better way ?
My use case is that I need to publish an IP address under some domain name. It seems that when not publishing something running on localhost I only have the option to specify a foreign host name which I can publish under some different name but there is no way to publish a foreign IP under a domain name.
I'm not even publishing a new service in the normal sense, just want to create an artifical DNS record for some IP.
I can best describe this situation by showing how we are currently implementing it using avahi and dns-sd:
You can see that dns-sd is not that friendly to publishing this as I have to specify quite a few unecessary parameters but it does get the job done.
What do you think, can bonjour
be used or extended to support this? I would be glad to help out if it some relatively simple modification. Using bonjour
would give us a nice platform independent way instead of having to rely on platform specific command line apis.
Hi! First off, this library is great. It works flawlessly on Mac. However, I'm having a hard time finding devices on Windows 10.
I can discover devices properly using the mDNS Browser Chrome extension on Windows - so the machine is able to discover devices (somehow). However, I'm unable to discover any services using this library or dns-sd (which I installed through the Apple Bonjour SDK).
I'm sorry if this isn't the best place to post about this, but I'm wondering if anyone's encountered the same issue and how they got around it. Thanks for any help!
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.