mockapi

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 16, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Overview

Package mockapi serves a fixed positronick.com API fixture over net/http/httptest-compatible handlers, implementing the read contract (GET /api/souls, /api/souls/{slug}, /api/listings(?type=), /api/listings/{slug}, /api/research, /api/blog(?kind=), /api/blog/{slug}, /api/blog/{slug}.md) and the auth contract (device flow, /api/me, api-key/create) for the CLI's golden and e2e tests. The dataset is deliberately frozen: golden files pin command output byte-for-byte against it, so changing a fixture value is a contract-test change.

Index

Constants

View Source
const (
	AdminAPIKey = "posi_admin_test"
	AdminToken  = "admin-token"
	PlebToken   = "pleb-token"
)

Admin fixture credentials. AdminAPIKey/AdminToken authenticate as an admin; PlebToken authenticates as a regular user (403 on admin routes); anything else is 401.

View Source
const (
	FeedReleaseID = "01FEEDSEED0000000000000001"
	FeedBlogID    = "01FEEDSEED0000000000000002"
)

Seeded feed source ids — stable so the feed list/sync/update golden + tests can address them. FeedReleaseID is a github_release feed that syncs cleanly; FeedBlogID is a paused rss feed.

View Source
const (
	// DeviceCode is the device_code issued by /api/auth/device/code.
	DeviceCode = "mock-device-code-0001"
	// UserCode is the user_code the person types on the verification page.
	UserCode = "TJSPLLAV"
	// AccessToken is the session token granted once the device is approved;
	// it is the only bearer /api/me and api-key/create accept.
	AccessToken = "mock-session-token"
	// APIKey is the raw key minted by api-key/create, also accepted by
	// /api/me as x-api-key.
	APIKey = "posi_mockkey1234567890"
)

Auth fixture values. The device flow is scripted: the first token poll for DeviceCode answers authorization_pending, every later poll succeeds with AccessToken. The advertised poll interval is 0 so tests never really wait — interval pacing (and slow_down growth) is unit-tested in internal/auth.

Variables

