// ========= 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 { Double, MongoClient } from "mongodb"; import { CommandSafeguard, MongoDBResource, MountMode, Workspace, } from "@struktoai/mirage-node"; import { runNotFound, runProvisionProbe, runSedReadonlyProbe } from "./cases.ts"; const MONGODB_URI = process.env.MONGODB_URI ?? "mongodb://localhost:27017"; const DB = "mirage_integ"; const MOUNT = "/mongodb"; const DEC = new TextDecoder(); const BOOKS = [ { _id: 1, title: "alpha", author: "ada", year: 2020, tags: ["fiction", "classic"], rating: 4.5 }, { _id: 2, title: "beta", author: "ben", year: 2021, tags: ["fiction"], rating: 3.2 }, { _id: 3, title: "gamma", author: "cara", year: 2022, rating: 5.0 }, { _id: 4, title: "delta", author: "ada", year: 2023, tags: ["history"], rating: 4.0 }, { _id: 5, title: "epsilon", author: "ben", year: 2024, rating: 2.5 }, ]; const AUTHORS = [ { _id: 1, name: "ada", books: 2 }, { _id: 2, name: "ben", books: 2 }, { _id: 3, name: "cara", books: 1 }, ]; const CASES: ReadonlyArray = [ ["ls_root", `ls ${MOUNT}/`], ["ls_db", `ls ${MOUNT}/${DB}/`], ["ls_collections", `ls ${MOUNT}/${DB}/collections/`], ["ls_views", `ls ${MOUNT}/${DB}/views/`], ["ls_entity", `ls ${MOUNT}/${DB}/collections/books/`], ["tree", `tree -L 3 ${MOUNT}/${DB}/`], ["cat_database_json", `cat ${MOUNT}/${DB}/database.json`], ["cat_schema_json", `cat ${MOUNT}/${DB}/collections/books/schema.json`], ["cat_docs", `cat ${MOUNT}/${DB}/collections/books/documents.jsonl`], ["head_2", `head -n 2 ${MOUNT}/${DB}/collections/books/documents.jsonl`], ["tail_2", `tail -n 2 ${MOUNT}/${DB}/collections/books/documents.jsonl`], ["wc_l_books", `wc -l ${MOUNT}/${DB}/collections/books/documents.jsonl`], ["wc_default_books", `wc ${MOUNT}/${DB}/collections/books/documents.jsonl`], ["wc_l_authors", `wc -l ${MOUNT}/${DB}/collections/authors/documents.jsonl`], ["stat_docs", `stat ${MOUNT}/${DB}/collections/books/documents.jsonl`], ["grep_c_title", `grep -c title ${MOUNT}/${DB}/collections/books/documents.jsonl`], ["grep_ada", `grep ada ${MOUNT}/${DB}/collections/books/documents.jsonl`], ["grep_e_multi", `grep -n -e ada -e ben ${MOUNT}/${DB}/collections/books/documents.jsonl`], ["grep_r_e_multi", `grep -r -e alpha -e beta ${MOUNT}/${DB}/collections/books`], ["grep_db_scope", `grep ada ${MOUNT}/${DB}/`], ["grep_root_scope", `grep ada ${MOUNT}/`], ["rg_db_scope", `rg ben ${MOUNT}/${DB}/`], ["rg_e_multi", `rg -e gamma -e cara ${MOUNT}/${DB}/collections/books`], ["find_docs", `find ${MOUNT}/${DB}/ -name documents.jsonl`], ["find_schema", `find ${MOUNT}/${DB}/ -name schema.json`], ["jq_titles", `jq '.title' ${MOUNT}/${DB}/collections/books/documents.jsonl`], ["pipe_grep_c", `cat ${MOUNT}/${DB}/collections/books/documents.jsonl | grep -c fiction`], ["cat_view_docs", `cat ${MOUNT}/${DB}/views/recent_books/documents.jsonl`], ["wc_l_view", `wc -l ${MOUNT}/${DB}/views/recent_books/documents.jsonl`], ["safeguard_cat_truncates", `cat ${MOUNT}/${DB}/collections/books/documents.jsonl`], ["safeguard_cat_pipe_uncapped", `cat ${MOUNT}/${DB}/collections/books/documents.jsonl | wc -l`], // symlink to the database meta file (namespace state; read-only backend) ["sym_ln", `ln -s ${MOUNT}/${DB}/database.json ${MOUNT}/meta_link`], ["sym_readlink", `readlink ${MOUNT}/meta_link`], ["sym_cat", `cat ${MOUNT}/meta_link`], ["sym_ls", `ls -F ${MOUNT} | grep meta_link`], ["sym_rm", `rm ${MOUNT}/meta_link && ls ${MOUNT}`], ]; async function seed(client: MongoClient): Promise { const db = client.db(DB); await db.dropDatabase(); // Python seeds floats (BSON double); insert Double so the inferred schema // matches (JS whole numbers would otherwise serialize to BSON int32). await db .collection("books") .insertMany(BOOKS.map((d) => ({ ...d, rating: new Double(d.rating) }))); await db.collection("authors").insertMany(AUTHORS.map((d) => ({ ...d }))); await db.createCollection("recent_books", { viewOn: "books", pipeline: [{ $match: { year: { $gte: 2022 } } }], }); } async function run(ws: Workspace, name: string, cmd: string): Promise { const result = await ws.execute(cmd); const out = DEC.decode(result.stdout); process.stdout.write(`=== ${name} ===\n`); process.stdout.write(out.endsWith("\n") ? out : out + "\n"); if (name.startsWith("safeguard_")) { const err = DEC.decode(result.stderr); if (err) process.stdout.write(err.endsWith("\n") ? err : err + "\n"); } } async function insertUntilDone( client: MongoClient, done: () => boolean, ): Promise { let i = 0; while (!done()) { i += 1; await client .db(DB) .collection("books") .insertOne({ _id: 100 + i, title: "live_insert" }); await new Promise((r) => setTimeout(r, 500)); } } async function runFollow(ws: Workspace, client: MongoClient): Promise { const name = "tail_f_change_stream"; const cmd = `tail -f ${MOUNT}/${DB}/collections/books/documents.jsonl | head -n 1`; let finished = false; const followP = ws.execute(cmd).then((result) => { finished = true; return DEC.decode(result.stdout); }); const inserter = insertUntilDone(client, () => finished); const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error("tail -f change stream timed out")), 30000), ); try { const out = await Promise.race([followP, timeout]); process.stdout.write(`=== ${name} ===\n`); process.stdout.write(out.endsWith("\n") ? out : out + "\n"); } finally { finished = true; await inserter; } } function setCatSafeguard(ws: Workspace, maxLines: number): void { const sg = new CommandSafeguard({ maxLines }); for (const m of ws.registry.allMounts()) m.commandSafeguards.set("cat", sg); } async function main(): Promise { const seedClient = new MongoClient(MONGODB_URI); await seedClient.connect(); try { await seed(seedClient); } finally { await seedClient.close(); } const resource = new MongoDBResource({ uri: MONGODB_URI, databases: [DB] }); const ws = new Workspace({ [MOUNT]: resource }, { mode: MountMode.READ }); try { for (const [name, cmd] of CASES) { if (name === "safeguard_cat_truncates") setCatSafeguard(ws, 2); await run(ws, name, cmd); } await runNotFound(ws, MOUNT); await runProvisionProbe( ws, `${MOUNT}/${DB}/collections/books/documents.jsonl`, ); await runSedReadonlyProbe( ws, `${MOUNT}/${DB}/collections/books/documents.jsonl`, ); const liveClient = new MongoClient(MONGODB_URI); await liveClient.connect(); try { await runFollow(ws, liveClient); } finally { await liveClient.close(); } } finally { await ws.close(); await resource.close(); } } main().catch((err: unknown) => { console.error(err); process.exit(1); });