// Package fromruntime is compose/frombus's StatesReader over a bundle's runtime (the lead, 2026-10-29): the states a // module's manifest names in `reads` (frombus.StatesReads), read through stdio.State, and a templates archive fetched // over HTTP. Apart from frombus so that frombus stays pure for the mesh-compose command. package fromruntime import ( "context" "fmt" "io" "net/http" stdio "git.novox.be/novox/mesh-sdk/go" "git.novox.be/novox/mesh-sdk/go/compose/frombus" ) // States are a bundle's read states through its runtime. type States struct{} // Get is a key's value, false when it has none. func (States) Get(state, key string) ([]byte, bool, error) { e, err := stdio.State(state).Get(key) if err != nil || e == nil { return nil, false, err } return e.Value, true, nil } // Keys are a state's keys. func (States) Keys(state string) ([]string, error) { return stdio.State(state).Keys() } // Reader is frombus's Reader over this bundle's read states key by key, fetching archives over HTTP. A holder that // composes again and again reads through Watched instead (novox/hq ADR 0580). func Reader() frombus.Reader { return frombus.StatesReader(States{}, HTTPFetch) } // Watched is frombus's States over a watched copy (novox/hq ADR 0580 clause 1): the states a composition reads whole // (placement, haves, modules, resolved values) from the copy, a build by key when asked, a manifest once per digest. type Watched struct{ *stdio.WatchedStates } // Get is a key's value: a build read now, a manifest kept once read, any other state's from the copy. func (w Watched) Get(state, key string) ([]byte, bool, error) { switch state { case frombus.BuildsRead: return w.Direct(state, key) case frombus.ManifestsRead: return w.Once(state, key) } return w.WatchedStates.Get(state, key) } // WatchedReader is frombus's Reader over w, fetching archives over HTTP. func WatchedReader(w *stdio.WatchedStates) frombus.Reader { return frombus.StatesReader(Watched{w}, HTTPFetch) } // HTTPFetch reads an archive over HTTP, at most 256 MiB. func HTTPFetch(ctx context.Context, url string) ([]byte, error) { req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return nil, err } resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("%s answered %s", url, resp.Status) } return io.ReadAll(io.LimitReader(resp.Body, 256<<20)) }