View Source
var Listings = []api.Listing{
	{
		ID:               "01LSTCLAUDECODE000000000XX",
		Slug:             "claude-code",
		ProfileHandle:    "anthropic",
		ProfileName:      "Anthropic",
		ProfileTier:      ptr("official"),
		Name:             "Claude Code",
		Type:             "harness",
		Tagline:          "Agentic coding in your terminal",
		Description:      ptr("Anthropic's agentic coding harness."),
		Category:         "AI/ML",
		Tags:             []string{"agentic", "terminal"},
		Official:         true,
		SourceURL:        "https://example.com/claude-code",
		RepoURL:          ptr("https://example.com/anthropics/claude-code"),
		InstallCmd:       ptr("npm install -g @anthropic-ai/claude-code"),
		Data:             map[string]any{},
		HasAsset:         false,
		AssetVersion:     nil,
		AssetContentHash: nil,
		Confidence:       "official",
		Status:           "published",
		DownloadCount:    120,
		ChargeCount:      9,
		CreatedAt:        "2026-01-10T09:00:00.000Z",
		UpdatedAt:        "2026-01-12T09:00:00.000Z",
	},
	{
		ID:               "01LSTGITHUBCLI0000000000XX",
		Slug:             "github-cli",
		ProfileHandle:    "github",
		ProfileName:      "GitHub",
		ProfileTier:      ptr("official"),
		Name:             "GitHub CLI",
		Type:             "cli",
		Tagline:          "GitHub from the command line",
		Description:      ptr("Work with issues, PRs and releases from the shell."),
		Category:         "DevOps",
		Tags:             []string{"git", "github"},
		Official:         true,
		SourceURL:        "https://example.com/cli",
		RepoURL:          ptr("https://example.com/cli/cli"),
		InstallCmd:       ptr("brew install gh"),
		Data:             map[string]any{},
		HasAsset:         false,
		AssetVersion:     nil,
		AssetContentHash: nil,
		Confidence:       "official",
		Status:           "published",
		DownloadCount:    64,
		ChargeCount:      4,
		CreatedAt:        "2026-02-01T09:00:00.000Z",
		UpdatedAt:        "2026-02-03T09:00:00.000Z",
	},
	{
		ID:               "01LSTGRAFANAMCP000000000XX",
		Slug:             "grafana-mcp",
		ProfileHandle:    "grafana",
		ProfileName:      "Grafana Labs",
		ProfileTier:      ptr("official"),
		Name:             "Grafana MCP",
		Type:             "mcp",
		Tagline:          "Dashboards, alerts and incidents as agent tools",
		Description:      nil,
		Category:         "DevOps",
		Tags:             []string{"observability"},
		Official:         true,
		SourceURL:        "https://example.com/grafana-mcp",
		RepoURL:          nil,
		InstallCmd:       nil,
		Data:             map[string]any{},
		HasAsset:         false,
		AssetVersion:     nil,
		AssetContentHash: nil,
		Confidence:       "official",
		Status:           "published",
		DownloadCount:    31,
		ChargeCount:      2,
		CreatedAt:        "2026-03-15T09:00:00.000Z",
		UpdatedAt:        "2026-03-15T09:00:00.000Z",
	},
	{
		ID:               "01LSTSUPERPOWERS00000000XX",
		Slug:             "superpowers",
		ProfileHandle:    "obra",
		ProfileName:      "Jesse Vincent",
		ProfileTier:      ptr("verified"),
		Name:             "Superpowers",
		Type:             "skill",
		Tagline:          "A methodology pack that upgrades your coding agent",
		Description:      ptr("Skills for planning, debugging and shipping."),
		Category:         "AI/ML",
		Tags:             []string{"skills", "methodology"},
		Official:         true,
		SourceURL:        "https://example.com/superpowers",
		RepoURL:          ptr("https://example.com/obra/superpowers"),
		InstallCmd:       nil,
		Data:             map[string]any{"bundles": []any{"pr-to-green"}},
		HasAsset:         true,
		AssetVersion:     ptr("1.0.0"),
		AssetContentHash: ptr("11aa11aa11aa11aa11aa11aa11aa11aa11aa11aa11aa11aa11aa11aa11aa11aa"),
		Confidence:       "official",
		Status:           "published",
		DownloadCount:    18,
		ChargeCount:      6,
		CreatedAt:        "2026-04-01T09:00:00.000Z",
		UpdatedAt:        "2026-04-02T09:00:00.000Z",
	},
	{
		ID:               "01LSTPLAINSKILL000000000XX",
		Slug:             "plain-skill",
		ProfileHandle:    "obra",
		ProfileName:      "Jesse Vincent",
		ProfileTier:      ptr("verified"),
		Name:             "Plain Skill",
		Type:             "skill",
		Tagline:          "A skill listing with no hosted SKILL.md asset",
		Description:      ptr("Installs through its own command — no hosted asset for this listing."),
		Category:         "AI/ML",
		Tags:             []string{"skills"},
		Official:         true,
		SourceURL:        "https://example.com/plain-skill",
		RepoURL:          ptr("https://example.com/obra/plain-skill"),
		InstallCmd:       ptr("npx plain-skill install"),
		Data:             map[string]any{},
		HasAsset:         false,
		AssetVersion:     nil,
		AssetContentHash: nil,
		Confidence:       "official",
		Status:           "published",
		DownloadCount:    3,
		ChargeCount:      0,
		CreatedAt:        "2026-04-03T09:00:00.000Z",
		UpdatedAt:        "2026-04-03T09:00:00.000Z",
	},
	{
		ID:               "01LSTMISMATCHSKILL0000000",
		Slug:             "mismatched-skill",
		ProfileHandle:    "obra",
		ProfileName:      "Jesse Vincent",
		ProfileTier:      ptr("verified"),
		Name:             "Mismatched Skill",
		Type:             "skill",
		Tagline:          "A hosted skill whose SKILL.md name disagrees with its slug",
		Description:      ptr("Fixture for the frontmatter-name-must-equal-slug install rule."),
		Category:         "AI/ML",
		Tags:             []string{"skills"},
		Official:         true,
		SourceURL:        "https://example.com/mismatched-skill",
		RepoURL:          ptr("https://example.com/obra/mismatched-skill"),
		InstallCmd:       nil,
		Data:             map[string]any{},
		HasAsset:         true,
		AssetVersion:     ptr("1.0.0"),
		AssetContentHash: ptr("22bb22bb22bb22bb22bb22bb22bb22bb22bb22bb22bb22bb22bb22bb22bb22bb"),
		Confidence:       "official",
		Status:           "published",
		DownloadCount:    1,
		ChargeCount:      0,
		CreatedAt:        "2026-04-04T09:00:00.000Z",
		UpdatedAt:        "2026-04-04T09:00:00.000Z",
	},
	{
		ID:            "01LSTPRTOGREEN0000000000XX",
		Slug:          "pr-to-green",
		ProfileHandle: "nsollazzo",
		ProfileName:   "Nicholas Sollazzo",
		ProfileTier:   ptr("official"),
		Name:          "PR to Green",
		Type:          "loop",
		Tagline:       "Drive a pull request until CI is green and review approves",
		Description:   ptr("A goal loop: fix findings, push, re-check, repeat."),
		Category:      "Productivity",
		Tags:          []string{"ci", "automation"},
		Official:      true,
		SourceURL:     "https://example.com/pr-to-green",
		RepoURL:       nil,
		InstallCmd:    nil,
		Data: map[string]any{
			"goal":            "The PR has a fresh approval, green CI, and is mergeable",
			"checkCommand":    "gh pr checks --json state",
			"exitCondition":   "All checks pass and the latest review is APPROVED",
			"maxIterations":   20,
			"compatibleTools": []any{"claude-code", "codex"},
			"kickoff":         "Run the pr-to-green loop on the open PR:\n1. Read every review finding.\n2. Fix the valid ones, push back on the invalid.\n3. Push and re-check until green.",
			"bundles":         []any{"superpowers"},
		},
		HasAsset:         false,
		AssetVersion:     nil,
		AssetContentHash: nil,
		Confidence:       "official",
		Status:           "published",
		DownloadCount:    12,
		ChargeCount:      3,
		CreatedAt:        "2026-05-01T09:00:00.000Z",
		UpdatedAt:        "2026-05-02T09:00:00.000Z",
	},
	{
		ID:            "01LSTDAILYBRIEFING000000XX",
		Slug:          "daily-briefing",
		ProfileHandle: "nsollazzo",
		ProfileName:   "Nicholas Sollazzo",
		ProfileTier:   ptr("official"),
		Name:          "Daily Briefing",
		Type:          "bot",
		Tagline:       "A weekday standup bot that summarizes overnight activity",
		Description:   ptr("A scheduled briefing covering GitHub PRs, incidents, and the calendar."),
		Category:      "Productivity",
		Tags:          []string{"standup", "schedule"},
		Official:      true,
		SourceURL:     "https://example.com/daily-briefing",
		RepoURL:       nil,
		InstallCmd:    nil,
		Data: map[string]any{
			"prompt":       "Summarize overnight PRs, incidents, and calendar for the team standup.",
			"integrations": []any{"github", "slack"},
			"schedule":     "0 9 * * 1-5",
			"platforms":    []any{"hermes"},
			"soulSlug":     "sherlock",
		},
		HasAsset:         false,
		AssetVersion:     nil,
		AssetContentHash: nil,
		Confidence:       "official",
		Status:           "published",
		DownloadCount:    8,
		ChargeCount:      1,
		CreatedAt:        "2026-06-01T09:00:00.000Z",
		UpdatedAt:        "2026-06-02T09:00:00.000Z",
	},
}

