Skip to content

baguette v0.1.93

Choose a tag to compare

@github-actions github-actions released this 20 Aug 03:04
· 21 commits to main since this release

Added

  • Network conditioning — baguette network set|clear|status. Makes a
    simulator's apps see a worse network than your Mac has: added latency, a
    capped downlink, a proportion of requests failing, or hard offline. Named
    presets borrow Network Link Conditioner's vocabulary and its figures
    (wifi, dsl, lte, 3g, edge, very-bad-network, 100-loss), so
    3g means what everyone already means by 3G. NLC itself, and the
    dnctl / pfctl rules under it, are system-wide — simulator apps use
    the host's stack as the host user, so there is no interface to scope a
    rule to and conditioning one simulator that way degrades the whole Mac.
    Injecting into the app under test is the only way to scope it, so this
    works the way the virtual camera and
    motion do. Only apps launched after
    network set are conditioned
    ; changing the condition afterwards reaches
    a running app without a relaunch.
    Which interception mechanism matters was measured before any of it was
    designed: +[NSURLProtocol registerClass:] reaches only NSURLConnection
    and NSURLSession.shared, and against a real React Native app it caught
    zero of the app's own requests over 100 seconds. Swizzling
    +defaultSessionConfiguration is what reaches fetch, image loading and
    REST clients; both ship, and the load banner says which took. Two more
    measured facts shape the code: request bodies arrive only as
    HTTPBodyStream, which reads once, so a conditioned request is never
    retried; and the response is paced as bytes arrive rather than buffered
    and replayed, so a 23 MB bundle doesn't sit in memory.
    The hazard this is designed against is forgetting it is on — unlike a
    wrong camera picture, a throttle reads as "the app is slow" days later. So
    plain baguette network reports the current condition, network clear
    un-conditions apps that are already running, and the browser keeps an
    amber dot lit whether or not its card was ever opened. Honest about its
    reach: URLSession-shaped traffic only. URLSessionWebSocketTask gets its
    own hooks and takes latency, loss and offline (not bandwidth — an app
    cannot observe a partial message). Not conditioned: WKWebView page
    loads, NWConnection/Network.framework, raw sockets, and realtime SDKs
    that open their own socket — Ably's ably-cocoa vendors SocketRocket, so
    --offline will not feel offline to it. See
    docs/features/network.md.

  • Install plugins from the browser — from bakeries you already trust.
    The rail's + now opens a shelf of every trusted bakery, its pinned
    commit, and what it offers: an Install button on anything you don't
    have, Installed on anything you do, and the rail picks the new plugin
    up without a reload. Previously the browser could only preview and hand
    you a command to paste.
    The trust boundary moved, but only halfway, and the half that matters
    stayed put. POST /bakeries/install names a bakery by its recorded
    id
    , never a URL or a git ref — so a request can only reach a source
    already in bakeries.json, at the commit pinned there, and a plugin
    that source's own menu lists. Installing writes files baguette later
    executes from and the only thing in front of a browser route is a set
    of origin heuristics; naming sources by recorded id is what keeps the
    blast radius of a wrong one at "installs from a repo you already
    vetted" instead of "clones anything onto your disk". A refusal never
    echoes the id it was handed back into the page.
    Trusting a new source is still not something a page can do
    baguette bakery add stays a terminal act, because a modal button
    isn't consent (the page sets the flag it then checks) and trust is the
    decision that actually matters. Preview still ends by handing you the
    command. The decision is InstallDecision in Domain/Bakery/ with
    every refusal path unit-tested, and installing still only copies
    files — nothing runs until you open the plugin's panel.
    GET /bakeries.json now reports each offer's install state, decided
    host-side from what the plugin scan can see rather than from
    installed.json (a bundled plugin has no provenance record and must
    still read as satisfied). See
    docs/features/plugins.md.

Fixed

  • Adding a bakery no longer collides with itself and kills the clone.
    GitCheckout.clone emptied the cache directory and then cloned into it,
    leaving the live tree half-written for the tens of seconds a clone takes.
    A second clone of the same bakery arriving in that window — the browser's
    preview against a terminal bakery add, or one impatient second press of
    Preview — deleted the tree the first was still writing into, and git died
    on its own vanished temp pack:
    fatal: could not open '…/pack/tmp_pack_XXXXXX' for reading: No such file or directory / fatal: fetch-pack: invalid index-pack output.
    Each clone now assembles the checkout in a staging directory of its own
    beside the destination and moves it in only once it is complete, so
    nothing ever deletes a checkout in progress and whoever finishes last
    wins the swap. Two consequences worth having on their own: a failed clone
    now leaves the checkout you already had instead of a network blip taking
    the working copy with it, and a reader never sees the cache directory
    mid-delete. The modal also runs one preview at a time — the button
    disables while a clone is in flight, and says that a cold one takes a
    minute rather than showing a bare "Fetching…".

  • The add-a-bakery modal drew behind the device. It mounted into
    .right-rails alongside the plugin rail, and .right-rails is
    position: fixed — which creates a stacking context whatever its
    z-index says. So the modal's z-index: 60 was weighed against its
    siblings inside the rail rather than against the page, and the device's
    z-index: 2 screen area a level up painted over it: the phone cut the
    dialog in half and the scrim dimmed everything except the thing it was
    covering. Page-covering UI now hangs off the view root instead. The
    comment on .right-rails claimed the opposite ("with z-index: auto
    this container doesn't create a stacking context") and has been corrected
    position: fixed alone is enough.

What's Changed

  • feat(network): per-simulator network conditioning by @crockalet in #66

Full Changelog: v0.1.92...v0.1.93