UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

171 lines • 6.66 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. ========= var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) { if (typeof path === "string" && /^\.\.?\//.test(path)) { return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) { return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js"); }); } return path; }; // How much of a file one read or write moves while a tar streams. const CHUNK = 1024 * 1024; async function tryLoadFs() { const g = globalThis; if (g.process?.versions?.node === undefined) return null; try { const modName = 'node:fs/promises'; const mod = (await import(__rewriteRelativeImportExtension(/* @vite-ignore */ modName))); return mod; } catch { return null; } } const nodeFs = await tryLoadFs(); export async function readFileBytes(path) { if (nodeFs === null) throw new Error('readFileBytes: not available (node:fs unavailable)'); return nodeFs.readFile(path); } export async function writeFileBytes(path, data) { if (nodeFs === null) throw new Error('writeFileBytes: not available (node:fs unavailable)'); await nodeFs.writeFile(path, data); } function requireFs(caller) { if (nodeFs === null) throw new Error(`${caller}: not available (node:fs unavailable)`); return nodeFs; } export async function fileSize(path) { return (await requireFs('fileSize').stat(path)).size; } /** * Open a host file a state named, without following a link. A captured * file read later is refused when a link or anything but a regular file * has replaced it since its state named it: O_NOFOLLOW makes the open * fail on a link (ELOOP), and the handle's own stat refuses a directory, * FIFO or device, so a swapped file cannot pull a host file from outside * the mount into a workspace or a snapshot. Mirrors node's openRegular. */ async function openRegular(path) { const fs = requireFs('openRegular'); const handle = await fs.open(path, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0)); try { if (!(await handle.stat()).isFile()) throw new Error(`not a regular file: ${path}`); } catch (error) { await handle.close(); throw error; } return handle; } /** The bytes of a host file a state named, opened as `openRegular` opens it. */ export async function readRegular(path) { const handle = await openRegular(path); try { return await handle.readFile(); } finally { await handle.close(); } } /** The file at `path`, opened as `openRegular` opens it, a chunk at a time. */ export async function* regularChunks(path) { yield* chunksOf(await openRegular(path)); } /** Write `source` to a file at `path`, chunk by chunk; returns the bytes written. */ export async function writeStreamToFile(path, source) { const fs = requireFs('writeStreamToFile'); const slash = path.lastIndexOf('/'); if (slash > 0) await fs.mkdir(path.slice(0, slash), { recursive: true }); const handle = await fs.open(path, 'w'); const reader = source.getReader(); let written = 0; try { for (;;) { const { done, value } = await reader.read(); if (done) break; let offset = 0; while (offset < value.byteLength) { const { bytesWritten } = await handle.write(value, offset, value.byteLength - offset, null); if (bytesWritten === 0) throw new Error(`${path}: write made no progress`); offset += bytesWritten; } written += value.byteLength; } } finally { reader.releaseLock(); await handle.close(); } return written; } /** The file at `path`, a chunk at a time. */ export async function* fileChunks(path) { yield* chunksOf(await requireFs('fileChunks').open(path, 'r')); } /** An open file's bytes a chunk at a time, closing it once read or let go. */ async function* chunksOf(handle) { try { for (;;) { const chunk = new Uint8Array(CHUNK); const { bytesRead } = await handle.read(chunk, 0, CHUNK, null); if (bytesRead === 0) return; yield chunk.subarray(0, bytesRead); } } finally { await handle.close(); } } /** A fresh directory under the system temp dir, for staging a restore. */ /** * Where blob `name` is staged under `staging`, its directory made; a name * landing outside it, as written or through a link, is refused. */ export async function stagedPath(staging, name) { const fs = requireFs('stagedPath'); const modName = 'node:path'; const path = (await import(__rewriteRelativeImportExtension(/* @vite-ignore */ modName))); const root = await fs.realpath(staging); const inside = (target) => { const rel = path.relative(root, target); return rel !== '' && rel !== '..' && !rel.startsWith(`..${path.sep}`) && !path.isAbsolute(rel); }; const target = path.resolve(root, name); if (!inside(target)) throw new Error(`Unsafe blob path: ${name}`); await fs.mkdir(path.dirname(target), { recursive: true }); const real = path.join(await fs.realpath(path.dirname(target)), path.basename(target)); if (!inside(real)) throw new Error(`Unsafe blob path: ${name}`); return real; } export async function makeStagingDir() { const modName = 'node:os'; const os = (await import(__rewriteRelativeImportExtension(/* @vite-ignore */ modName))); return requireFs('makeStagingDir').mkdtemp(`${os.tmpdir()}/mirage-restore-`); } export async function removeDir(path) { await requireFs('removeDir').rm(path, { recursive: true, force: true }); } //# sourceMappingURL=fs.js.map