Listings is the fixture registry: six listings covering harness, cli, mcp, skill, a loop with a full LoopData payload (the `loop show` fixture), and a bot with a full BotData payload (the `bot install` fixture). The agent, plugin and memory types are deliberately empty so empty-result output is pinned too.

View Source
var MeUser = api.User{
	ID:          "usr_mock0001",
	Name:        "Ada Lovelace",
	Email:       "ada@example.com",
	Image:       ptr("https://example.com/ada.png"),
	GithubLogin: ptr("ada"),
}

MeUser is the identity /api/me answers for valid credentials (isAdmin false).

View Source
var Posts = []api.Post{
	{
		PostCard: api.PostCard{
			ID:           "01POSTINTRO0000000000000XX",
			Slug:         "introducing-positronick",
			SlugHistory:  []string{},
			Kind:         "article",
			Title:        "Introducing Positronick",
			Excerpt:      "Publish once, discover and use anywhere — the marketplace for everything AI.",
			Description:  ptr("Why we built a provider- and framework-agnostic registry for souls, MCP servers, CLIs, agents, skills and more."),
			ContentHash:  "1111aaaa1111aaaa1111aaaa1111aaaa1111aaaa1111aaaa1111aaaa1111aaaa",
			Version:      "1.0.0",
			Category:     "Announcements",
			Tags:         []string{"launch", "registry"},
			AuthorHandle: ptr("positronick"),
			AuthorName:   ptr("Positronick"),
			AuthorAvatar: ptr("https://example.com/positronick.png"),
			AuthorTier:   ptr("official"),
			ListingSlug:  nil,
			ListingName:  nil,
			CanonicalURL: nil,
			Status:       "published",
			ViewCount:    128,
			PublishedAt:  ptr("2026-05-20T12:00:00.000Z"),
			CreatedAt:    "2026-05-20T12:00:00.000Z",
			UpdatedAt:    "2026-05-21T09:00:00.000Z",
		},
		Content: "# Introducing Positronick\n\nPublish once — discover and use anywhere.\n",
	},
	{
		PostCard: api.PostCard{
			ID:           "01POSTCLIRELEASE000000000X",
			Slug:         "positronick-cli-v0-1-0",
			SlugHistory:  []string{},
			Kind:         "release",
			Title:        "Positronick CLI v0.1.0",
			Excerpt:      "Install souls and browse the registry from your terminal.",
			Description:  nil,
			ContentHash:  "2222bbbb2222bbbb2222bbbb2222bbbb2222bbbb2222bbbb2222bbbb2222bbbb",
			Version:      "1.0.0",
			Category:     "Releases",
			Tags:         []string{"cli", "release"},
			AuthorHandle: ptr("nsollazzo"),
			AuthorName:   ptr("Nicholas Sollazzo"),
			AuthorAvatar: nil,
			AuthorTier:   ptr("verified"),
			ListingSlug:  ptr("positronick-cli"),
			ListingName:  ptr("Positronick CLI"),
			CanonicalURL: ptr("https://github.com/positronick/cli/releases/tag/v0.1.0"),
			Status:       "published",
			ViewCount:    42,
			PublishedAt:  ptr("2026-06-01T09:00:00.000Z"),
			CreatedAt:    "2026-06-01T09:00:00.000Z",
			UpdatedAt:    "2026-06-01T09:00:00.000Z",
		},
		Content: "# Positronick CLI v0.1.0\n\nThe first public release of the command-line client.\n",
	},
	{
		PostCard: api.PostCard{
			ID:           "01POSTOPENCLAWLINK0000000X",
			Slug:         "openclaw-joins-the-registry",
			SlugHistory:  []string{},
			Kind:         "link",
			Title:        "OpenClaw joins the registry",
			Excerpt:      "A new open-source agent harness is now listed on Positronick.",
			Description:  nil,
			ContentHash:  "3333cccc3333cccc3333cccc3333cccc3333cccc3333cccc3333cccc3333cccc",
			Version:      "1.0.0",
			Category:     "Community",
			Tags:         []string{"openclaw", "news"},
			AuthorHandle: nil,
			AuthorName:   nil,
			AuthorAvatar: nil,
			AuthorTier:   nil,
			ListingSlug:  nil,
			ListingName:  nil,
			CanonicalURL: ptr("https://example.com/openclaw-joins"),
			Status:       "published",
			ViewCount:    9,
			PublishedAt:  ptr("2026-06-10T08:00:00.000Z"),
			CreatedAt:    "2026-06-10T08:00:00.000Z",
			UpdatedAt:    "2026-06-10T08:00:00.000Z",
		},
		Content: "OpenClaw, an open-source agent harness, is now listed on Positronick.\n",
	},
}

