Files
recap/node_modules/@start9labs/start-sdk/base/lib/util/getServiceInterface.js
T

226 lines
8.4 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GetServiceInterface = exports.filledAddress = exports.addressHostToUrl = exports.getHostname = void 0;
exports.filterNonLocal = filterNonLocal;
exports.getOwnServiceInterface = getOwnServiceInterface;
exports.getServiceInterface = getServiceInterface;
const Host_1 = require("../interfaces/Host");
const ip_1 = require("./ip");
const deepEqual_1 = require("./deepEqual");
const once_1 = require("./once");
const Watchable_1 = require("./Watchable");
const getHostnameRegex = /^(\w+:\/\/)?([^\/\:]+)(:\d{1,3})?(\/)?/;
const getHostname = (url) => {
const founds = url.match(getHostnameRegex)?.[2];
if (!founds)
return null;
const parts = founds.split('@');
const last = parts[parts.length - 1];
return last;
};
exports.getHostname = getHostname;
const nonLocalFilter = {
exclude: {
kind: ['localhost', 'link-local', 'bridge'],
},
};
const publicFilter = {
visibility: 'public',
};
const either = (...args) => (a) => args.some((x) => x(a));
const negate = (fn) => (a) => !fn(a);
const unique = (values) => Array.from(new Set(values));
const addressHostToUrl = ({ scheme, sslScheme, username, suffix }, hostname) => {
const effectiveScheme = hostname.ssl ? sslScheme : scheme;
let host;
if (hostname.metadata.kind === 'ipv6') {
host = ip_1.IPV6_LINK_LOCAL.contains(hostname.hostname)
? `[${hostname.hostname}%${hostname.metadata.scopeId}]`
: `[${hostname.hostname}]`;
}
else {
host = hostname.hostname;
}
let portStr = '';
if (hostname.port !== null) {
const excludePort = effectiveScheme &&
effectiveScheme in Host_1.knownProtocols &&
hostname.port ===
Host_1.knownProtocols[effectiveScheme]
.defaultPort;
if (!excludePort)
portStr = `:${hostname.port}`;
}
return `${effectiveScheme ? `${effectiveScheme}://` : ''}${username ? `${username}@` : ''}${host}${portStr}${suffix}`;
};
exports.addressHostToUrl = addressHostToUrl;
function filterRec(hostnames, filter, invert) {
if (filter.predicate) {
const pred = filter.predicate;
hostnames = hostnames.filter((h) => invert !== pred(h));
}
if (filter.visibility === 'public')
hostnames = hostnames.filter((h) => invert !== h.public);
if (filter.visibility === 'private')
hostnames = hostnames.filter((h) => invert !== !h.public);
if (filter.kind) {
const kind = new Set(Array.isArray(filter.kind) ? filter.kind : [filter.kind]);
if (kind.has('ip')) {
kind.add('ipv4');
kind.add('ipv6');
}
hostnames = hostnames.filter((h) => invert !==
((kind.has('mdns') && h.metadata.kind === 'mdns') ||
(kind.has('domain') &&
(h.metadata.kind === 'private-domain' ||
h.metadata.kind === 'public-domain')) ||
(kind.has('ipv4') && h.metadata.kind === 'ipv4') ||
(kind.has('ipv6') && h.metadata.kind === 'ipv6') ||
(kind.has('localhost') &&
['localhost', '127.0.0.1', '::1'].includes(h.hostname)) ||
(kind.has('link-local') &&
h.metadata.kind === 'ipv6' &&
ip_1.IPV6_LINK_LOCAL.contains(ip_1.IpAddress.parse(h.hostname))) ||
(kind.has('bridge') &&
h.metadata.kind === 'ipv4' &&
h.metadata.gateway === 'lxcbr0') ||
(kind.has('plugin') && h.metadata.kind === 'plugin')));
}
if (filter.pluginId) {
const id = filter.pluginId;
hostnames = hostnames.filter((h) => invert !==
(h.metadata.kind === 'plugin' && h.metadata.packageId === id));
}
if (filter.exclude)
return filterRec(hostnames, filter.exclude, !invert);
return hostnames;
}
function isPublicIp(h) {
return h.public && (h.metadata.kind === 'ipv4' || h.metadata.kind === 'ipv6');
}
function enabledAddresses(addr) {
return addr.available.filter((h) => {
if (isPublicIp(h)) {
// Public IPs: disabled by default, explicitly enabled via SocketAddr string
if (h.port === null)
return true;
const sa = h.metadata.kind === 'ipv6'
? `[${h.hostname}]:${h.port}`
: `${h.hostname}:${h.port}`;
return addr.enabled.includes(sa);
}
else {
// Everything else: enabled by default, explicitly disabled via [hostname, port] tuple
return !addr.disabled.some(([hostname, port]) => hostname === h.hostname && port === (h.port ?? 0));
}
});
}
/**
* Filters out localhost and IPv6 link-local hostnames from a list.
* Equivalent to the `nonLocal` filter on `Filled` addresses.
*/
function filterNonLocal(hostnames) {
return filterRec(hostnames, nonLocalFilter, false);
}
const filledAddress = (host, addressInfo) => {
const toUrl = exports.addressHostToUrl.bind(null, addressInfo);
const binding = host.bindings[addressInfo.internalPort];
const hostnames = binding ? enabledAddresses(binding.addresses) : [];
function filledAddressFromHostnames(hostnames) {
const getNonLocal = (0, once_1.once)(() => filledAddressFromHostnames(filterRec(hostnames, nonLocalFilter, false)));
const getPublic = (0, once_1.once)(() => filledAddressFromHostnames(filterRec(hostnames, publicFilter, false)));
return {
...addressInfo,
hostnames,
toUrl,
format: (format) => {
let res = hostnames;
if (format === 'hostname-info')
return res;
const urls = hostnames.map(toUrl);
if (format === 'url')
res = urls.map((u) => new URL(u));
else
res = urls;
return res;
},
filter: (filter) => {
return filledAddressFromHostnames(filterRec(hostnames, filter, false));
},
matchesAny: (filters) => {
const seen = new Set();
const union = [];
for (const f of filters) {
for (const h of filterRec(hostnames, f, false)) {
if (!seen.has(h)) {
seen.add(h);
union.push(h);
}
}
}
return filledAddressFromHostnames(union);
},
get nonLocal() {
return getNonLocal();
},
get public() {
return getPublic();
},
};
}
return filledAddressFromHostnames(hostnames);
};
exports.filledAddress = filledAddress;
const makeInterfaceFilled = async ({ effects, id, packageId, callback, }) => {
const serviceInterfaceValue = await effects.getServiceInterface({
serviceInterfaceId: id,
packageId,
callback,
});
if (!serviceInterfaceValue) {
return null;
}
const hostId = serviceInterfaceValue.addressInfo.hostId;
const host = await effects.getHostInfo({
packageId,
hostId,
callback,
});
const interfaceFilled = {
...serviceInterfaceValue,
host,
addressInfo: host
? (0, exports.filledAddress)(host, serviceInterfaceValue.addressInfo)
: null,
};
return interfaceFilled;
};
class GetServiceInterface extends Watchable_1.Watchable {
constructor(effects, opts, options) {
super(effects, options);
this.opts = opts;
this.label = 'GetServiceInterface';
}
fetch(callback) {
return makeInterfaceFilled({
effects: this.effects,
id: this.opts.id,
packageId: this.opts.packageId,
callback,
});
}
}
exports.GetServiceInterface = GetServiceInterface;
function getOwnServiceInterface(effects, id, map, eq) {
return new GetServiceInterface(effects, { id }, {
map: map ?? ((a) => a),
eq: eq ?? ((a, b) => (0, deepEqual_1.deepEqual)(a, b)),
});
}
function getServiceInterface(effects, opts, map, eq) {
return new GetServiceInterface(effects, opts, {
map: map ?? ((a) => a),
eq: eq ?? ((a, b) => (0, deepEqual_1.deepEqual)(a, b)),
});
}
//# sourceMappingURL=getServiceInterface.js.map