'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); // Type definitions for meilisearch // Project: https://github.com/meilisearch/meilisearch-js // Definitions by: qdequele // Definitions: https://github.com/meilisearch/meilisearch-js // TypeScript Version: ^3.8.3 /* * SEARCH PARAMETERS */ const MatchingStrategies = { ALL: "all", LAST: "last", FREQUENCY: "frequency", }; const ContentTypeEnum = { JSON: "application/json", CSV: "text/csv", NDJSON: "application/x-ndjson", }; /* ** TASKS */ const TaskStatus = { TASK_SUCCEEDED: "succeeded", TASK_PROCESSING: "processing", TASK_FAILED: "failed", TASK_ENQUEUED: "enqueued", TASK_CANCELED: "canceled", }; const TaskTypes = { DOCUMENTS_ADDITION_OR_UPDATE: "documentAdditionOrUpdate", DOCUMENT_DELETION: "documentDeletion", DUMP_CREATION: "dumpCreation", INDEX_CREATION: "indexCreation", INDEX_DELETION: "indexDeletion", INDEXES_SWAP: "indexSwap", INDEX_UPDATE: "indexUpdate", SETTINGS_UPDATE: "settingsUpdate", SNAPSHOT_CREATION: "snapshotCreation", TASK_CANCELATION: "taskCancelation", TASK_DELETION: "taskDeletion", }; // @TODO: This doesn't seem to be up to date, and its usefullness comes into question. const ErrorStatusCode = { /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#index_creation_failed */ INDEX_CREATION_FAILED: "index_creation_failed", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#missing_index_uid */ MISSING_INDEX_UID: "missing_index_uid", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#index_already_exists */ INDEX_ALREADY_EXISTS: "index_already_exists", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#index_not_found */ INDEX_NOT_FOUND: "index_not_found", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_index_uid */ INVALID_INDEX_UID: "invalid_index_uid", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#index_not_accessible */ INDEX_NOT_ACCESSIBLE: "index_not_accessible", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_index_offset */ INVALID_INDEX_OFFSET: "invalid_index_offset", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_index_limit */ INVALID_INDEX_LIMIT: "invalid_index_limit", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_state */ INVALID_STATE: "invalid_state", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#primary_key_inference_failed */ PRIMARY_KEY_INFERENCE_FAILED: "primary_key_inference_failed", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#index_primary_key_already_exists */ INDEX_PRIMARY_KEY_ALREADY_EXISTS: "index_primary_key_already_exists", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_index_primary_key */ INVALID_INDEX_PRIMARY_KEY: "invalid_index_primary_key", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#max_fields_limit_exceeded */ DOCUMENTS_FIELDS_LIMIT_REACHED: "document_fields_limit_reached", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#missing_document_id */ MISSING_DOCUMENT_ID: "missing_document_id", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#missing_document_id */ INVALID_DOCUMENT_ID: "invalid_document_id", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_content_type */ INVALID_CONTENT_TYPE: "invalid_content_type", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#missing_content_type */ MISSING_CONTENT_TYPE: "missing_content_type", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_document_fields */ INVALID_DOCUMENT_FIELDS: "invalid_document_fields", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_document_limit */ INVALID_DOCUMENT_LIMIT: "invalid_document_limit", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_document_offset */ INVALID_DOCUMENT_OFFSET: "invalid_document_offset", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_document_filter */ INVALID_DOCUMENT_FILTER: "invalid_document_filter", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#missing_document_filter */ MISSING_DOCUMENT_FILTER: "missing_document_filter", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_document_vectors_field */ INVALID_DOCUMENT_VECTORS_FIELD: "invalid_document_vectors_field", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#payload_too_large */ PAYLOAD_TOO_LARGE: "payload_too_large", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#missing_payload */ MISSING_PAYLOAD: "missing_payload", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#malformed_payload */ MALFORMED_PAYLOAD: "malformed_payload", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#no_space_left_on_device */ NO_SPACE_LEFT_ON_DEVICE: "no_space_left_on_device", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_store_file */ INVALID_STORE_FILE: "invalid_store_file", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_ranking_rules */ INVALID_RANKING_RULES: "missing_document_id", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_request */ INVALID_REQUEST: "invalid_request", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_document_geo_field */ INVALID_DOCUMENT_GEO_FIELD: "invalid_document_geo_field", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_q */ INVALID_SEARCH_Q: "invalid_search_q", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_offset */ INVALID_SEARCH_OFFSET: "invalid_search_offset", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_limit */ INVALID_SEARCH_LIMIT: "invalid_search_limit", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_page */ INVALID_SEARCH_PAGE: "invalid_search_page", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_hits_per_page */ INVALID_SEARCH_HITS_PER_PAGE: "invalid_search_hits_per_page", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_attributes_to_retrieve */ INVALID_SEARCH_ATTRIBUTES_TO_RETRIEVE: "invalid_search_attributes_to_retrieve", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_attributes_to_crop */ INVALID_SEARCH_ATTRIBUTES_TO_CROP: "invalid_search_attributes_to_crop", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_crop_length */ INVALID_SEARCH_CROP_LENGTH: "invalid_search_crop_length", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_attributes_to_highlight */ INVALID_SEARCH_ATTRIBUTES_TO_HIGHLIGHT: "invalid_search_attributes_to_highlight", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_show_matches_position */ INVALID_SEARCH_SHOW_MATCHES_POSITION: "invalid_search_show_matches_position", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_filter */ INVALID_SEARCH_FILTER: "invalid_search_filter", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_sort */ INVALID_SEARCH_SORT: "invalid_search_sort", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_facets */ INVALID_SEARCH_FACETS: "invalid_search_facets", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_highlight_pre_tag */ INVALID_SEARCH_HIGHLIGHT_PRE_TAG: "invalid_search_highlight_pre_tag", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_highlight_post_tag */ INVALID_SEARCH_HIGHLIGHT_POST_TAG: "invalid_search_highlight_post_tag", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_crop_marker */ INVALID_SEARCH_CROP_MARKER: "invalid_search_crop_marker", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_matching_strategy */ INVALID_SEARCH_MATCHING_STRATEGY: "invalid_search_matching_strategy", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_vector */ INVALID_SEARCH_VECTOR: "invalid_search_vector", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_attributes_to_search_on */ INVALID_SEARCH_ATTRIBUTES_TO_SEARCH_ON: "invalid_search_attributes_to_search_on", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#bad_request */ BAD_REQUEST: "bad_request", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#document_not_found */ DOCUMENT_NOT_FOUND: "document_not_found", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#internal */ INTERNAL: "internal", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_api_key */ INVALID_API_KEY: "invalid_api_key", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_api_key_description */ INVALID_API_KEY_DESCRIPTION: "invalid_api_key_description", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_api_key_actions */ INVALID_API_KEY_ACTIONS: "invalid_api_key_actions", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_api_key_indexes */ INVALID_API_KEY_INDEXES: "invalid_api_key_indexes", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_api_key_expires_at */ INVALID_API_KEY_EXPIRES_AT: "invalid_api_key_expires_at", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#api_key_not_found */ API_KEY_NOT_FOUND: "api_key_not_found", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#immutable_api_key_uid */ IMMUTABLE_API_KEY_UID: "immutable_api_key_uid", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#immutable_api_key_actions */ IMMUTABLE_API_KEY_ACTIONS: "immutable_api_key_actions", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#immutable_api_key_indexes */ IMMUTABLE_API_KEY_INDEXES: "immutable_api_key_indexes", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#immutable_api_key_expires_at */ IMMUTABLE_API_KEY_EXPIRES_AT: "immutable_api_key_expires_at", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#immutable_api_key_created_at */ IMMUTABLE_API_KEY_CREATED_AT: "immutable_api_key_created_at", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#immutable_api_key_updated_at */ IMMUTABLE_API_KEY_UPDATED_AT: "immutable_api_key_updated_at", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#missing_authorization_header */ MISSING_AUTHORIZATION_HEADER: "missing_authorization_header", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#unretrievable_document */ UNRETRIEVABLE_DOCUMENT: "unretrievable_document", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#database_size_limit_reached */ MAX_DATABASE_SIZE_LIMIT_REACHED: "database_size_limit_reached", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#task_not_found */ TASK_NOT_FOUND: "task_not_found", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#dump_process_failed */ DUMP_PROCESS_FAILED: "dump_process_failed", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#dump_not_found */ DUMP_NOT_FOUND: "dump_not_found", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_swap_duplicate_index_found */ INVALID_SWAP_DUPLICATE_INDEX_FOUND: "invalid_swap_duplicate_index_found", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_swap_indexes */ INVALID_SWAP_INDEXES: "invalid_swap_indexes", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#missing_swap_indexes */ MISSING_SWAP_INDEXES: "missing_swap_indexes", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#missing_master_key */ MISSING_MASTER_KEY: "missing_master_key", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_task_types */ INVALID_TASK_TYPES: "invalid_task_types", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_task_uids */ INVALID_TASK_UIDS: "invalid_task_uids", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_task_statuses */ INVALID_TASK_STATUSES: "invalid_task_statuses", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_task_limit */ INVALID_TASK_LIMIT: "invalid_task_limit", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_task_from */ INVALID_TASK_FROM: "invalid_task_from", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_task_canceled_by */ INVALID_TASK_CANCELED_BY: "invalid_task_canceled_by", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#missing_task_filters */ MISSING_TASK_FILTERS: "missing_task_filters", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#too_many_open_files */ TOO_MANY_OPEN_FILES: "too_many_open_files", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#io_error */ IO_ERROR: "io_error", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_task_index_uids */ INVALID_TASK_INDEX_UIDS: "invalid_task_index_uids", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#immutable_index_uid */ IMMUTABLE_INDEX_UID: "immutable_index_uid", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#immutable_index_created_at */ IMMUTABLE_INDEX_CREATED_AT: "immutable_index_created_at", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#immutable_index_updated_at */ IMMUTABLE_INDEX_UPDATED_AT: "immutable_index_updated_at", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_settings_displayed_attributes */ INVALID_SETTINGS_DISPLAYED_ATTRIBUTES: "invalid_settings_displayed_attributes", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_settings_searchable_attributes */ INVALID_SETTINGS_SEARCHABLE_ATTRIBUTES: "invalid_settings_searchable_attributes", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_settings_filterable_attributes */ INVALID_SETTINGS_FILTERABLE_ATTRIBUTES: "invalid_settings_filterable_attributes", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_settings_sortable_attributes */ INVALID_SETTINGS_SORTABLE_ATTRIBUTES: "invalid_settings_sortable_attributes", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_settings_ranking_rules */ INVALID_SETTINGS_RANKING_RULES: "invalid_settings_ranking_rules", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_settings_stop_words */ INVALID_SETTINGS_STOP_WORDS: "invalid_settings_stop_words", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_settings_synonyms */ INVALID_SETTINGS_SYNONYMS: "invalid_settings_synonyms", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_settings_distinct_attribute */ INVALID_SETTINGS_DISTINCT_ATTRIBUTE: "invalid_settings_distinct_attribute", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_settings_typo_tolerance */ INVALID_SETTINGS_TYPO_TOLERANCE: "invalid_settings_typo_tolerance", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_settings_faceting */ INVALID_SETTINGS_FACETING: "invalid_settings_faceting", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_settings_pagination */ INVALID_SETTINGS_PAGINATION: "invalid_settings_pagination", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_settings_search_cutoff_ms */ INVALID_SETTINGS_SEARCH_CUTOFF_MS: "invalid_settings_search_cutoff_ms", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_settings_search_cutoff_ms */ INVALID_SETTINGS_LOCALIZED_ATTRIBUTES: "invalid_settings_localized_attributes", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_task_before_enqueued_at */ INVALID_TASK_BEFORE_ENQUEUED_AT: "invalid_task_before_enqueued_at", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_task_after_enqueued_at */ INVALID_TASK_AFTER_ENQUEUED_AT: "invalid_task_after_enqueued_at", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_task_before_started_at */ INVALID_TASK_BEFORE_STARTED_AT: "invalid_task_before_started_at", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_task_after_started_at */ INVALID_TASK_AFTER_STARTED_AT: "invalid_task_after_started_at", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_task_before_finished_at */ INVALID_TASK_BEFORE_FINISHED_AT: "invalid_task_before_finished_at", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_task_after_finished_at */ INVALID_TASK_AFTER_FINISHED_AT: "invalid_task_after_finished_at", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#missing_api_key_actions */ MISSING_API_KEY_ACTIONS: "missing_api_key_actions", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#missing_api_key_indexes */ MISSING_API_KEY_INDEXES: "missing_api_key_indexes", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#missing_api_key_expires_at */ MISSING_API_KEY_EXPIRES_AT: "missing_api_key_expires_at", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_api_key_limit */ INVALID_API_KEY_LIMIT: "invalid_api_key_limit", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_api_key_offset */ INVALID_API_KEY_OFFSET: "invalid_api_key_offset", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_facet_search_facet_name */ INVALID_FACET_SEARCH_FACET_NAME: "invalid_facet_search_facet_name", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#missing_facet_search_facet_name */ MISSING_FACET_SEARCH_FACET_NAME: "missing_facet_search_facet_name", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_facet_search_facet_query */ INVALID_FACET_SEARCH_FACET_QUERY: "invalid_facet_search_facet_query", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_search_ranking_score_threshold */ INVALID_SEARCH_RANKING_SCORE_THRESHOLD: "invalid_search_ranking_score_threshold", /** @see https://www.meilisearch.com/docs/reference/errors/error_codes#invalid_similar_ranking_score_threshold */ INVALID_SIMILAR_RANKING_SCORE_THRESHOLD: "invalid_similar_ranking_score_threshold", }; class MeiliSearchError extends Error { name = "MeiliSearchError"; constructor(...params) { super(...params); } } class MeiliSearchApiError extends MeiliSearchError { name = "MeiliSearchApiError"; cause; response; constructor(response, responseBody) { super(responseBody?.message ?? `${response.status}: ${response.statusText}`); this.response = response; if (responseBody !== undefined) { this.cause = responseBody; } } } class MeiliSearchRequestError extends MeiliSearchError { name = "MeiliSearchRequestError"; constructor(url, cause) { super(`Request to ${url} has failed`, { cause }); } } class MeiliSearchTimeOutError extends MeiliSearchError { name = "MeiliSearchTimeOutError"; constructor(message) { super(message); } } function versionErrorHintMessage(message, method) { return `${message}\nHint: It might not be working because maybe you're not up to date with the Meilisearch version that ${method} call requires.`; } /** Removes undefined entries from object */ function removeUndefinedFromObject(obj) { return Object.entries(obj).reduce((acc, curEntry) => { const [key, val] = curEntry; if (val !== undefined) acc[key] = val; return acc; }, {}); } async function sleep(ms) { return await new Promise((resolve) => setTimeout(resolve, ms)); } function addProtocolIfNotPresent(host) { if (!(host.startsWith("https://") || host.startsWith("http://"))) { return `http://${host}`; } return host; } function addTrailingSlash(url) { if (!url.endsWith("/")) { url += "/"; } return url; } function validateUuid4(uuid) { const regexExp = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/gi; return regexExp.test(uuid); } const PACKAGE_VERSION = "0.45.0"; function toQueryParams(parameters) { const params = Object.keys(parameters); const queryParams = params.reduce((acc, key) => { const value = parameters[key]; if (value === undefined) { return acc; } else if (Array.isArray(value)) { return { ...acc, [key]: value.join(",") }; } else if (value instanceof Date) { return { ...acc, [key]: value.toISOString() }; } return { ...acc, [key]: value }; }, {}); return queryParams; } function constructHostURL(host) { try { host = addProtocolIfNotPresent(host); host = addTrailingSlash(host); return host; } catch { throw new MeiliSearchError("The provided host is not valid."); } } function cloneAndParseHeaders(headers) { if (Array.isArray(headers)) { return headers.reduce((acc, headerPair) => { acc[headerPair[0]] = headerPair[1]; return acc; }, {}); } else if ("has" in headers) { const clonedHeaders = {}; headers.forEach((value, key) => (clonedHeaders[key] = value)); return clonedHeaders; } else { return Object.assign({}, headers); } } function createHeaders(config) { const agentHeader = "X-Meilisearch-Client"; const packageAgent = `Meilisearch JavaScript (v${PACKAGE_VERSION})`; const contentType = "Content-Type"; const authorization = "Authorization"; const headers = cloneAndParseHeaders(config.requestConfig?.headers ?? {}); // do not override if user provided the header if (config.apiKey && !headers[authorization]) { headers[authorization] = `Bearer ${config.apiKey}`; } if (!headers[contentType]) { headers["Content-Type"] = "application/json"; } // Creates the custom user agent with information on the package used. if (config.clientAgents && Array.isArray(config.clientAgents)) { const clients = config.clientAgents.concat(packageAgent); headers[agentHeader] = clients.join(" ; "); } else if (config.clientAgents && !Array.isArray(config.clientAgents)) { // If the header is defined but not an array throw new MeiliSearchError(`Meilisearch: The header "${agentHeader}" should be an array of string(s).\n`); } else { headers[agentHeader] = packageAgent; } return headers; } class HttpRequests { headers; url; requestConfig; httpClient; requestTimeout; constructor(config) { this.headers = createHeaders(config); this.requestConfig = config.requestConfig; this.httpClient = config.httpClient; this.requestTimeout = config.timeout; try { const host = constructHostURL(config.host); this.url = new URL(host); } catch { throw new MeiliSearchError("The provided host is not valid."); } } async request({ method, url, params, body, config = {}, }) { const constructURL = new URL(url, this.url); if (params) { const queryParams = new URLSearchParams(); Object.keys(params) .filter((x) => params[x] !== null) .map((x) => queryParams.set(x, params[x])); constructURL.search = queryParams.toString(); } // in case a custom content-type is provided // do not stringify body if (!config.headers?.["Content-Type"]) { body = JSON.stringify(body); } const headers = { ...this.headers, ...config.headers }; const responsePromise = this.fetchWithTimeout(constructURL.toString(), { ...config, ...this.requestConfig, method, body, headers, }, this.requestTimeout); const response = await responsePromise.catch((error) => { throw new MeiliSearchRequestError(constructURL.toString(), error); }); // When using a custom HTTP client, the response is returned to allow the user to parse/handle it as they see fit if (this.httpClient !== undefined) { return response; } const responseBody = await response.text(); const parsedResponse = responseBody === "" ? undefined : JSON.parse(responseBody); if (!response.ok) { throw new MeiliSearchApiError(response, parsedResponse); } return parsedResponse; } async fetchWithTimeout(url, options, timeout) { return new Promise((resolve, reject) => { const fetchFn = this.httpClient ? this.httpClient : fetch; const fetchPromise = fetchFn(url, options); const promises = [fetchPromise]; // TimeoutPromise will not run if undefined or zero let timeoutId; if (timeout) { const timeoutPromise = new Promise((_, reject) => { timeoutId = setTimeout(() => { reject(new Error("Error: Request Timed Out")); }, timeout); }); promises.push(timeoutPromise); } Promise.race(promises) .then(resolve) .catch(reject) .finally(() => { clearTimeout(timeoutId); }); }); } async get(url, params, config) { return await this.request({ method: "GET", url, params, config, }); } async post(url, data, params, config) { return await this.request({ method: "POST", url, body: data, params, config, }); } async put(url, data, params, config) { return await this.request({ method: "PUT", url, body: data, params, config, }); } async patch(url, data, params, config) { return await this.request({ method: "PATCH", url, body: data, params, config, }); } async delete(url, data, params, config) { return await this.request({ method: "DELETE", url, body: data, params, config, }); } } class EnqueuedTask { taskUid; indexUid; status; type; enqueuedAt; constructor(task) { this.taskUid = task.taskUid; this.indexUid = task.indexUid; this.status = task.status; this.type = task.type; this.enqueuedAt = new Date(task.enqueuedAt); } } class Task { indexUid; status; type; uid; canceledBy; details; error; duration; startedAt; enqueuedAt; finishedAt; constructor(task) { this.indexUid = task.indexUid; this.status = task.status; this.type = task.type; this.uid = task.uid; this.details = task.details; this.canceledBy = task.canceledBy; this.error = task.error; this.duration = task.duration; this.startedAt = new Date(task.startedAt); this.enqueuedAt = new Date(task.enqueuedAt); this.finishedAt = new Date(task.finishedAt); } } class TaskClient { httpRequest; constructor(config) { this.httpRequest = new HttpRequests(config); } /** * Get one task * * @param uid - Unique identifier of the task * @returns */ async getTask(uid) { const url = `tasks/${uid}`; const taskItem = await this.httpRequest.get(url); return new Task(taskItem); } /** * Get tasks * * @param parameters - Parameters to browse the tasks * @returns Promise containing all tasks */ async getTasks(parameters = {}) { const url = `tasks`; const tasks = await this.httpRequest.get(url, toQueryParams(parameters)); return { ...tasks, results: tasks.results.map((task) => new Task(task)), }; } /** * Wait for a task to be processed. * * @param taskUid - Task identifier * @param options - Additional configuration options * @returns Promise returning a task after it has been processed */ async waitForTask(taskUid, { timeOutMs = 5000, intervalMs = 50 } = {}) { const startingTime = Date.now(); while (Date.now() - startingTime < timeOutMs) { const response = await this.getTask(taskUid); if (![ TaskStatus.TASK_ENQUEUED, TaskStatus.TASK_PROCESSING, ].includes(response.status)) return response; await sleep(intervalMs); } throw new MeiliSearchTimeOutError(`timeout of ${timeOutMs}ms has exceeded on process ${taskUid} when waiting a task to be resolved.`); } /** * Waits for multiple tasks to be processed * * @param taskUids - Tasks identifier list * @param options - Wait options * @returns Promise returning a list of tasks after they have been processed */ async waitForTasks(taskUids, { timeOutMs = 5000, intervalMs = 50 } = {}) { const tasks = []; for (const taskUid of taskUids) { const task = await this.waitForTask(taskUid, { timeOutMs, intervalMs, }); tasks.push(task); } return tasks; } /** * Cancel a list of enqueued or processing tasks. * * @param parameters - Parameters to filter the tasks. * @returns Promise containing an EnqueuedTask */ async cancelTasks(parameters = {}) { const url = `tasks/cancel`; const task = await this.httpRequest.post(url, {}, toQueryParams(parameters)); return new EnqueuedTask(task); } /** * Delete a list tasks. * * @param parameters - Parameters to filter the tasks. * @returns Promise containing an EnqueuedTask */ async deleteTasks(parameters = {}) { const url = `tasks`; const task = await this.httpRequest.delete(url, {}, toQueryParams(parameters)); return new EnqueuedTask(task); } } /* * Bundle: MeiliSearch / Indexes * Project: MeiliSearch - Javascript API * Author: Quentin de Quelen * Copyright: 2019, MeiliSearch */ class Index { uid; primaryKey; createdAt; updatedAt; httpRequest; tasks; /** * @param config - Request configuration options * @param uid - UID of the index * @param primaryKey - Primary Key of the index */ constructor(config, uid, primaryKey) { this.uid = uid; this.primaryKey = primaryKey; this.httpRequest = new HttpRequests(config); this.tasks = new TaskClient(config); } /// /// SEARCH /// /** * Search for documents into an index * * @param query - Query string * @param options - Search options * @param config - Additional request configuration options * @returns Promise containing the search response */ async search(query, options, config) { const url = `indexes/${this.uid}/search`; return await this.httpRequest.post(url, removeUndefinedFromObject({ q: query, ...options }), undefined, config); } /** * Search for documents into an index using the GET method * * @param query - Query string * @param options - Search options * @param config - Additional request configuration options * @returns Promise containing the search response */ async searchGet(query, options, config) { const url = `indexes/${this.uid}/search`; const parseFilter = (filter) => { if (typeof filter === "string") return filter; else if (Array.isArray(filter)) throw new MeiliSearchError("The filter query parameter should be in string format when using searchGet"); else return undefined; }; const getParams = { q: query, ...options, filter: parseFilter(options?.filter), sort: options?.sort?.join(","), facets: options?.facets?.join(","), attributesToRetrieve: options?.attributesToRetrieve?.join(","), attributesToCrop: options?.attributesToCrop?.join(","), attributesToHighlight: options?.attributesToHighlight?.join(","), vector: options?.vector?.join(","), attributesToSearchOn: options?.attributesToSearchOn?.join(","), }; return await this.httpRequest.get(url, removeUndefinedFromObject(getParams), config); } /** * Search for facet values * * @param params - Parameters used to search on the facets * @param config - Additional request configuration options * @returns Promise containing the search response */ async searchForFacetValues(params, config) { const url = `indexes/${this.uid}/facet-search`; return await this.httpRequest.post(url, removeUndefinedFromObject(params), undefined, config); } /** * Search for similar documents * * @param params - Parameters used to search for similar documents * @returns Promise containing the search response */ async searchSimilarDocuments(params) { const url = `indexes/${this.uid}/similar`; return await this.httpRequest.post(url, removeUndefinedFromObject(params), undefined); } /// /// INDEX /// /** * Get index information. * * @returns Promise containing index information */ async getRawInfo() { const url = `indexes/${this.uid}`; const res = await this.httpRequest.get(url); this.primaryKey = res.primaryKey; this.updatedAt = new Date(res.updatedAt); this.createdAt = new Date(res.createdAt); return res; } /** * Fetch and update Index information. * * @returns Promise to the current Index object with updated information */ async fetchInfo() { await this.getRawInfo(); return this; } /** * Get Primary Key. * * @returns Promise containing the Primary Key of the index */ async fetchPrimaryKey() { this.primaryKey = (await this.getRawInfo()).primaryKey; return this.primaryKey; } /** * Create an index. * * @param uid - Unique identifier of the Index * @param options - Index options * @param config - Request configuration options * @returns Newly created Index object */ static async create(uid, options = {}, config) { const url = `indexes`; const req = new HttpRequests(config); const task = await req.post(url, { ...options, uid }); return new EnqueuedTask(task); } /** * Update an index. * * @param data - Data to update * @returns Promise to the current Index object with updated information */ async update(data) { const url = `indexes/${this.uid}`; const task = await this.httpRequest.patch(url, data); task.enqueuedAt = new Date(task.enqueuedAt); return task; } /** * Delete an index. * * @returns Promise which resolves when index is deleted successfully */ async delete() { const url = `indexes/${this.uid}`; const task = await this.httpRequest.delete(url); return new EnqueuedTask(task); } /// /// TASKS /// /** * Get the list of all the tasks of the index. * * @param parameters - Parameters to browse the tasks * @returns Promise containing all tasks */ async getTasks(parameters = {}) { return await this.tasks.getTasks({ ...parameters, indexUids: [this.uid] }); } /** * Get one task of the index. * * @param taskUid - Task identifier * @returns Promise containing a task */ async getTask(taskUid) { return await this.tasks.getTask(taskUid); } /** * Wait for multiple tasks to be processed. * * @param taskUids - Tasks identifier * @param waitOptions - Options on timeout and interval * @returns Promise containing an array of tasks */ async waitForTasks(taskUids, { timeOutMs = 5000, intervalMs = 50 } = {}) { return await this.tasks.waitForTasks(taskUids, { timeOutMs, intervalMs, }); } /** * Wait for a task to be processed. * * @param taskUid - Task identifier * @param waitOptions - Options on timeout and interval * @returns Promise containing an array of tasks */ async waitForTask(taskUid, { timeOutMs = 5000, intervalMs = 50 } = {}) { return await this.tasks.waitForTask(taskUid, { timeOutMs, intervalMs, }); } /// /// STATS /// /** * Get stats of an index * * @returns Promise containing object with stats of the index */ async getStats() { const url = `indexes/${this.uid}/stats`; return await this.httpRequest.get(url); } /// /// DOCUMENTS /// /** * Get documents of an index. * * @param parameters - Parameters to browse the documents. Parameters can * contain the `filter` field only available in Meilisearch v1.2 and newer * @returns Promise containing the returned documents */ async getDocuments(parameters = {}) { parameters = removeUndefinedFromObject(parameters); // In case `filter` is provided, use `POST /documents/fetch` if (parameters.filter !== undefined) { try { const url = `indexes/${this.uid}/documents/fetch`; return await this.httpRequest.post(url, parameters); } catch (e) { if (e instanceof MeiliSearchRequestError) { e.message = versionErrorHintMessage(e.message, "getDocuments"); } else if (e instanceof MeiliSearchApiError) { e.message = versionErrorHintMessage(e.message, "getDocuments"); } throw e; } // Else use `GET /documents` method } else { const url = `indexes/${this.uid}/documents`; // Transform fields to query parameter string format const fields = Array.isArray(parameters?.fields) ? { fields: parameters?.fields?.join(",") } : {}; return await this.httpRequest.get(url, { ...parameters, ...fields, }); } } /** * Get one document * * @param documentId - Document ID * @param parameters - Parameters applied on a document * @returns Promise containing Document response */ async getDocument(documentId, parameters) { const url = `indexes/${this.uid}/documents/${documentId}`; const fields = (() => { if (Array.isArray(parameters?.fields)) { return parameters?.fields?.join(","); } return undefined; })(); return await this.httpRequest.get(url, removeUndefinedFromObject({ ...parameters, fields, })); } /** * Add or replace multiples documents to an index * * @param documents - Array of Document objects to add/replace * @param options - Options on document addition * @returns Promise containing an EnqueuedTask */ async addDocuments(documents, options) { const url = `indexes/${this.uid}/documents`; const task = await this.httpRequest.post(url, documents, options); return new EnqueuedTask(task); } /** * Add or replace multiples documents in a string format to an index. It only * supports csv, ndjson and json formats. * * @param documents - Documents provided in a string to add/replace * @param contentType - Content type of your document: * 'text/csv'|'application/x-ndjson'|'application/json' * @param options - Options on document addition * @returns Promise containing an EnqueuedTask */ async addDocumentsFromString(documents, contentType, queryParams) { const url = `indexes/${this.uid}/documents`; const task = await this.httpRequest.post(url, documents, queryParams, { headers: { "Content-Type": contentType, }, }); return new EnqueuedTask(task); } /** * Add or replace multiples documents to an index in batches * * @param documents - Array of Document objects to add/replace * @param batchSize - Size of the batch * @param options - Options on document addition * @returns Promise containing array of enqueued task objects for each batch */ async addDocumentsInBatches(documents, batchSize = 1000, options) { const updates = []; for (let i = 0; i < documents.length; i += batchSize) { updates.push(await this.addDocuments(documents.slice(i, i + batchSize), options)); } return updates; } /** * Add or update multiples documents to an index * * @param documents - Array of Document objects to add/update * @param options - Options on document update * @returns Promise containing an EnqueuedTask */ async updateDocuments(documents, options) { const url = `indexes/${this.uid}/documents`; const task = await this.httpRequest.put(url, documents, options); return new EnqueuedTask(task); } /** * Add or update multiples documents to an index in batches * * @param documents - Array of Document objects to add/update * @param batchSize - Size of the batch * @param options - Options on document update * @returns Promise containing array of enqueued task objects for each batch */ async updateDocumentsInBatches(documents, batchSize = 1000, options) { const updates = []; for (let i = 0; i < documents.length; i += batchSize) { updates.push(await this.updateDocuments(documents.slice(i, i + batchSize), options)); } return updates; } /** * Add or update multiples documents in a string format to an index. It only * supports csv, ndjson and json formats. * * @param documents - Documents provided in a string to add/update * @param contentType - Content type of your document: * 'text/csv'|'application/x-ndjson'|'application/json' * @param queryParams - Options on raw document addition * @returns Promise containing an EnqueuedTask */ async updateDocumentsFromString(documents, contentType, queryParams) { const url = `indexes/${this.uid}/documents`; const task = await this.httpRequest.put(url, documents, queryParams, { headers: { "Content-Type": contentType, }, }); return new EnqueuedTask(task); } /** * Delete one document * * @param documentId - Id of Document to delete * @returns Promise containing an EnqueuedTask */ async deleteDocument(documentId) { const url = `indexes/${this.uid}/documents/${documentId}`; const task = await this.httpRequest.delete(url); task.enqueuedAt = new Date(task.enqueuedAt); return task; } /** * Delete multiples documents of an index. * * @param params - Params value can be: * * - DocumentsDeletionQuery: An object containing the parameters to customize * your document deletion. Only available in Meilisearch v1.2 and newer * - DocumentsIds: An array of document ids to delete * * @returns Promise containing an EnqueuedTask */ async deleteDocuments(params) { // If params is of type DocumentsDeletionQuery const isDocumentsDeletionQuery = !Array.isArray(params) && typeof params === "object"; const endpoint = isDocumentsDeletionQuery ? "documents/delete" : "documents/delete-batch"; const url = `indexes/${this.uid}/${endpoint}`; try { const task = await this.httpRequest.post(url, params); return new EnqueuedTask(task); } catch (e) { if (e instanceof MeiliSearchRequestError && isDocumentsDeletionQuery) { e.message = versionErrorHintMessage(e.message, "deleteDocuments"); } else if (e instanceof MeiliSearchApiError) { e.message = versionErrorHintMessage(e.message, "deleteDocuments"); } throw e; } } /** * Delete all documents of an index * * @returns Promise containing an EnqueuedTask */ async deleteAllDocuments() { const url = `indexes/${this.uid}/documents`; const task = await this.httpRequest.delete(url); task.enqueuedAt = new Date(task.enqueuedAt); return task; } /** * This is an EXPERIMENTAL feature, which may break without a major version. * It's available after Meilisearch v1.10. * * More info about the feature: * https://github.com/orgs/meilisearch/discussions/762 More info about * experimental features in general: * https://www.meilisearch.com/docs/reference/api/experimental-features * * @param options - Object containing the function string and related options * @returns Promise containing an EnqueuedTask */ async updateDocumentsByFunction(options) { const url = `indexes/${this.uid}/documents/edit`; const task = await this.httpRequest.post(url, options); return new EnqueuedTask(task); } /// /// SETTINGS /// /** * Retrieve all settings * * @returns Promise containing Settings object */ async getSettings() { const url = `indexes/${this.uid}/settings`; return await this.httpRequest.get(url); } /** * Update all settings Any parameters not provided will be left unchanged. * * @param settings - Object containing parameters with their updated values * @returns Promise containing an EnqueuedTask */ async updateSettings(settings) { const url = `indexes/${this.uid}/settings`; const task = await this.httpRequest.patch(url, settings); task.enqueued = new Date(task.enqueuedAt); return task; } /** * Reset settings. * * @returns Promise containing an EnqueuedTask */ async resetSettings() { const url = `indexes/${this.uid}/settings`; const task = await this.httpRequest.delete(url); task.enqueuedAt = new Date(task.enqueuedAt); return task; } /// /// PAGINATION SETTINGS /// /** * Get the pagination settings. * * @returns Promise containing object of pagination settings */ async getPagination() { const url = `indexes/${this.uid}/settings/pagination`; return await this.httpRequest.get(url); } /** * Update the pagination settings. * * @param pagination - Pagination object * @returns Promise containing an EnqueuedTask */ async updatePagination(pagination) { const url = `indexes/${this.uid}/settings/pagination`; const task = await this.httpRequest.patch(url, pagination); return new EnqueuedTask(task); } /** * Reset the pagination settings. * * @returns Promise containing an EnqueuedTask */ async resetPagination() { const url = `indexes/${this.uid}/settings/pagination`; const task = await this.httpRequest.delete(url); return new EnqueuedTask(task); } /// /// SYNONYMS /// /** * Get the list of all synonyms * * @returns Promise containing object of synonym mappings */ async getSynonyms() { const url = `indexes/${this.uid}/settings/synonyms`; return await this.httpRequest.get(url); } /** * Update the list of synonyms. Overwrite the old list. * * @param synonyms - Mapping of synonyms with their associated words * @returns Promise containing an EnqueuedTask */ async updateSynonyms(synonyms) { const url = `indexes/${this.uid}/settings/synonyms`; const task = await this.httpRequest.put(url, synonyms); return new EnqueuedTask(task); } /** * Reset the synonym list to be empty again * * @returns Promise containing an EnqueuedTask */ async resetSynonyms() { const url = `indexes/${this.uid}/settings/synonyms`; const task = await this.httpRequest.delete(url); task.enqueuedAt = new Date(task.enqueuedAt); return task; } /// /// STOP WORDS /// /** * Get the list of all stop-words * * @returns Promise containing array of stop-words */ async getStopWords() { const url = `indexes/${this.uid}/settings/stop-words`; return await this.httpRequest.get(url); } /** * Update the list of stop-words. Overwrite the old list. * * @param stopWords - Array of strings that contains the stop-words. * @returns Promise containing an EnqueuedTask */ async updateStopWords(stopWords) { const url = `indexes/${this.uid}/settings/stop-words`; const task = await this.httpRequest.put(url, stopWords); return new EnqueuedTask(task); } /** * Reset the stop-words list to be empty again * * @returns Promise containing an EnqueuedTask */ async resetStopWords() { const url = `indexes/${this.uid}/settings/stop-words`; const task = await this.httpRequest.delete(url); task.enqueuedAt = new Date(task.enqueuedAt); return task; } /// /// RANKING RULES /// /** * Get the list of all ranking-rules * * @returns Promise containing array of ranking-rules */ async getRankingRules() { const url = `indexes/${this.uid}/settings/ranking-rules`; return await this.httpRequest.get(url); } /** * Update the list of ranking-rules. Overwrite the old list. * * @param rankingRules - Array that contain ranking rules sorted by order of * importance. * @returns Promise containing an EnqueuedTask */ async updateRankingRules(rankingRules) { const url = `indexes/${this.uid}/settings/ranking-rules`; const task = await this.httpRequest.put(url, rankingRules); return new EnqueuedTask(task); } /** * Reset the ranking rules list to its default value * * @returns Promise containing an EnqueuedTask */ async resetRankingRules() { const url = `indexes/${this.uid}/settings/ranking-rules`; const task = await this.httpRequest.delete(url); task.enqueuedAt = new Date(task.enqueuedAt); return task; } /// /// DISTINCT ATTRIBUTE /// /** * Get the distinct-attribute * * @returns Promise containing the distinct-attribute of the index */ async getDistinctAttribute() { const url = `indexes/${this.uid}/settings/distinct-attribute`; return await this.httpRequest.get(url); } /** * Update the distinct-attribute. * * @param distinctAttribute - Field name of the distinct-attribute * @returns Promise containing an EnqueuedTask */ async updateDistinctAttribute(distinctAttribute) { const url = `indexes/${this.uid}/settings/distinct-attribute`; const task = await this.httpRequest.put(url, distinctAttribute); return new EnqueuedTask(task); } /** * Reset the distinct-attribute. * * @returns Promise containing an EnqueuedTask */ async resetDistinctAttribute() { const url = `indexes/${this.uid}/settings/distinct-attribute`; const task = await this.httpRequest.delete(url); task.enqueuedAt = new Date(task.enqueuedAt); return task; } /// /// FILTERABLE ATTRIBUTES /// /** * Get the filterable-attributes * * @returns Promise containing an array of filterable-attributes */ async getFilterableAttributes() { const url = `indexes/${this.uid}/settings/filterable-attributes`; return await this.httpRequest.get(url); } /** * Update the filterable-attributes. * * @param filterableAttributes - Array of strings containing the attributes * that can be used as filters at query time * @returns Promise containing an EnqueuedTask */ async updateFilterableAttributes(filterableAttributes) { const url = `indexes/${this.uid}/settings/filterable-attributes`; const task = await this.httpRequest.put(url, filterableAttributes); return new EnqueuedTask(task); } /** * Reset the filterable-attributes. * * @returns Promise containing an EnqueuedTask */ async resetFilterableAttributes() { const url = `indexes/${this.uid}/settings/filterable-attributes`; const task = await this.httpRequest.delete(url); task.enqueuedAt = new Date(task.enqueuedAt); return task; } /// /// SORTABLE ATTRIBUTES /// /** * Get the sortable-attributes * * @returns Promise containing array of sortable-attributes */ async getSortableAttributes() { const url = `indexes/${this.uid}/settings/sortable-attributes`; return await this.httpRequest.get(url); } /** * Update the sortable-attributes. * * @param sortableAttributes - Array of strings containing the attributes that * can be used to sort search results at query time * @returns Promise containing an EnqueuedTask */ async updateSortableAttributes(sortableAttributes) { const url = `indexes/${this.uid}/settings/sortable-attributes`; const task = await this.httpRequest.put(url, sortableAttributes); return new EnqueuedTask(task); } /** * Reset the sortable-attributes. * * @returns Promise containing an EnqueuedTask */ async resetSortableAttributes() { const url = `indexes/${this.uid}/settings/sortable-attributes`; const task = await this.httpRequest.delete(url); task.enqueuedAt = new Date(task.enqueuedAt); return task; } /// /// SEARCHABLE ATTRIBUTE /// /** * Get the searchable-attributes * * @returns Promise containing array of searchable-attributes */ async getSearchableAttributes() { const url = `indexes/${this.uid}/settings/searchable-attributes`; return await this.httpRequest.get(url); } /** * Update the searchable-attributes. * * @param searchableAttributes - Array of strings that contains searchable * attributes sorted by order of importance(most to least important) * @returns Promise containing an EnqueuedTask */ async updateSearchableAttributes(searchableAttributes) { const url = `indexes/${this.uid}/settings/searchable-attributes`; const task = await this.httpRequest.put(url, searchableAttributes); return new EnqueuedTask(task); } /** * Reset the searchable-attributes. * * @returns Promise containing an EnqueuedTask */ async resetSearchableAttributes() { const url = `indexes/${this.uid}/settings/searchable-attributes`; const task = await this.httpRequest.delete(url); task.enqueuedAt = new Date(task.enqueuedAt); return task; } /// /// DISPLAYED ATTRIBUTE /// /** * Get the displayed-attributes * * @returns Promise containing array of displayed-attributes */ async getDisplayedAttributes() { const url = `indexes/${this.uid}/settings/displayed-attributes`; return await this.httpRequest.get(url); } /** * Update the displayed-attributes. * * @param displayedAttributes - Array of strings that contains attributes of * an index to display * @returns Promise containing an EnqueuedTask */ async updateDisplayedAttributes(displayedAttributes) { const url = `indexes/${this.uid}/settings/displayed-attributes`; const task = await this.httpRequest.put(url, displayedAttributes); return new EnqueuedTask(task); } /** * Reset the displayed-attributes. * * @returns Promise containing an EnqueuedTask */ async resetDisplayedAttributes() { const url = `indexes/${this.uid}/settings/displayed-attributes`; const task = await this.httpRequest.delete(url); task.enqueuedAt = new Date(task.enqueuedAt); return task; } /// /// TYPO TOLERANCE /// /** * Get the typo tolerance settings. * * @returns Promise containing the typo tolerance settings. */ async getTypoTolerance() { const url = `indexes/${this.uid}/settings/typo-tolerance`; return await this.httpRequest.get(url); } /** * Update the typo tolerance settings. * * @param typoTolerance - Object containing the custom typo tolerance * settings. * @returns Promise containing object of the enqueued update */ async updateTypoTolerance(typoTolerance) { const url = `indexes/${this.uid}/settings/typo-tolerance`; const task = await this.httpRequest.patch(url, typoTolerance); task.enqueuedAt = new Date(task.enqueuedAt); return task; } /** * Reset the typo tolerance settings. * * @returns Promise containing object of the enqueued update */ async resetTypoTolerance() { const url = `indexes/${this.uid}/settings/typo-tolerance`; const task = await this.httpRequest.delete(url); task.enqueuedAt = new Date(task.enqueuedAt); return task; } /// /// FACETING /// /** * Get the faceting settings. * * @returns Promise containing object of faceting index settings */ async getFaceting() { const url = `indexes/${this.uid}/settings/faceting`; return await this.httpRequest.get(url); } /** * Update the faceting settings. * * @param faceting - Faceting index settings object * @returns Promise containing an EnqueuedTask */ async updateFaceting(faceting) { const url = `indexes/${this.uid}/settings/faceting`; const task = await this.httpRequest.patch(url, faceting); return new EnqueuedTask(task); } /** * Reset the faceting settings. * * @returns Promise containing an EnqueuedTask */ async resetFaceting() { const url = `indexes/${this.uid}/settings/faceting`; const task = await this.httpRequest.delete(url); return new EnqueuedTask(task); } /// /// SEPARATOR TOKENS /// /** * Get the list of all separator tokens. * * @returns Promise containing array of separator tokens */ async getSeparatorTokens() { const url = `indexes/${this.uid}/settings/separator-tokens`; return await this.httpRequest.get(url); } /** * Update the list of separator tokens. Overwrite the old list. * * @param separatorTokens - Array that contains separator tokens. * @returns Promise containing an EnqueuedTask or null */ async updateSeparatorTokens(separatorTokens) { const url = `indexes/${this.uid}/settings/separator-tokens`; const task = await this.httpRequest.put(url, separatorTokens); return new EnqueuedTask(task); } /** * Reset the separator tokens list to its default value * * @returns Promise containing an EnqueuedTask */ async resetSeparatorTokens() { const url = `indexes/${this.uid}/settings/separator-tokens`; const task = await this.httpRequest.delete(url); task.enqueuedAt = new Date(task.enqueuedAt); return task; } /// /// NON-SEPARATOR TOKENS /// /** * Get the list of all non-separator tokens. * * @returns Promise containing array of non-separator tokens */ async getNonSeparatorTokens() { const url = `indexes/${this.uid}/settings/non-separator-tokens`; return await this.httpRequest.get(url); } /** * Update the list of non-separator tokens. Overwrite the old list. * * @param nonSeparatorTokens - Array that contains non-separator tokens. * @returns Promise containing an EnqueuedTask or null */ async updateNonSeparatorTokens(nonSeparatorTokens) { const url = `indexes/${this.uid}/settings/non-separator-tokens`; const task = await this.httpRequest.put(url, nonSeparatorTokens); return new EnqueuedTask(task); } /** * Reset the non-separator tokens list to its default value * * @returns Promise containing an EnqueuedTask */ async resetNonSeparatorTokens() { const url = `indexes/${this.uid}/settings/non-separator-tokens`; const task = await this.httpRequest.delete(url); task.enqueuedAt = new Date(task.enqueuedAt); return task; } /// /// DICTIONARY /// /** * Get the dictionary settings of a Meilisearch index. * * @returns Promise containing the dictionary settings */ async getDictionary() { const url = `indexes/${this.uid}/settings/dictionary`; return await this.httpRequest.get(url); } /** * Update the dictionary settings. Overwrite the old settings. * * @param dictionary - Array that contains the new dictionary settings. * @returns Promise containing an EnqueuedTask or null */ async updateDictionary(dictionary) { const url = `indexes/${this.uid}/settings/dictionary`; const task = await this.httpRequest.put(url, dictionary); return new EnqueuedTask(task); } /** * Reset the dictionary settings to its default value * * @returns Promise containing an EnqueuedTask */ async resetDictionary() { const url = `indexes/${this.uid}/settings/dictionary`; const task = await this.httpRequest.delete(url); task.enqueuedAt = new Date(task.enqueuedAt); return task; } /// /// PROXIMITY PRECISION /// /** * Get the proximity precision settings of a Meilisearch index. * * @returns Promise containing the proximity precision settings */ async getProximityPrecision() { const url = `indexes/${this.uid}/settings/proximity-precision`; return await this.httpRequest.get(url); } /** * Update the proximity precision settings. Overwrite the old settings. * * @param proximityPrecision - String that contains the new proximity * precision settings. * @returns Promise containing an EnqueuedTask or null */ async updateProximityPrecision(proximityPrecision) { const url = `indexes/${this.uid}/settings/proximity-precision`; const task = await this.httpRequest.put(url, proximityPrecision); return new EnqueuedTask(task); } /** * Reset the proximity precision settings to its default value * * @returns Promise containing an EnqueuedTask */ async resetProximityPrecision() { const url = `indexes/${this.uid}/settings/proximity-precision`; const task = await this.httpRequest.delete(url); task.enqueuedAt = new Date(task.enqueuedAt); return task; } /// /// EMBEDDERS /// /** * Get the embedders settings of a Meilisearch index. * * @returns Promise containing the embedders settings */ async getEmbedders() { const url = `indexes/${this.uid}/settings/embedders`; return await this.httpRequest.get(url); } /** * Update the embedders settings. Overwrite the old settings. * * @param embedders - Object that contains the new embedders settings. * @returns Promise containing an EnqueuedTask or null */ async updateEmbedders(embedders) { const url = `indexes/${this.uid}/settings/embedders`; const task = await this.httpRequest.patch(url, embedders); return new EnqueuedTask(task); } /** * Reset the embedders settings to its default value * * @returns Promise containing an EnqueuedTask */ async resetEmbedders() { const url = `indexes/${this.uid}/settings/embedders`; const task = await this.httpRequest.delete(url); task.enqueuedAt = new Date(task.enqueuedAt); return task; } /// /// SEARCHCUTOFFMS SETTINGS /// /** * Get the SearchCutoffMs settings. * * @returns Promise containing object of SearchCutoffMs settings */ async getSearchCutoffMs() { const url = `indexes/${this.uid}/settings/search-cutoff-ms`; return await this.httpRequest.get(url); } /** * Update the SearchCutoffMs settings. * * @param searchCutoffMs - Object containing SearchCutoffMsSettings * @returns Promise containing an EnqueuedTask */ async updateSearchCutoffMs(searchCutoffMs) { const url = `indexes/${this.uid}/settings/search-cutoff-ms`; const task = await this.httpRequest.put(url, searchCutoffMs); return new EnqueuedTask(task); } /** * Reset the SearchCutoffMs settings. * * @returns Promise containing an EnqueuedTask */ async resetSearchCutoffMs() { const url = `indexes/${this.uid}/settings/search-cutoff-ms`; const task = await this.httpRequest.delete(url); return new EnqueuedTask(task); } /// /// LOCALIZED ATTRIBUTES SETTINGS /// /** * Get the localized attributes settings. * * @returns Promise containing object of localized attributes settings */ async getLocalizedAttributes() { const url = `indexes/${this.uid}/settings/localized-attributes`; return await this.httpRequest.get(url); } /** * Update the localized attributes settings. * * @param localizedAttributes - Localized attributes object * @returns Promise containing an EnqueuedTask */ async updateLocalizedAttributes(localizedAttributes) { const url = `indexes/${this.uid}/settings/localized-attributes`; const task = await this.httpRequest.put(url, localizedAttributes); return new EnqueuedTask(task); } /** * Reset the localized attributes settings. * * @returns Promise containing an EnqueuedTask */ async resetLocalizedAttributes() { const url = `indexes/${this.uid}/settings/localized-attributes`; const task = await this.httpRequest.delete(url); return new EnqueuedTask(task); } } /* * Bundle: MeiliSearch * Project: MeiliSearch - Javascript API * Author: Quentin de Quelen * Copyright: 2019, MeiliSearch */ class Client { config; httpRequest; tasks; /** * Creates new MeiliSearch instance * * @param config - Configuration object */ constructor(config) { this.config = config; this.httpRequest = new HttpRequests(config); this.tasks = new TaskClient(config); } /** * Return an Index instance * * @param indexUid - The index UID * @returns Instance of Index */ index(indexUid) { return new Index(this.config, indexUid); } /** * Gather information about an index by calling MeiliSearch and return an * Index instance with the gathered information * * @param indexUid - The index UID * @returns Promise returning Index instance */ async getIndex(indexUid) { return new Index(this.config, indexUid).fetchInfo(); } /** * Gather information about an index by calling MeiliSearch and return the raw * JSON response * * @param indexUid - The index UID * @returns Promise returning index information */ async getRawIndex(indexUid) { return new Index(this.config, indexUid).getRawInfo(); } /** * Get all the indexes as Index instances. * * @param parameters - Parameters to browse the indexes * @returns Promise returning array of raw index information */ async getIndexes(parameters = {}) { const rawIndexes = await this.getRawIndexes(parameters); const indexes = rawIndexes.results.map((index) => new Index(this.config, index.uid, index.primaryKey)); return { ...rawIndexes, results: indexes }; } /** * Get all the indexes in their raw value (no Index instances). * * @param parameters - Parameters to browse the indexes * @returns Promise returning array of raw index information */ async getRawIndexes(parameters = {}) { const url = `indexes`; return await this.httpRequest.get(url, parameters); } /** * Create a new index * * @param uid - The index UID * @param options - Index options * @returns Promise returning Index instance */ async createIndex(uid, options = {}) { return await Index.create(uid, options, this.config); } /** * Update an index * * @param uid - The index UID * @param options - Index options to update * @returns Promise returning Index instance after updating */ async updateIndex(uid, options = {}) { return await new Index(this.config, uid).update(options); } /** * Delete an index * * @param uid - The index UID * @returns Promise which resolves when index is deleted successfully */ async deleteIndex(uid) { return await new Index(this.config, uid).delete(); } /** * Deletes an index if it already exists. * * @param uid - The index UID * @returns Promise which resolves to true when index exists and is deleted * successfully, otherwise false if it does not exist */ async deleteIndexIfExists(uid) { try { await this.deleteIndex(uid); return true; } catch (e) { if (e.code === ErrorStatusCode.INDEX_NOT_FOUND) { return false; } throw e; } } /** * Swaps a list of index tuples. * * @param params - List of indexes tuples to swap. * @returns Promise returning object of the enqueued task */ async swapIndexes(params) { const url = "/swap-indexes"; return await this.httpRequest.post(url, params); } async multiSearch(queries, config) { const url = `multi-search`; return await this.httpRequest.post(url, queries, undefined, config); } /// /// TASKS /// /** * Get the list of all client tasks * * @param parameters - Parameters to browse the tasks * @returns Promise returning all tasks */ async getTasks(parameters = {}) { return await this.tasks.getTasks(parameters); } /** * Get one task on the client scope * * @param taskUid - Task identifier * @returns Promise returning a task */ async getTask(taskUid) { return await this.tasks.getTask(taskUid); } /** * Wait for multiple tasks to be finished. * * @param taskUids - Tasks identifier * @param waitOptions - Options on timeout and interval * @returns Promise returning an array of tasks */ async waitForTasks(taskUids, { timeOutMs = 5000, intervalMs = 50 } = {}) { return await this.tasks.waitForTasks(taskUids, { timeOutMs, intervalMs, }); } /** * Wait for a task to be finished. * * @param taskUid - Task identifier * @param waitOptions - Options on timeout and interval * @returns Promise returning an array of tasks */ async waitForTask(taskUid, { timeOutMs = 5000, intervalMs = 50 } = {}) { return await this.tasks.waitForTask(taskUid, { timeOutMs, intervalMs, }); } /** * Cancel a list of enqueued or processing tasks. * * @param parameters - Parameters to filter the tasks. * @returns Promise containing an EnqueuedTask */ async cancelTasks(parameters) { return await this.tasks.cancelTasks(parameters); } /** * Delete a list of tasks. * * @param parameters - Parameters to filter the tasks. * @returns Promise containing an EnqueuedTask */ async deleteTasks(parameters = {}) { return await this.tasks.deleteTasks(parameters); } /// /// KEYS /// /** * Get all API keys * * @param parameters - Parameters to browse the indexes * @returns Promise returning an object with keys */ async getKeys(parameters = {}) { const url = `keys`; const keys = await this.httpRequest.get(url, parameters); keys.results = keys.results.map((key) => ({ ...key, createdAt: new Date(key.createdAt), updatedAt: new Date(key.updatedAt), })); return keys; } /** * Get one API key * * @param keyOrUid - Key or uid of the API key * @returns Promise returning a key */ async getKey(keyOrUid) { const url = `keys/${keyOrUid}`; return await this.httpRequest.get(url); } /** * Create one API key * * @param options - Key options * @returns Promise returning a key */ async createKey(options) { const url = `keys`; return await this.httpRequest.post(url, options); } /** * Update one API key * * @param keyOrUid - Key * @param options - Key options * @returns Promise returning a key */ async updateKey(keyOrUid, options) { const url = `keys/${keyOrUid}`; return await this.httpRequest.patch(url, options); } /** * Delete one API key * * @param keyOrUid - Key * @returns */ async deleteKey(keyOrUid) { const url = `keys/${keyOrUid}`; return await this.httpRequest.delete(url); } /// /// HEALTH /// /** * Checks if the server is healthy, otherwise an error will be thrown. * * @returns Promise returning an object with health details */ async health() { const url = `health`; return await this.httpRequest.get(url); } /** * Checks if the server is healthy, return true or false. * * @returns Promise returning a boolean */ async isHealthy() { try { const url = `health`; await this.httpRequest.get(url); return true; } catch { return false; } } /// /// STATS /// /** * Get the stats of all the database * * @returns Promise returning object of all the stats */ async getStats() { const url = `stats`; return await this.httpRequest.get(url); } /// /// VERSION /// /** * Get the version of MeiliSearch * * @returns Promise returning object with version details */ async getVersion() { const url = `version`; return await this.httpRequest.get(url); } /// /// DUMPS /// /** * Creates a dump * * @returns Promise returning object of the enqueued task */ async createDump() { const url = `dumps`; const task = await this.httpRequest.post(url); return new EnqueuedTask(task); } /// /// SNAPSHOTS /// /** * Creates a snapshot * * @returns Promise returning object of the enqueued task */ async createSnapshot() { const url = `snapshots`; const task = await this.httpRequest.post(url); return new EnqueuedTask(task); } /// /// TOKENS /// /** * Generate a tenant token * * @param apiKeyUid - The uid of the api key used as issuer of the token. * @param searchRules - Search rules that are applied to every search. * @param options - Token options to customize some aspect of the token. * @returns The token in JWT format. */ generateTenantToken(_apiKeyUid, _searchRules, _options) { const error = new Error(); error.message = `Meilisearch: failed to generate a tenant token. Generation of a token only works in a node environment \n ${error.stack}.`; return Promise.reject(error); } } function encode64(data) { return Buffer.from(JSON.stringify(data)).toString("base64"); } /** * Create the header of the token. * * @param apiKey - API key used to sign the token. * @param encodedHeader - Header of the token in base64. * @param encodedPayload - Payload of the token in base64. * @returns The signature of the token in base64. */ async function sign(apiKey, encodedHeader, encodedPayload) { const { createHmac } = await import('node:crypto'); return createHmac("sha256", apiKey) .update(`${encodedHeader}.${encodedPayload}`) .digest("base64") .replace(/\+/g, "-") .replace(/\//g, "_") .replace(/=/g, ""); } /** * Create the header of the token. * * @returns The header encoded in base64. */ function createHeader() { const header = { alg: "HS256", typ: "JWT", }; return encode64(header).replace(/=/g, ""); } /** * Validate the parameter used for the payload of the token. * * @param searchRules - Search rules that are applied to every search. * @param apiKey - Api key used as issuer of the token. * @param uid - The uid of the api key used as issuer of the token. * @param expiresAt - Date at which the token expires. */ function validateTokenParameters(tokenParams) { const { searchRules, uid, apiKey, expiresAt } = tokenParams; if (expiresAt) { if (!(expiresAt instanceof Date)) { throw new MeiliSearchError(`Meilisearch: The expiredAt field must be an instance of Date.`); } else if (expiresAt.getTime() < Date.now()) { throw new MeiliSearchError(`Meilisearch: The expiresAt field must be a date in the future.`); } } if (searchRules) { if (!(typeof searchRules === "object" || Array.isArray(searchRules))) { throw new MeiliSearchError(`Meilisearch: The search rules added in the token generation must be of type array or object.`); } } if (!apiKey || typeof apiKey !== "string") { throw new MeiliSearchError(`Meilisearch: The API key used for the token generation must exist and be of type string.`); } if (!uid || typeof uid !== "string") { throw new MeiliSearchError(`Meilisearch: The uid of the api key used for the token generation must exist, be of type string and comply to the uuid4 format.`); } if (!validateUuid4(uid)) { throw new MeiliSearchError(`Meilisearch: The uid of your key is not a valid uuid4. To find out the uid of your key use getKey().`); } } /** * Create the payload of the token. * * @param searchRules - Search rules that are applied to every search. * @param uid - The uid of the api key used as issuer of the token. * @param expiresAt - Date at which the token expires. * @returns The payload encoded in base64. */ function createPayload(payloadParams) { const { searchRules, uid, expiresAt } = payloadParams; const payload = { searchRules, apiKeyUid: uid, exp: expiresAt ? Math.floor(expiresAt.getTime() / 1000) : undefined, }; return encode64(payload).replace(/=/g, ""); } class Token { config; constructor(config) { this.config = config; } /** * Generate a tenant token * * @param apiKeyUid - The uid of the api key used as issuer of the token. * @param searchRules - Search rules that are applied to every search. * @param options - Token options to customize some aspect of the token. * @returns The token in JWT format. */ async generateTenantToken(apiKeyUid, searchRules, options) { const apiKey = options?.apiKey || this.config.apiKey || ""; const uid = apiKeyUid || ""; const expiresAt = options?.expiresAt; validateTokenParameters({ apiKey, uid, expiresAt, searchRules }); const encodedHeader = createHeader(); const encodedPayload = createPayload({ searchRules, uid, expiresAt }); const signature = await sign(apiKey, encodedHeader, encodedPayload); return `${encodedHeader}.${encodedPayload}.${signature}`; } } class MeiliSearch extends Client { tokens; constructor(config) { super(config); this.tokens = new Token(config); } /** * Generate a tenant token * * @param apiKeyUid - The uid of the api key used as issuer of the token. * @param searchRules - Search rules that are applied to every search. * @param options - Token options to customize some aspect of the token. * @returns The token in JWT format. */ async generateTenantToken(apiKeyUid, searchRules, options) { if (typeof window === "undefined") { return await this.tokens.generateTenantToken(apiKeyUid, searchRules, options); } return await super.generateTenantToken(apiKeyUid, searchRules, options); } } exports.ContentTypeEnum = ContentTypeEnum; exports.EnqueuedTask = EnqueuedTask; exports.ErrorStatusCode = ErrorStatusCode; exports.Index = Index; exports.MatchingStrategies = MatchingStrategies; exports.MeiliSearch = MeiliSearch; exports.MeiliSearchApiError = MeiliSearchApiError; exports.MeiliSearchError = MeiliSearchError; exports.MeiliSearchRequestError = MeiliSearchRequestError; exports.MeiliSearchTimeOutError = MeiliSearchTimeOutError; exports.Meilisearch = MeiliSearch; exports.Task = Task; exports.TaskClient = TaskClient; exports.TaskStatus = TaskStatus; exports.TaskTypes = TaskTypes; exports.default = MeiliSearch; exports.versionErrorHintMessage = versionErrorHintMessage; //# sourceMappingURL=meilisearch.cjs.map