PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/r/moul/x/allinone/devtools/v0

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v0
Namespace
moul / x / allinone / devtools
Files
4 (README)(gnomod.toml)
Exported functions
3
Module
gno.land/r/moul/x/allinone/devtools/v0
gno
0.9

Files (4)

  • README.mdmarkdown
  • gnomod.tomltoml
  • devtools.gnogno
  • devtools_test.gnogno
devtools.gnogno
1// Package devtools is the worked example of wiring moul's developer packages2// into one realm: what a new realm should copy.3//4// Everything here is a COMBINATION. Any one of these packages has its own demo5// showing it alone; the thing that is hard to find is what they look like used6// together, which is the only form a real realm ever meets them in. So this7// realm imports eight of them and every one earns its place in the output:8//9//	r/moul/config     the notice block, the pause switch, the explorer to link10//	p/moul/mygnoscan  the explorer routes themselves11//	p/moul/debug      a metadata panel behind ?debug=112//	p/moul/realmpath  reading the query string Render was handed13//	p/moul/kit/ui     escaping what a caller typed, and the tables14//	p/moul/md         the markdown, without hand-rolled strings15//	p/moul/txlink     the "call this function" link16//	p/moul/fifo       the bounded note list, so the state cannot grow forever17//18// # The three lines worth copying19//20// A realm that does nothing else should still do these:21//22//	func Render(path string) string {23//		return config.TopBlockFor(realmPath) + body + config.BottomBlockFor(realmPath)24//	}25//26//	func Mutate(cur realm, s string) {27//		config.AssertWritableFor(realmPath)  // honours the pause switch28//		notes.Append(ui.Inline(s))           // escapes what a caller typed29//	}30//31// The first gives every realm a place to put a warning and a pause banner32// without shipping banner code. The second is two separate guards that both33// have to be there: the pause one so an incident can stop writes from outside34// the realm, and the escaping one because a string a caller typed is the35// oldest way to break a page.36package devtools3738import (39	"strconv"40	"strings"4142	"chain/runtime"4344	"gno.land/p/moul/debug/v0"45	"gno.land/p/moul/fifo/v0"46	"gno.land/p/moul/kit/ui/v0"47	"gno.land/p/moul/md/v0"48	"gno.land/p/moul/mygnoscan/v0"49	"gno.land/p/moul/realmpath/v0"50	"gno.land/p/moul/txlink/v0"51	"gno.land/r/moul/config/v1"52)5354// realmPath is this realm's own path.55//56// Passed explicitly to config rather than relying on its zero-argument helpers.57// Both work (a plain read is borrowed, so config sees this realm as the58// caller), but a constant cannot be wrong, and spelling it out is what a reader59// copying this file needs to see.60const realmPath = "gno.land/r/moul/x/allinone/devtools/v0"6162// maxNotes bounds the guest list. fifo drops the oldest rather than growing,63// because storage on chain is paid for and never refunded: an unbounded list in64// a demo anyone can write to is a bill with no ceiling.65const maxNotes = 106667var notes = fifo.New(maxNotes)6869// Add appends a note. It is the only mutating function here, and it carries70// both guards a real one should.71func Add(cur realm, body string) {72	// 1. The pause switch. Set `pause@r/moul/x/allinone/devtools` on73	//    r/moul/config and this aborts, with the reason, from outside this74	//    realm and without a redeploy.75	config.AssertWritableFor(realmPath)7677	body = strings.TrimSpace(body)78	if body == "" {79		panic("empty note")80	}81	if len(body) > 200 {82		panic("note too long: max 200 bytes, got " + strconv.Itoa(len(body)))83	}8485	// 2. Escaping. body is a string a caller typed, and it is about to land in86	//    a markdown list. ui.Inline is the repo's standard for exactly this.87	notes.Append(ui.Inline(body))88}8990// Reset empties the list, so the realm can be put back to a known state91// without a redeploy. Not gated by the pause switch: clearing up is the thing92// you still want to do while paused.93func Reset(cur realm) {94	notes = fifo.New(maxNotes)95}9697// Render shows every package at work. The root view is deliberately free of98// anything that moves on its own, so an example test can pin it; the live99// numbers live under "chain".100func Render(path string) string {101	req := realmpath.Parse(path)102103	var b strings.Builder104	b.WriteString(config.TopBlockFor(realmPath))105	b.WriteString(md.H1("allinone: devtools"))106	b.WriteString("\nEight packages, used together rather than one at a time. ")107	b.WriteString(md.Link("source", config.MygnoscanFor(realmPath)))108	b.WriteString("\n\n")109110	switch req.PathPart(0) {111	case "chain":112		b.WriteString(renderChain())113	default:114		b.WriteString(renderHome(path))115	}116117	// debug prints its own panel only when ?debug=1 is set, and the empty118	// string otherwise, so this line is safe to leave in a deployed realm.119	b.WriteString(debug.Render(path))120	b.WriteString(config.BottomBlockFor(realmPath))121	return b.String()122}123124func renderHome(path string) string {125	var b strings.Builder126127	b.WriteString(md.H2("Notes"))128	b.WriteString("\n")129	// renderNotes returns no trailing newline of its own: md.BulletList and130	// ui.Empty already end in one, and adding a second here produced two131	// consecutive blank lines, which gno collapses inside an // Output: block132	// and which therefore no example test can ever pin.133	b.WriteString(renderNotes())134	b.WriteString("\n")135136	// txlink builds the "call this function" URL for the CURRENT realm, so it137	// keeps working after a version bump without anything here changing.138	b.WriteString(md.BulletList([]string{139		md.Link("add a note", txlink.NewLink("Add").AddArgs("body", "hello").URL()),140		md.Link("clear them", txlink.Call("Reset")),141		md.Link("live chain values", "/r/moul/x/allinone/devtools/v0:chain"),142		md.Link("toggle the debug panel", debug.ToggleURL(path)),143	}))144	b.WriteString("\n")145146	b.WriteString(md.H2("What each package does here"))147	b.WriteString("\n")148	t := ui.NewTable("package", "what it contributed")149	t.Row("`r/moul/config`", "the notice block above, the pause guard on `Add`, and which explorer to link")150	t.Row("`p/moul/mygnoscan`", "the explorer routes, so a link survives a route change")151	t.Row("`p/moul/debug`", "the `?debug=1` panel, empty otherwise")152	t.Row("`p/moul/realmpath`", "splitting the render path into a view and a query")153	t.Row("`p/moul/kit/ui`", "escaping a caller's note, and this table")154	t.Row("`p/moul/md`", "every heading, link and list on the page")155	t.Row("`p/moul/txlink`", "the call links above, relative to this realm")156	t.Row("`p/moul/fifo`", "the note list, bounded so storage cannot grow forever")157	b.WriteString(t.String())158159	return b.String()160}161162// renderChain is everything that moves: the height, and the links built from163// it. Kept off the root view so the root can be pinned by an example.164func renderChain() string {165	s := config.Scanner()166	h := runtime.ChainHeight()167168	var b strings.Builder169	b.WriteString(md.H2("Live"))170	b.WriteString("\n")171172	t := ui.NewTable("what", "value")173	t.Row("chain id", ui.Cell(runtime.ChainID()))174	t.Row("height", strconv.FormatInt(h, 10))175	t.Row("explorer", ui.Cell(s.Base()))176	t.Row("network", ui.Cell(s.Network()))177	b.WriteString(t.String())178	b.WriteString("\n")179180	// mygnoscan.Link used directly, rather than through config, to show that181	// the builder is an ordinary value: config only decides where it points.182	b.WriteString(md.BulletList([]string{183		mygnoscan.Link("this block", s.Block(h)),184		mygnoscan.Link("this realm's transactions", s.Realm(realmPath, mygnoscan.TabCalls)),185		mygnoscan.Link("its dependency graph", s.Realm(realmPath, mygnoscan.TabDeps)),186		mygnoscan.Link("the parked deploy queue", s.Packages(mygnoscan.PackagesInert)),187	}))188	b.WriteString("\n")189	b.WriteString(md.Link("back", "/r/moul/x/allinone/devtools/v0"))190	b.WriteString("\n")191	return b.String()192}193194func renderNotes() string {195	if notes.Size() == 0 {196		return ui.Empty("no notes yet")197	}198	items := []string{}199	for _, e := range notes.Entries() {200		items = append(items, e.(string))201	}202	return md.BulletList(items)203}204

Functions

  • Add(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, body string)

  • Render(path string) string

  • Reset(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string})

Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

Rendered

RenderedRawgnoweb ↗

vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.