UNPKG

@elastic/elasticsearch

Version:
368 lines 16.7 kB
"use strict"; /* * 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