docs(modules): rewrite cabana, wire, towel, festival, compass, backpack READMEs

This commit is contained in:
Jakub Zych
2026-09-28 15:43:23 +02:00
parent cc584e906e
commit 1bd34948a9
6 changed files with 574 additions and 6 deletions

View File

@@ -1,3 +1,74 @@
# wire
`wire` writes JSON response envelopes and normalizes optional slices for stable API serialization. `surf` and Fonoteka API controllers import it for HTTP responses; use `wire.WriteJSON` from `response.go`.
JSON response helpers and value types that keep API bodies byte-compatible with a PHP (WinterCMS/Laravel) backend.
`import "git.golem15.com/golem15/summercms/modules/wire"`
## Overview
`wire` is the lowest layer of the HTTP stack: it decides how a Go value becomes response bytes. Its helpers reproduce what `json_encode` and Carbon produce in a WinterCMS or Laravel app (unescaped HTML characters, no trailing newline, `+00:00` timestamps, nullable booleans, `[]` rather than `null` for empty lists), so an endpoint ported from PHP returns the same body its existing clients already parse. [surf](../surf/README.md) uses it for the opaque 500 response of its panic recovery, and application handlers use it directly for their JSON responses.
## Features
- `wire.WriteJSON` encodes a value with HTML escaping disabled and without the trailing newline `encoding/json` adds, then sets `Content-Type: application/json` and the status code. If encoding fails, it writes the opaque 500 body instead of a partial response.
- `wire.WriteOpaque500` writes a 500 response with the fixed body `{"error":true,"message":"Internal server error"}`, which reveals nothing about the failure.
- `wire.Time` wraps `time.Time` and always marshals in UTC as `2006-01-02T15:04:05+00:00` (Carbon's form, never Go's `Z`). It unmarshals a timestamp with a numeric offset or an RFC 3339 `Z` timestamp, and turns `null` into the zero time.
- `wire.TriBool` models a nullable boolean: when `wire.TriBool.Valid` is false it marshals as `null`, otherwise as `wire.TriBool.Value`.
- `wire.Slice` returns a non-nil empty slice for a nil input, so optional lists marshal as `[]` instead of `null`.
## Usage
```go
package blog
import (
"net/http"
"time"
"git.golem15.com/golem15/summercms/modules/wire"
)
type postJSON struct {
ID uint `json:"id"`
Title string `json:"title"`
Tags []string `json:"tags"`
Featured wire.TriBool `json:"featured"`
PublishedAt wire.Time `json:"published_at"`
}
func showPost(w http.ResponseWriter, r *http.Request) {
var tags []string // nil when the post has no tags
body := postJSON{
ID: 1,
Title: "Hello & welcome", // "&" stays unescaped
Tags: wire.Slice(tags), // marshals as []
Featured: wire.TriBool{}, // marshals as null
PublishedAt: wire.Time{Time: time.Now()}, // "...+00:00"
}
wire.WriteJSON(w, http.StatusOK, map[string]any{"data": body})
}
```
## API reference
| Identifier | Description |
|------------|-------------|
| `wire.WriteJSON` | Writes a JSON body with HTML escaping off and no trailing newline; falls back to the opaque 500 on an encoding error. |
| `wire.WriteOpaque500` | Writes the fixed `{"error":true,"message":"Internal server error"}` 500 response. |
| `wire.Time` | `time.Time` wrapper that marshals as UTC `+00:00` and reads both `+00:00` and `Z` forms. |
| `wire.TriBool` | Nullable boolean: `wire.TriBool.Valid` false marshals `null`, otherwise `wire.TriBool.Value`. |
| `wire.Slice` | Generic helper that turns a nil slice into an empty one so it marshals as `[]`. |
## Dependencies
- SummerCMS modules: none.
- Third-party: none.
- Standard library: `bytes`, `encoding/json`, `net/http`, `time`.
## Testing
```sh
go test ./modules/wire/...
```
The tests use `net/http/httptest` and need no external services.