Posts is the fixture blog: one post of each kind (article, release, link) with deliberately different publish dates, authors and nullable fields so --kind filtering, newest-first ordering, the did-you-mean hint and the null-field rendering each have something to disagree about. Ordered oldest-first; the handler sorts newest-first like the server. Content is the body only (no frontmatter) — the JSON detail's `content`; the .md endpoint wraps it in frontmatter via PostMarkdown.

View Source
var ResearchPosts = []api.ResearchItem{
	{
		Slug:         "shipping-the-cli",
		Title:        "Shipping the Positronick CLI",
		Excerpt:      "Install souls and browse the registry from your terminal.",
		Kind:         "article",
		Category:     "Engineering",
		Tags:         []string{"cli", "launch"},
		URL:          "https://positronick.com/blog/shipping-the-cli",
		MdURL:        "https://positronick.com/api/blog/shipping-the-cli.md",
		CanonicalURL: nil,
		ContentHash:  "dddd4444dddd4444dddd4444dddd4444dddd4444dddd4444dddd4444dddd4444",
		PublishedAt:  ptr("2026-05-20T12:00:00.000Z"),
	},
	{
		Slug:         "hermes-v2-1-0",
		Title:        "Hermes v2.1.0",
		Excerpt:      "Tool-calling fixes and a faster device flow.",
		Kind:         "release",
		Category:     "Releases",
		Tags:         []string{"hermes"},
		URL:          "https://positronick.com/blog/hermes-v2-1-0",
		MdURL:        "https://positronick.com/api/blog/hermes-v2-1-0.md",
		CanonicalURL: ptr("https://github.com/NousResearch/hermes/releases/tag/v2.1.0"),
		ContentHash:  "eeee5555eeee5555eeee5555eeee5555eeee5555eeee5555eeee5555eeee5555",
		PublishedAt:  ptr("2026-06-01T09:00:00.000Z"),
	},
	{
		Slug:         "openclaw-launch",
		Title:        "OpenClaw launches its agent harness",
		Excerpt:      "A new open-source harness joins the registry.",
		Kind:         "link",
		Category:     "Community",
		Tags:         []string{"openclaw", "news"},
		URL:          "https://positronick.com/blog/openclaw-launch",
		MdURL:        "https://positronick.com/api/blog/openclaw-launch.md",
		CanonicalURL: ptr("https://example.com/openclaw-launch"),
		ContentHash:  "ffff6666ffff6666ffff6666ffff6666ffff6666ffff6666ffff6666ffff6666",
		PublishedAt:  ptr("2026-06-10T08:00:00.000Z"),
	},
}

