UNPKG

@kinvolk/headlamp-plugin

Version:

The needed infrastructure for building Headlamp plugins.

84 lines (83 loc) 3.4 kB
/* * Copyright 2025 The Kubernetes Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import jsep from 'jsep'; import evaluate from 'simple-eval'; import { getTopLevelKeys } from './getTopLevelKeys'; /** * Performs an asynchronous search on a collection of items using a custom query function. * The search process can be interrupted by setting the interruptRef to true. * * @param {any[]} items - The array of items to search through. Each item is expected to have a jsonData property. * @param {string} query - A string expression that will be converted into a query function. This should be a valid JavaScript expression that returns a boolean. * @param {Object} interruptRef - A ref object with a `current` property that can be set to true to interrupt the search. */ export async function searchWithQuery(items, query, interruptRef) { const searchTimeStart = performance.now(); const results = []; let filterExpression; const functionBody = query; try { filterExpression = jsep(functionBody); } catch (e) { // Intentionally empty: parse errors are expected as users type partial queries } if (!filterExpression) return { results: [], searchTimeMs: 0 }; const batchSize = 5000; // Create object that contains all keys of all objects const dummyObject = {}; getTopLevelKeys(items.map(it => it.jsonData)).forEach(key => (dummyObject[key] = undefined)); let evalErrorCount = 0; let firstEvalError = null; let firstFailedResource = null; for (let i = 0; i < items.length; i++) { const resource = items[i]; const data = resource.jsonData; try { const res = evaluate(filterExpression, { ...dummyObject, ...data }); if (res === true) { results.push(resource); } } catch (e) { evalErrorCount++; if (!firstEvalError) { firstEvalError = e; firstFailedResource = resource; } } // After processing a batch or at the end, yield to the event loop if ((i + 1) % batchSize === 0 || i === items.length - 1) { // Give the browser a chance to handle other events (like clicks) await new Promise(resolve => setTimeout(resolve, 0)); // Check interrupt again *after* yielding if (interruptRef.current) { return { results: [], searchTimeMs: 0 }; } } } if (evalErrorCount > 0) { console.debug('searchWithQuery: failed to evaluate filter expression', { errorCount: evalErrorCount, firstError: firstEvalError, firstFailedResource, }); } const searchTimeEnd = performance.now(); const searchTimeMs = searchTimeEnd - searchTimeStart; return { results, searchTimeMs }; }