UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

175 lines • 7.45 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 { specOf } from "../../spec/builtins.js"; import { FlagView } from "../../spec/flag_view.js"; import { flagKwargName } from "../../spec/constants.js"; import { fsStrerror, isFsError } from "../../../errors/fs.js"; import { stripSlash } from "../../../utils/slash.js"; import { IOResult } from "../../../io/types.js"; import { LineFacts, PathSpec } from "../../../types.js"; import { gnuBasename } from "../../../utils/path.js"; import { gzipCompressStream } from "../../../utils/compress.js"; import { linkResolver } from "../utils/links.js"; import { formatRecords } from "../utils/output.js"; import { resolveSource, stdinStream } from "../utils/stream.js"; import { GZIP_SUFFIX } from "../constants.js"; import { besideLink, decompressInputs, existsLine, gzipSuffix, openGzipInput, outputTaken, removeInput, replaceOutput, suffixRefusal, } from "./decompress.js"; import { ByteSpool } from "../../../io/spool.js"; import { OutputStream, closeQuietly, drain } from "../../../io/stream.js"; // -1..-9, the last one typed, as GNU's option loop keeps it; -1 parses to // args_1 (AMBIGUOUS_NAMES). Read off the flag tape: an object lists its // integer-like keys first and in numeric order, so typedOrder cannot rank // the digits. function extractLevel(fl) { const names = new Map(); for (let n = 1; n <= 9; n++) names.set(flagKwargName(String(n)), n); const typed = fl.occurrences(...names.keys()).filter(([, value]) => value === true); const last = typed.at(-1); return last === undefined ? null : (names.get(last[0]) ?? null); } function parseFlags(bag) { const fl = new FlagView(bag, specOf('gzip')); return { decompress: fl.asBool('d'), keep: fl.asBool('k'), force: fl.asBool('f'), toStdout: fl.asBool('c'), quiet: fl.asBool('q'), suffix: fl.asStr('S') ?? GZIP_SUFFIX, level: extractLevel(fl), }; } export async function gzipGeneric(paths, ctx, stream, write, unlink, stat) { const { decompress, keep, force, toStdout: stdoutMode, quiet, suffix, level, } = parseFlags(ctx.flags); const resolver = linkResolver(ctx); const refused = suffixRefusal(suffix); if (refused !== null) return [null, refused]; if (decompress) return decompressInputs(paths, stream, { stdin: ctx.stdin, keep, force, quiet, suffix, toStdout: stdoutMode, write, unlink, ...(stat !== undefined ? { stat } : {}), resolver, }); if (paths.length === 0) { const result = gzipCompressStream(resolveSource(ctx.stdin), level); return [result, new IOResult()]; } const read = stdinStream(stream, ctx.stdin); const io = new IOResult(); const lines = []; const report = (line, code, warning) => { if (!(warning && quiet)) { lines.push(line.replace(/\n$/, '')); io.stderr = formatRecords(lines); } if (io.exitCode !== 1) io.exitCode = code; }; async function* run() { for (const p of paths) { const inPlace = !(stdoutMode || p.rawPath === '-'); const found = p.rawPath === '-' ? null : await openGzipInput(p, inPlace ? stream : read, report, { suffix, decompress: false, follow: stdoutMode || force, resolver, }); if (found === null && p.rawPath !== '-') continue; const input = found === null ? read(p) : found.stream; const link = found?.link ?? null; try { const known = inPlace ? gzipSuffix(p.rawPath, suffix) : null; if (known !== null && !force) { report(`gzip: ${p.rawPath} already has ${known} suffix -- unchanged`, io.exitCode, true); continue; } const compressed = gzipCompressStream(input, level, p.rawPath === '-' ? '' : gnuBasename(p.rawPath)); if (!inPlace) { try { yield* compressed; } catch (error) { if (!isFsError(error)) throw error; report(`\ngzip: ${p.rawPath}: ${String(fsStrerror(error))}`, 1, false); return; } continue; } const spool = new ByteSpool(); try { try { await spool.fill(compressed); } catch (error) { if (!isFsError(error)) throw error; report(`\ngzip: ${p.rawPath}: ${String(fsStrerror(error))}`, 1, false); return; } const out = link === null ? PathSpec.fromStrPath(`/${stripSlash(p.virtual)}${suffix}`) : besideLink(link, p.rawPath + suffix); const existed = await outputTaken(out, stat, resolver); if (existed && !force) { report(existsLine(p.rawPath + suffix), 2, false); continue; } try { await replaceOutput(out, spool.stream(), write, resolver, link !== null); } catch (error) { if (!isFsError(error)) throw error; report(`${existed ? '' : '\n'}gzip: ${p.rawPath}${suffix}: ${String(fsStrerror(error))}`, 1, false); if (existed) continue; return; } } finally { await spool.close(); } if (!keep) await removeInput(p, link, unlink, resolver); } finally { await closeQuietly(input); } } } const body = new OutputStream(run(), () => closeQuietly(ctx.stdin)); if (!stdoutMode && paths.every((p) => p.rawPath !== '-')) { await drain(body); return [null, io]; } return [body, io]; } /** A gzip line replaces its file operands unless `-c`. Mirrors Python's `facts`. */ export function facts(fl, operands) { return new LineFacts({ writes: operands.length > 0 && !fl.asBool('c') }); } //# sourceMappingURL=gzip.js.map