dnssd
Version:
Bonjour/Avahi-like service discovery in pure JavaScript
261 lines (212 loc) • 8.08 kB
JavaScript
'use strict';
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function _toArray(arr) { return Array.isArray(arr) ? arr : Array.from(arr); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var validate = require('./validate');
var ValidationError = require('./customError').create('ValidationError');
/**
* Creates a new ServiceType
* @class
*
* Used to turn some input into a reliable service type for advertisements and
* browsers. Does validation on input, throwing errors if there's a problem.
*
* Name and protocol are always required, subtypes are optional.
*
* String (single argument):
* '_http._tcp'
* '_http._tcp,mysubtype,anothersub'
*
* Object (single argument):
* {
* name: '_http',
* protocol: '_tcp',
* subtypes: ['mysubtype', 'anothersub'],
* }
*
* Array (single argument):
* ['_http', '_tcp', ['mysubtype', 'anothersub']]
* ['_http', '_tcp', 'mysubtype', 'anothersub']
*
* Strings (multiple arguments):
* '_http', '_tcp'
* '_http', '_tcp', 'mysubtype', 'anothersub'
*
* Validation step is forgiving about required leading underscores and
* will add them it missing. So 'http.tcp' would be the same as '_http._tcp'.
*
* @param {string|object|array|...string} arguments
*/
var ServiceType = function () {
function ServiceType() {
_classCallCheck(this, ServiceType);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
var input = args.length === 1 ? args[0] : args;
this.name = null;
this.protocol = null;
this.subtypes = [];
this.isEnumerator = false;
var type = typeof input === 'undefined' ? 'undefined' : _typeof(input);
if (type === 'string') this._fromString(input);else if (Array.isArray(input)) this._fromArray(input);else if (type === 'object') this._fromObj(input);else {
throw new ValidationError('Argument must be string, obj, or array. got %s', type);
}
this._validate();
}
/**
* Creates a new ServiceType with tcp protocol
* Ex:
* ServiceType.tcp('_http')
* ServiceType.tcp('_http', 'sub1', 'sub2')
* ServiceType.tcp(['_http', 'sub1', 'sub2'])
*
* @param {string|array|...string} arguments
* @return {ServiceType}
*/
_createClass(ServiceType, [{
key: '_fromString',
/**
* Parse a string into service parts
* Ex:
* '_http._tcp'
* '_http._tcp,mysubtype,anothersub'
*/
value: function _fromString(str) {
// trim off weird whitespace and extra trailing commas
var parts = str.replace(/^[ ,]+|[ ,]+$/g, '').split(',').map(function (s) {
return s.trim();
});
this.name = parts[0].split('.').slice(0, -1).join('.');
this.protocol = parts[0].split('.').slice(-1)[0];
this.subtypes = parts.slice(1);
}
/**
* Parse an array into service parts
* Ex:
* ['_http', '_tcp', ['mysubtype', 'anothersub']]
* ['_http', '_tcp', 'mysubtype', 'anothersub']
*/
}, {
key: '_fromArray',
value: function _fromArray(_ref) {
var _ref3;
var _ref2 = _toArray(_ref),
name = _ref2[0],
protocol = _ref2[1],
subtypes = _ref2.slice(2);
this._fromObj({
name: name,
protocol: protocol,
subtypes: (_ref3 = []).concat.apply(_ref3, _toConsumableArray(subtypes))
});
}
/**
* Parse an object into service parts
* Ex: {
* name: '_http',
* protocol: '_tcp',
* subtypes: ['mysubtype', 'anothersub'],
* }
*/
}, {
key: '_fromObj',
value: function _fromObj(_ref4) {
var name = _ref4.name,
protocol = _ref4.protocol,
_ref4$subtypes = _ref4.subtypes,
subtypes = _ref4$subtypes === undefined ? [] : _ref4$subtypes;
this.name = name;
this.protocol = protocol;
this.subtypes = Array.isArray(subtypes) ? subtypes : [subtypes];
}
/**
* Validates service name, protocol, and subtypes. Throws if any of them
* are invalid.
*/
}, {
key: '_validate',
value: function _validate() {
if (typeof this.name !== 'string') {
throw new ValidationError('Service name must be a string, got %s', _typeof(this.name));
}
if (!this.name) {
throw new ValidationError("Service name can't be empty");
}
if (typeof this.protocol !== 'string') {
throw new ValidationError('Protocol must be a string, got %s', _typeof(this.protocol));
}
if (!this.protocol) {
throw new ValidationError("Protocol can't be empty");
}
// massage properties a little before validating
// be lenient about underscores, add when missing
if (this.name.substr(0, 1) !== '_') this.name = '_' + this.name;
if (this.protocol.substr(0, 1) !== '_') this.protocol = '_' + this.protocol;
// special case: check this service type is the service enumerator
if (this.name === '_services._dns-sd' && this.protocol === '_udp') {
this.isEnumerator = true;
// enumerators shouldn't have subtypes
this.subtypes = [];
// skip validation for service enumerators, they would fail since
// '_services._dns-sd' is getting shoehorned into this.name
return;
}
validate.serviceName(this.name);
validate.protocol(this.protocol);
this.subtypes.forEach(function (subtype) {
return validate.label(subtype, 'Subtype');
});
}
/**
* A string representation of the service
* ex: '_http._tcp,sub1,sub2'
*/
}, {
key: 'toString',
value: function toString() {
return this.subtypes.length ? this.name + '.' + this.protocol + ',' + this.subtypes.join(',') : this.name + '.' + this.protocol;
}
}], [{
key: 'tcp',
value: function tcp() {
var _ref5;
// insert protocol in the right spot (second arg)
var input = (_ref5 = []).concat.apply(_ref5, arguments);
input.splice(1, 0, '_tcp');
return new ServiceType(input);
}
/**
* Creates a new ServiceType with udp protocol
* Ex:
* ServiceType.tcp('_sleep-proxy,sub1,sub2')
* ServiceType.tcp('_sleep-proxy', 'sub1', 'sub2')
* ServiceType.tcp(['_sleep-proxy', 'sub1', 'sub2'])
*
* @param {string|array|...string} [arguments]
* @return {ServiceType}
*/
}, {
key: 'udp',
value: function udp() {
var _ref6;
// insert protocol in the right spot (second arg)
var input = (_ref6 = []).concat.apply(_ref6, arguments);
input.splice(1, 0, '_udp');
return new ServiceType(input);
}
/**
* Creates a new service enumerator
* @return {ServiceType}
*/
}, {
key: 'all',
value: function all() {
return new ServiceType('_services._dns-sd._udp');
}
}]);
return ServiceType;
}();
module.exports = ServiceType;