ResearchPosts is the fixture "what's new" feed: one of each post kind (article, release, link) with deliberately different publish dates, categories and tags so --since/--kind/--category/--tag and the `latest` high-water mark each have something to disagree about. Ordered oldest-first; the handler sorts newest-first like the server.

View Source
var SkillContent = map[string]string{
	"superpowers": "---\nname: superpowers\ndescription: Skills for planning, debugging and shipping.\n" +
		"---\n\n# Superpowers\n\nA battle-tested methodology pack: plan, then execute, then verify.\n",
	"mismatched-skill": "---\nname: not-mismatched-skill\ndescription: name disagrees with the slug.\n" +
		"---\n\n# Mismatched Skill\n",
}

SkillContent is the fixture hosted SKILL.md body for skill listings whose HasAsset is true, keyed by slug — kept separate from api.Listing since the real JSON detail endpoint has no content field (only the .md endpoint serves the body; see internal/api's SkillMarkdown). superpowers' frontmatter name matches its slug (the happy path); mismatched-skill's deliberately doesn't, for the name-must-equal-slug install rule.

View Source
var Souls = []api.Soul{
	{
		SoulCard: api.SoulCard{
			ID:            "01SOULSHERLOCK0000000000XX",
			Slug:          "sherlock",
			SlugHistory:   []string{"holmes"},
			Name:          "Sherlock",
			AuthorHandle:  "acdoyle",
			AuthorName:    ptr("Arthur Conan Doyle"),
			AuthorURL:     ptr("https://example.com/acdoyle"),
			Tagline:       "Deductive debugging: reason from evidence, never from vibes",
			Description:   ptr("A consulting-detective personality for root-cause analysis."),
			Category:      "Technical",
			Tags:          []string{"detective", "reasoning"},
			Frameworks:    []string{"hermes", "claude-code"},
			Models:        []string{"claude-fable-5"},
			Version:       "1.2.0",
			License:       "MIT",
			RepoURL:       ptr("https://example.com/souls/sherlock"),
			ContentHash:   "aaaa1111aaaa1111aaaa1111aaaa1111aaaa1111aaaa1111aaaa1111aaaa1111",
			Status:        "published",
			DownloadCount: 42,
			ChargeCount:   5,
			RatingAvg:     ptr(4.5),
			RatingCount:   8,
			ArenaRank:     ptr(2),
			CreatedAt:     "2026-01-05T09:00:00.000Z",
			UpdatedAt:     "2026-01-06T09:00:00.000Z",
		},
		Content: "---\nname: Sherlock\n---\n\n# SOUL.md\n\nYou are Sherlock.\n\n- Reason from evidence.\n- Never guess.\n",
	},
	{
		SoulCard: api.SoulCard{
			ID:            "01SOULWATSON000000000000XX",
			Slug:          "watson",
			SlugHistory:   []string{},
			Name:          "Watson",
			AuthorHandle:  "acdoyle",
			AuthorName:    nil,
			AuthorURL:     nil,
			Tagline:       "A steady pair-programming companion",
			Description:   nil,
			Category:      "Professional",
			Tags:          []string{"assistant"},
			Frameworks:    []string{"hermes"},
			Models:        []string{},
			Version:       "0.3.1",
			License:       "Apache-2.0",
			RepoURL:       nil,
			ContentHash:   "bbbb2222bbbb2222bbbb2222bbbb2222bbbb2222bbbb2222bbbb2222bbbb2222",
			Status:        "published",
			DownloadCount: 7,
			ChargeCount:   0,
			RatingAvg:     nil,
			RatingCount:   0,
			ArenaRank:     nil,
			CreatedAt:     "2026-02-10T09:00:00.000Z",
			UpdatedAt:     "2026-02-10T09:00:00.000Z",
		},
		Content: "# SOUL.md\n\nYou are Watson. You assist, summarize, and keep the record.\n",
	},
	{
		SoulCard: api.SoulCard{
			ID:            "01SOULMORIARTY0000000000XX",
			Slug:          "moriarty",
			SlugHistory:   []string{},
			Name:          "Moriarty",
			AuthorHandle:  "napoleon-of-crime",
			AuthorName:    ptr("James Moriarty"),
			AuthorURL:     nil,
			Tagline:       "An adversarial red-team persona that attacks every assumption your plan quietly makes",
			Description:   ptr("Breaks plans before production does."),
			Category:      "Experimental",
			Tags:          []string{"adversarial", "red-team"},
			Frameworks:    []string{"openclaw"},
			Models:        []string{"claude-fable-5", "gpt-6"},
			Version:       "2.0.0",
			License:       "MIT",
			RepoURL:       nil,
			ContentHash:   "cccc3333cccc3333cccc3333cccc3333cccc3333cccc3333cccc3333cccc3333",
			Status:        "published",
			DownloadCount: 99,
			ChargeCount:   11,
			RatingAvg:     ptr(4.9),
			RatingCount:   21,
			ArenaRank:     ptr(1),
			CreatedAt:     "2026-03-01T09:00:00.000Z",
			UpdatedAt:     "2026-03-02T09:00:00.000Z",
		},
		Content: "# SOUL.md\n\nYou are Moriarty. Attack the plan.\n",
	},
}

Souls is the fixture gallery: three souls with deliberately different download counts, dates, categories and frameworks so ranking, sorting and filtering each have something to disagree about.

Functions

func Handler

func Handler() http.Handler

Handler returns an http.Handler implementing the read API over the fixture data, including the server's JSON error envelope on 404 and on an unknown ?type=. Requests to /api/souls/{slug}.md and /api/skills/{slug}.md are answered with 418 — both .md endpoints bump an install counter, so any read command hitting them is a bug the consuming test must surface.

func InstallHandler

func InstallHandler() http.Handler

InstallHandler returns Handler plus the install contract: GET /api/souls/{slug}.md answers a soul's markdown body verbatim, and GET /api/skills/{slug}.md answers a skill's hosted SKILL.md body from SkillContent — the endpoints that bump the server's download counter. Read-command tests must keep using Handler (which answers 418 on both .md paths so an accidental hit fails loudly); only install-path tests opt into this handler.

func PostMarkdown added in v0.2.0

func PostMarkdown(p api.Post) string

PostMarkdown renders a fixture post as the raw markdown file the .md endpoint serves: a minimal frontmatter block plus the body. It is deliberately distinct from the JSON detail's body-only `content`, so a test can prove `blog show --raw` read the .md endpoint. Shared by the handler and the CLI raw-output test.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL