UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

110 lines • 4.55 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 { isStdin, neverOpened } from "../utils/stream.js"; import { specOf } from "../../spec/builtins.js"; import { FlagView } from "../../spec/flag_view.js"; import { IOResult } from "../../../io/types.js"; import { PathSpec } from "../../../types.js"; import { isFsError } from "../../../errors/fs.js"; import { fsErrorLine } from "../../../errors/render.js"; import { AsyncLineIterator } from "../../../io/async_line_iterator.js"; import { closeStream, mergeOutput, openStream, runStream } from "../../../rs/stream.js"; import { byteView } from "../../../shell/bytes.js"; const ENC = new TextEncoder(); export function pasteGeneric(paths, ctx, stream) { const fl = new FlagView(ctx.flags, specOf('paste')); const config = { delimiters: byteView(fl.asStr('delimiters', '\t')), serial: fl.asBool('serial'), zero_terminated: fl.asBool('zero_terminated'), }; const shared = new AsyncLineIterator(ctx.stdin ?? new Uint8Array()); const operands = paths.length === 0 ? [PathSpec.fromStrPath('-')] : paths; const inputs = operands.map((p) => [p, isStdin(p) ? shared : new AsyncLineIterator(stream(p))]); const io = new IOResult(); return [pasteStream(inputs, config, io), io]; } /** * Paste the operands. In rows every operand opens before any is read, as GNU * opens them, and one that never opened ends the run before any line is * written; under -s each opens when its turn comes, and one that never opened * is left out. A failed read is reported, exit 1, and its operand reads as * ended. Mirrors Python's _paste_stream. */ async function* pasteStream(inputs, config, io) { const errors = []; const report = (path, error) => { if (!isFsError(error)) throw error; errors.push(fsErrorLine('paste', path, error)); io.stderr = ENC.encode(errors.join('')); io.exitCode = 1; }; const opened = async (path, reader) => { try { if (!isStdin(path)) await reader.peek(); return true; } catch (error) { report(path, error); return !neverOpened(reader, path, error); } }; const pull = async (path, reader) => { try { return await reader.readChunk(); } catch (error) { report(path, error); return null; } }; async function* records(path, reader) { for (let chunk = await pull(path, reader); chunk !== null; chunk = await pull(path, reader)) yield chunk; } try { if (config.serial === true) { for (const [path, reader] of inputs) { if (await opened(path, reader)) yield* runStream('paste', { ...config, inputs: [0] }, records(path, reader)); } return; } for (const [path, reader] of inputs) if (!(await opened(path, reader))) return; const sources = [...new Set(inputs.map(([, reader]) => reader))]; const names = new Map([...inputs].reverse().map(([path, reader]) => [reader, path])); config.inputs = inputs.map(([, reader]) => sources.indexOf(reader)); const read = async (index) => { const reader = sources[index]; const path = reader === undefined ? undefined : names.get(reader); return reader === undefined || path === undefined ? null : pull(path, reader); }; const stream = await openStream('paste', config); try { yield* mergeOutput(stream, read); } finally { await closeStream(stream); } } finally { for (const reader of new Set(inputs.map(([, reader]) => reader))) await reader.discard(); } } //# sourceMappingURL=paste.js.map