@elastic/elasticsearch
Version:
The official Elasticsearch client for Node.js
368 lines • 16.7 kB
JavaScript
/*
* Copyright Elasticsearch B.V. and contributors
* SPDX-License-Identifier: Apache-2.0
*/
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const node_process_1 = tslib_1.__importDefault(require("node:process"));
const node_url_1 = require("node:url");
const node_buffer_1 = tslib_1.__importDefault(require("node:buffer"));
const node_os_1 = tslib_1.__importDefault(require("node:os"));
const transport_1 = require("@elastic/transport");
const BaseConnection_1 = require("@elastic/transport/lib/connection/BaseConnection");
const sniffingTransport_1 = tslib_1.__importDefault(require("./sniffingTransport"));
const helpers_1 = tslib_1.__importDefault(require("./helpers"));
const api_1 = tslib_1.__importDefault(require("./api"));
const package_json_1 = tslib_1.__importDefault(require("../package.json"));
const package_json_2 = tslib_1.__importDefault(require("@elastic/transport/package.json"));
const kChild = Symbol('elasticsearchjs-child');
const kInitialOptions = Symbol('elasticsearchjs-initial-options');
let clientVersion = package_json_1.default.version;
/* istanbul ignore next */
if (clientVersion.includes('-')) {
// clean prerelease
clientVersion = clientVersion.slice(0, clientVersion.indexOf('-')) + 'p';
}
let transportVersion = package_json_2.default.version; // eslint-disable-line
/* istanbul ignore next */
if (transportVersion.includes('-')) {
// clean prerelease
transportVersion = transportVersion.slice(0, transportVersion.indexOf('-')) + 'p';
}
const nodeVersion = node_process_1.default.versions.node;
const serverlessApiVersion = '2023-10-31';
class Client extends api_1.default {
constructor(opts) {
var _a, _b, _c, _d;
super();
Object.defineProperty(this, "diagnostic", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "name", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "connectionPool", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "transport", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "serializer", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "helpers", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
// @ts-expect-error kChild symbol is for internal use only
if ((opts.cloud != null || opts.serverMode === 'serverless') && opts[kChild] === undefined) {
if (opts.cloud != null) {
const { id } = opts.cloud;
if (typeof id !== 'string') {
throw new transport_1.errors.ConfigurationError('Cloud ID must be a string.');
}
const parts = id.split(':');
if (parts.length !== 2 || parts[1] === '') {
throw new transport_1.errors.ConfigurationError('Cloud ID must be in the format "name:base64string".');
}
// the cloud id is `cluster-name:base64encodedurl`
// the url is a string divided by two '$', the first is the cloud url
// the second the elasticsearch instance, the third the kibana instance
let cloudUrls;
try {
cloudUrls = Buffer.from(parts[1], 'base64').toString().split('$');
}
catch (err) {
throw new transport_1.errors.ConfigurationError('Cloud ID base64 decoding failed.');
}
if (cloudUrls.length < 2 || cloudUrls[0] === '' || cloudUrls[1] === '') {
throw new transport_1.errors.ConfigurationError('Cloud ID base64 must contain at least two "$" separated parts: "<cloudUrl>$<esId>[$<kibanaId>]".');
}
opts.node = `https://${cloudUrls[1]}.${cloudUrls[0]}`;
}
// Cloud has better performance with compression enabled
// see https://github.com/elastic/elasticsearch-py/pull/704.
// So unless the user specifies otherwise, we enable compression.
if (opts.compression == null)
opts.compression = true;
if (opts.tls == null ||
(opts.tls != null && opts.tls.secureProtocol == null)) {
opts.tls = (_a = opts.tls) !== null && _a !== void 0 ? _a : {};
opts.tls.secureProtocol = 'TLSv1_2_method';
}
}
if (opts.node == null && opts.nodes == null) {
throw new transport_1.errors.ConfigurationError('Missing node(s) option');
}
// @ts-expect-error kChild symbol is for internal use only
if (opts[kChild] === undefined) {
const checkAuth = getAuth((_b = opts.node) !== null && _b !== void 0 ? _b : opts.nodes);
if ((checkAuth != null) && checkAuth.username !== '' && checkAuth.password !== '') {
opts.auth = Object.assign({}, opts.auth, { username: checkAuth.username, password: checkAuth.password });
}
}
const headers = {
'user-agent': `elasticsearch-js/${clientVersion} (${node_os_1.default.platform()} ${node_os_1.default.release()}-${node_os_1.default.arch()}; Node.js ${nodeVersion}; Transport ${transportVersion})`
};
if (opts.serverMode === 'serverless')
headers['elastic-api-version'] = serverlessApiVersion;
const options = Object.assign({}, {
Connection: transport_1.UndiciConnection,
Transport: opts.serverMode === 'serverless' ? transport_1.Transport : sniffingTransport_1.default,
Serializer: transport_1.Serializer,
ConnectionPool: (opts.cloud != null || opts.serverMode === 'serverless') ? transport_1.CloudConnectionPool : transport_1.WeightedConnectionPool,
maxRetries: 3,
pingTimeout: 3000,
sniffInterval: false,
sniffOnStart: false,
sniffEndpoint: '_nodes/_all/http',
sniffOnConnectionFault: false,
resurrectStrategy: 'ping',
compression: false,
tls: null,
caFingerprint: null,
agent: null,
headers,
nodeFilter: null,
generateRequestId: null,
name: 'elasticsearch-js',
auth: null,
opaqueIdPrefix: null,
context: null,
proxy: null,
enableMetaHeader: true,
maxResponseSize: null,
maxCompressedResponseSize: null,
redaction: {
type: 'replace',
additionalKeys: []
},
serverMode: 'stack'
}, opts);
if (options.caFingerprint != null && isHttpConnection((_c = opts.node) !== null && _c !== void 0 ? _c : opts.nodes)) {
throw new transport_1.errors.ConfigurationError('You can\'t configure the caFingerprint with a http connection');
}
if (options.maxResponseSize != null && options.maxResponseSize > node_buffer_1.default.constants.MAX_STRING_LENGTH) {
throw new transport_1.errors.ConfigurationError(`The maxResponseSize cannot be bigger than ${node_buffer_1.default.constants.MAX_STRING_LENGTH}`);
}
if (options.maxCompressedResponseSize != null && options.maxCompressedResponseSize > node_buffer_1.default.constants.MAX_LENGTH) {
throw new transport_1.errors.ConfigurationError(`The maxCompressedResponseSize cannot be bigger than ${node_buffer_1.default.constants.MAX_LENGTH}`);
}
if (options.enableMetaHeader) {
let clientMeta = `es=${clientVersion},js=${nodeVersion},t=${transportVersion}`;
if (options.Connection === transport_1.UndiciConnection) {
clientMeta += `,un=${nodeVersion}`;
}
else {
// assumes HttpConnection
clientMeta += `,hc=${nodeVersion}`;
}
options.headers['x-elastic-client-meta'] = clientMeta;
}
this.name = options.name;
// @ts-expect-error kInitialOptions symbol is for internal use only
this[kInitialOptions] = options;
// @ts-expect-error kChild symbol is for internal use only
if (opts[kChild] !== undefined) {
// @ts-expect-error kChild symbol is for internal use only
this.serializer = opts[kChild].serializer;
// @ts-expect-error kChild symbol is for internal use only
this.connectionPool = opts[kChild].connectionPool;
// @ts-expect-error kChild symbol is for internal use only
this.diagnostic = opts[kChild].diagnostic;
}
else {
this.diagnostic = new transport_1.Diagnostic();
let serializerOptions;
if (opts.disablePrototypePoisoningProtection != null) {
if (typeof opts.disablePrototypePoisoningProtection === 'boolean') {
serializerOptions = {
enablePrototypePoisoningProtection: !opts.disablePrototypePoisoningProtection
};
}
else {
serializerOptions = {
enablePrototypePoisoningProtection: opts.disablePrototypePoisoningProtection
};
}
}
this.serializer = new options.Serializer(serializerOptions);
this.connectionPool = new options.ConnectionPool({
pingTimeout: options.pingTimeout,
resurrectStrategy: options.resurrectStrategy,
tls: options.tls,
agent: options.agent,
proxy: options.proxy,
Connection: options.Connection,
auth: options.auth,
diagnostic: this.diagnostic,
caFingerprint: options.caFingerprint
});
// ensure default connection values are inherited when creating new connections
// see https://github.com/elastic/elasticsearch-js/issues/1791
let nodes = (_d = options.node) !== null && _d !== void 0 ? _d : options.nodes;
// serverless only supports one node, so pick the first one
if (options.serverMode === 'serverless' && Array.isArray(nodes)) {
nodes = nodes[0];
}
let nodeOptions = Array.isArray(nodes) ? nodes : [nodes];
nodeOptions = nodeOptions.map(opt => {
const { tls, headers, auth, requestTimeout: timeout, agent, proxy, caFingerprint } = options;
let defaults = { tls, headers, auth, timeout, agent, proxy, caFingerprint };
// strip undefined values from defaults
defaults = Object.keys(defaults).reduce((acc, key) => {
const val = defaults[key];
if (val !== undefined)
acc[key] = val;
return acc;
}, {});
let newOpts;
if (typeof opt === 'string') {
newOpts = {
url: new node_url_1.URL(opt)
};
}
else {
newOpts = opt;
}
return { ...defaults, ...newOpts };
});
this.connectionPool.addConnection(nodeOptions);
}
let transportOptions = {
diagnostic: this.diagnostic,
connectionPool: this.connectionPool,
serializer: this.serializer,
maxRetries: options.maxRetries,
requestTimeout: options.requestTimeout,
compression: options.compression,
headers: options.headers,
generateRequestId: options.generateRequestId,
name: options.name,
opaqueIdPrefix: options.opaqueIdPrefix,
context: options.context,
productCheck: 'Elasticsearch',
maxResponseSize: options.maxResponseSize,
maxCompressedResponseSize: options.maxCompressedResponseSize,
redaction: options.redaction
};
if (options.serverMode !== 'serverless') {
transportOptions = Object.assign({}, transportOptions, {
sniffInterval: options.sniffInterval,
sniffOnStart: options.sniffOnStart,
sniffOnConnectionFault: options.sniffOnConnectionFault,
sniffEndpoint: options.sniffEndpoint,
nodeFilter: options.nodeFilter,
nodeSelector: options.nodeSelector,
vendoredHeaders: {
jsonContentType: 'application/vnd.elasticsearch+json; compatible-with=9',
ndjsonContentType: 'application/vnd.elasticsearch+x-ndjson; compatible-with=9',
accept: 'application/vnd.elasticsearch+json; compatible-with=9,text/plain'
}
});
}
this.transport = new options.Transport(transportOptions);
this.helpers = new helpers_1.default({
client: this,
metaHeader: options.enableMetaHeader
? `es=${clientVersion},js=${nodeVersion},t=${transportVersion},hc=${nodeVersion}`
: null,
maxRetries: options.maxRetries
});
}
/**
* Creates a child client instance that shared its connection pool with the parent client
* @see {@link https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/child.html}
*/
child(opts) {
// Merge the new options with the initial ones
// @ts-expect-error kChild symbol is for internal use only
const options = Object.assign({}, this[kInitialOptions], opts);
// Pass to the child client the parent instances that cannot be overridden
// @ts-expect-error kInitialOptions symbol is for internal use only
options[kChild] = {
connectionPool: this.connectionPool,
serializer: this.serializer,
diagnostic: this.diagnostic,
initialOptions: options
};
/* istanbul ignore else */
if (options.auth !== undefined) {
options.headers = (0, BaseConnection_1.prepareHeaders)(options.headers, options.auth);
}
return new Client(options);
}
/**
* Closes all connections in the connection pool. Connections shared with any parent or child instances will also be closed.
*/
async close() {
return await this.connectionPool.empty();
}
}
exports.default = Client;
function isHttpConnection(node) {
if (Array.isArray(node)) {
return node.some((n) => (typeof n === 'string' ? new node_url_1.URL(n).protocol : n.url.protocol) === 'http:');
}
else {
if (node == null)
return false;
return (typeof node === 'string' ? new node_url_1.URL(node).protocol : node.url.protocol) === 'http:';
}
}
function getAuth(node) {
if (Array.isArray(node)) {
for (const url of node) {
const auth = getUsernameAndPassword(url);
if (auth != null && auth.username !== '' && auth.password !== '') {
return auth;
}
}
return null;
}
else {
const auth = getUsernameAndPassword(node);
if (auth != null && auth.username !== '' && auth.password !== '') {
return auth;
}
return null;
}
function getUsernameAndPassword(node) {
/* istanbul ignore else */
if (typeof node === 'string') {
const { username, password } = new node_url_1.URL(node);
return {
username: decodeURIComponent(username),
password: decodeURIComponent(password)
};
}
else if (node != null && node.url instanceof node_url_1.URL) {
return {
username: decodeURIComponent(node.url.username),
password: decodeURIComponent(node.url.password)
};
}
else {
return null;
}
}
}
//# sourceMappingURL=client.js.map
;