package memba_appstore_v2 import ( "strings" "gno.land/p/nt/ufmt/v0" ) const ( DefaultPageSize = 20 // default JSON window MaxPageLimit = 100 // hard cap on any JSON read window (read-DoS guard) ) func clampLimit(limit int) int { if limit <= 0 { return DefaultPageSize } if limit > MaxPageLimit { return MaxPageLimit } return limit } // jsonEscape escapes a string for embedding in a JSON string literal — every // listing text field is attacker-supplied, so this is the only thing between a // malicious name/tagline and a broken/injected JSON payload on the read path. func jsonEscape(s string) string { var sb strings.Builder for _, r := range s { switch r { case '"': sb.WriteString("\\\"") case '\\': sb.WriteString("\\\\") case '\n': sb.WriteString("\\n") case '\r': sb.WriteString("\\r") case '\t': sb.WriteString("\\t") default: if r < 0x20 { sb.WriteString(ufmt.Sprintf("\\u%04x", r)) } else { sb.WriteString(string(r)) } } } return sb.String() } // listingJSON renders one listing. `full` adds the (large) description — omitted // from list windows so a bounded page stays small. func listingJSON(l *Listing, full bool) string { descr := "" if full { descr = ufmt.Sprintf(`,"descr":"%s"`, jsonEscape(l.Descr)) } return ufmt.Sprintf( `{"id":%d,"pkgPath":"%s","name":"%s","tagline":"%s","category":"%s","iconCID":"%s","appURL":"%s","publisher":"%s","status":"%s","flagCount":%d,"createdAt":%d%s}`, l.Id, jsonEscape(l.PkgPath), jsonEscape(l.Name), jsonEscape(l.Tagline), jsonEscape(l.Category), jsonEscape(l.IconCID), jsonEscape(l.AppURL), l.Publisher.String(), l.Status, l.FlagCount, l.CreatedAt, descr, ) } // ListLiveJSON returns a bounded JSON array of the visible (live, un-hidden) // listings: skip `offset`, take up to clamped `limit`. Iteration stops as soon as // the window is filled, so the scan is bounded by offset+limit regardless of // catalog size. func ListLiveJSON(offset, limit int) string { if offset < 0 { offset = 0 } limit = clampLimit(limit) var sb strings.Builder sb.WriteString("[") skipped := 0 taken := 0 first := true listings.Iterate("", "", func(_ string, v any) bool { l := v.(*Listing) if !isVisible(l) { return false } if skipped < offset { skipped++ return false } if taken >= limit { return true // window full — stop } if !first { sb.WriteString(",") } first = false sb.WriteString(listingJSON(l, false)) taken++ return false }) sb.WriteString("]") return sb.String() } // GetListingJSON returns a single listing (any status) with its description, or // the JSON literal `null` if the package path is not registered. func GetListingJSON(pkgPath string) string { v, ok := listings.Get(pkgPath) if !ok { return "null" } return listingJSON(v.(*Listing), true) }