UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

214 lines • 8.43 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. ========= /** * One bash variable attribute, spelled as its `declare` letter. * * Declaration order is the order `declare -p` prints a cluster in, * pinned exhaustively against bash 5.2.37 over all 72 ordered pairs of * `a A i l n r t u x`: `a`/`A` first, then `i n r t x`, then `l`/`u`. It * is bash's own internal order, not the order the letters were typed in * (`declare -xri` prints `-irx`), and not alphabetical. * * `-a` and `-A` are deliberately absent: whether a variable is an * indexed array, an associative array or a scalar is what its value * *is*, so storing it a second time as an attribute would let the two * contradict each other. `attrLetters` derives them from the value. */ export var VarAttr; (function (VarAttr) { VarAttr["Integer"] = "i"; VarAttr["Nameref"] = "n"; VarAttr["Readonly"] = "r"; VarAttr["Trace"] = "t"; VarAttr["Export"] = "x"; VarAttr["Lower"] = "l"; VarAttr["Upper"] = "u"; })(VarAttr || (VarAttr = {})); // `declare -p` print order, derived from the enum rather than restated: // a string enum has no reverse mapping, so `Object.values` is its // declaration order exactly. Python reads the same order off `VarAttr` // itself, so an attribute added to one enum cannot print in a different // place from the other, and cannot be silently dropped by a hand-written // list nobody remembered to extend. const ATTR_ORDER = Object.values(VarAttr); /** What a variable's value is, derived from the value itself. */ export var VarKind; (function (VarKind) { VarKind["Scalar"] = "scalar"; VarKind["Indexed"] = "indexed"; VarKind["Assoc"] = "assoc"; })(VarKind || (VarKind = {})); /** * The records a prefix assignment shadowed for one function call. * * bash runs `x=1 f` with `x` in a scope of its own around the function, * its temporary environment: the caller's value comes back when the call * returns, `unset x` inside reveals it, and an `export` or `readonly` of * the name lets the value outlive the call. A frame of this type on the * call path is that scope; a plain frame holds a function's `local` * shadows. */ export class TempEnv extends Map { } const NO_ATTRS = new Set(); /** Build a variable record. */ export function makeVar(value = null, attrs = NO_ATTRS) { return { value, attrs }; } /** * What kind of variable this is, read off its value. * * An unset variable reads as a scalar: bash renders `declare -i n` with * no `-a`, so nothing but an actual array value earns the letter. */ export function varKind(v) { if (Array.isArray(v.value)) return VarKind.Indexed; if (v.value !== null && typeof v.value === 'object') return VarKind.Assoc; return VarKind.Scalar; } /** Copy a variable and its mutable array value for a child shell. */ export function copyVar(v) { if (Array.isArray(v.value)) return withValue(v, [...v.value]); if (v.value !== null && typeof v.value === 'object') return withValue(v, { ...v.value }); return v; } /** * The variable with a new value and the same attributes, but a reference's * under an array: bash's reference cannot hold one, so `declare -n r; * r=(x)` leaves `declare -a r`. */ export function withValue(v, value) { if (value !== null && typeof value === 'object' && v.attrs.has(VarAttr.Nameref)) { return { ...v, value, attrs: new Set([...v.attrs].filter((a) => a !== VarAttr.Nameref)) }; } return { ...v, value }; } /** * The variable with its managed pointer dropped, value kept. * * An agent write to a managed name shadows session-locally: the record * becomes a plain variable for this session only, so the fill step * never clobbers it and the declaration (new sessions fetch fresh) and * the remote store are untouched by construction. */ export function detach(v) { return { value: v.value, attrs: v.attrs }; } /** * The variable with one attribute turned on or off. `+attr` is the off * direction, which is why this takes a flag rather than being two * functions. */ export function withAttr(v, attr, on = true) { const attrs = new Set(v.attrs); if (on) attrs.add(attr); else attrs.delete(attr); return { ...v, attrs }; } /** * The stored attribute letters, in `declare -p` print order. * * The tail of `attrLetters` without the `a`/`A` kind lead, which is * derived rather than stored. Split out because two callers want the * stored half alone: the serializer, which must not write a letter it * would then read back as an attribute the value already implies, and * `attrLetters` itself. */ /** * Apply the value-shaping attributes to one scalar being stored. * * bash applies these at assignment, not at read: `declare -l s; s=ABC` * stores `abc`, and `declare -i n; n=2+2` stores `4`, so `declare -p` * and `$s` agree with no per-read work. Order is integer first, then * case, which only matters for hex digits and is what GNU does * (`declare -il n=0xA` stores `10`). `-l` and `-u` cannot both hold; a * declaration that sets one clears the other, so at most one applies. */ export function coerceScalar(text, attrs, integer) { let out = text; if (attrs.has(VarAttr.Integer) && integer !== null) out = integer(out); if (attrs.has(VarAttr.Lower)) return out.toLowerCase(); if (attrs.has(VarAttr.Upper)) return out.toUpperCase(); return out; } /** * `coerceScalar` lifted over every value shape. An array applies the * attribute per element, which is GNU's `declare -ai a=(1+1 2*3)` giving * `([0]="2" [1]="6")`. A write that assigns some elements only (`a[1]=x`, * `a=x`) names them in `assigned`, and the others are carried over as * stored: bash shapes a value when it is assigned, so `declare -i a; * a[0]=9` never re-evaluates a stored `a[1]`. */ export function coerceValue(value, attrs, integer, assigned = null) { if (!attrs.has(VarAttr.Integer) && !attrs.has(VarAttr.Lower) && !attrs.has(VarAttr.Upper)) { return value; } if (typeof value === 'string') return coerceScalar(value, attrs, integer); if (Array.isArray(value)) { return value.map((v, i) => v === null || (assigned !== null && !assigned.has(i)) ? v : coerceScalar(v, attrs, integer)); } const out = {}; for (const [k, v] of Object.entries(value)) { out[k] = assigned !== null && !assigned.has(k) ? v : coerceScalar(v, attrs, integer); } return out; } export function storedAttrs(v) { return ATTR_ORDER.filter((a) => v.attrs.has(a)).join(''); } /** * The attribute set a stored letter cluster spells, the inverse of * `storedAttrs`, used when a persisted session is read back. A letter * that names no attribute is ignored rather than throwing: the store is * shared with the other language and with future versions, and refusing * to load a session because one letter is unknown loses far more than * the letter. */ export function attrsFromLetters(letters) { const known = new Set(ATTR_ORDER); const out = new Set(); for (const c of letters.split('')) { if (known.has(c)) out.add(c); } return out; } /** * The attribute cluster `declare -p` prints for this variable. * * `-a`/`-A` come from the value's kind and lead, then the stored * attributes in print order. bash prints `--` for a plain scalar with * nothing set, which is the caller's to render since only it knows it is * writing a `declare` line. */ export function attrLetters(v) { const kind = varKind(v); let lead = ''; if (kind === VarKind.Indexed) lead = 'a'; else if (kind === VarKind.Assoc) lead = 'A'; return lead + storedAttrs(v); } //# sourceMappingURL=variable.js.map