UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

171 lines • 7.2 kB
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= // 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. // ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= import { recordQueries } from "../../utils/record_search.js"; import { iterDocuments } from "./client.js"; import { entityGuard } from "./readdir.js"; import { detectScope } from "./scope.js"; import { applyElision, elisionPaths, renderDoc } from "./stream.js"; import { PRIMARY_KEY } from "./types.js"; // The words a document's line holds whatever its keys and strings say: // Extended JSON's type wrappers and their keys, the JSON literals, a double's // NaN and Infinity and a date's Z. Mirrors Python's `RECORD_KEYS`. export const RECORD_KEYS = new Set([ 'oid', 'date', 'numberlong', 'numberint', 'numberdouble', 'numberdecimal', 'binary', 'base64', 'subtype', 'uuid', 'regularexpression', 'pattern', 'options', 'code', 'scope', 'symbol', 'timestamp', 't', 'i', 'minkey', 'maxkey', 'dbpointer', 'ref', 'id', 'undefined', 'true', 'false', 'null', 'nan', 'infinity', 'z', ]); // Value types whose line spells letters no key or string of the document holds // (a binary's base64, a regex's pattern, code, a symbol), so a document holding // one is always a candidate. const OPAQUE = ['binData', 'regex', 'javascript', 'javascriptWithScope', 'symbol', 'dbPointer']; // How deep the walk looks into nested documents and arrays; a document nested // deeper is a candidate. export const DEPTH = 8; // A line spells a character outside ASCII as a `\uXXXX` escape on one host, // whose letters a text starting with `u` or a hex letter can begin inside. const ESCAPE_STARTS = new Set('abcdefu'); function bind(test, value) { return { $let: { vars: { tested: value }, in: test } }; } // One level of the walk: test each key and string of the values at `level`, // and gather what nests inside them for the next. Mirrors Python's `_step`. function step(level, test) { return { $reduce: { input: `$$${level}.next`, initialValue: { hit: `$$${level}.hit`, next: [] }, in: { $switch: { branches: [ { case: { $eq: [{ $type: '$$this' }, 'string'] }, then: { hit: { $or: ['$$value.hit', bind(test, '$$this')] }, next: '$$value.next' }, }, { case: { $eq: [{ $type: '$$this' }, 'object'] }, then: { $let: { vars: { pairs: { $objectToArray: '$$this' } }, in: { hit: { $or: [ '$$value.hit', { $anyElementTrue: [ { $map: { input: '$$pairs', as: 'pair', in: bind(test, '$$pair.k') } }, ], }, ], }, next: { $concatArrays: ['$$value.next', '$$pairs.v'] }, }, }, }, }, { case: { $eq: [{ $type: '$$this' }, 'array'] }, then: { hit: '$$value.hit', next: { $concatArrays: ['$$value.next', '$$this'] } }, }, { case: { $in: [{ $type: '$$this' }, OPAQUE] }, then: { hit: true, next: '$$value.next' }, }, ], default: '$$value', }, }, }, }; } /** * A find filter for the documents whose line may hold one of `queries`: a key * or a string at any depth holds it in any case, a string holds a character * the line may spell as an escape, or a value is one the walk cannot see into. * Mirrors Python's `holds_filter`. */ export function holdsFilter(queries) { const words = { $regexMatch: { input: '$$tested', regex: queries.join('|'), options: 'i' } }; const test = queries.some((query) => ESCAPE_STARTS.has((query[0] ?? '').toLowerCase())) ? { $or: [words, { $regexMatch: { input: '$$tested', regex: '[^\\x00-\\x7f]' } }] } : words; let body = { $or: [`$$s${String(DEPTH)}.hit`, { $gt: [{ $size: `$$s${String(DEPTH)}.next` }, 0] }], }; for (let depth = DEPTH - 1; depth >= 0; depth -= 1) { body = { $let: { vars: { [`s${String(depth + 1)}`]: step(`s${String(depth)}`, test) }, in: body }, }; } return { $expr: { $let: { vars: { s0: { hit: false, next: ['$$ROOT'] } }, in: body } } }; } /** * The lines of a collection's or view's documents.jsonl that may hold `text`, * streamed in the `_id` order a plain read takes. MongoDB walks each * document's keys and strings on the server (`holdsFilter`), so only the * documents that may match are sent; grep matches each line itself. `text` * goes through `recordQueries` for the words of Extended JSON's wrappers and * for escapes. null for any other file and for a text that could match outside * a key or a string. Mirrors Python's `lines_containing`. */ export async function linesContaining(accessor, path, text) { const match = detectScope(path); if (match.kind !== 'documents') return null; const queries = recordQueries(text, RECORD_KEYS, false); if (queries === null) return null; await entityGuard(accessor, match, path.virtual); return matching(accessor, match.slots.database ?? '', match.slots.name ?? '', queries); } async function* matching(accessor, database, name, queries) { const elide = elisionPaths(accessor, database, name); const encoder = new TextEncoder(); for await (const doc of iterDocuments(accessor, database, name, { filter: holdsFilter(queries), sort: { [PRIMARY_KEY]: 1 }, batchSize: 100, })) { const final = elide.size > 0 ? applyElision(doc, elide) : doc; yield encoder.encode(`${renderDoc(final)}\n`); } } //# sourceMappingURL=search.js.map