feat(cabana): widget action payload and data channel (quick-261006-eyj)

- pact.AdminActionInput.Payload (json.RawMessage) carries the widget's own
  JSON value untouched; pact.AdminActionResult.Data is passed through as data
- cabana decodes payload with a 64 KiB cap (422 on body), refuses it on the
  toolbar and record routes, and embeds Data once encoded with a 256 KiB cap
  (opaque 500 when larger or unencodable); fill stays filtered
- root .swaggo overrides json.RawMessage so swag keeps record_id and values;
  admin.json and schema.d.ts regenerated (payload?: unknown, data?: unknown)
- TestWidgetPayloadAndData covers pass-through, cap, refusal and data 500
- cabana and pact READMEs, partials-and-widgets and admin-spa docs updated
This commit is contained in:
Jakub Zych
2026-10-06 11:13:16 +02:00
parent 964145628a
commit 6af88f9df6
12 changed files with 257 additions and 32 deletions

View File

@@ -2,6 +2,7 @@ package pact
import (
"context"
"encoding/json"
"io/fs"
"net/http"
"time"
@@ -233,20 +234,33 @@ type AdminAction struct {
// record the framework loaded through the controller's FormExtendQuery scope,
// nil when RecordID is nil. Values is the widget's snapshot of its fill
// fields, already reduced to the field's declared fill keys and to scalars.
// Payload is the widget's own payload: the `summer-action` event's
// detail.payload, posted by the SPA as the body key `payload`. It is any JSON
// value up to 64 KiB, exactly the bytes the client sent, nil when the request
// carried none and always nil for a toolbar action. The framework neither
// inspects nor filters it: an action decodes it itself with encoding/json and
// validates any ids it contains against its own scope.
type AdminActionInput struct {
Field string
RecordID *uint64
Record any
Values map[string]any
Payload json.RawMessage
}
// AdminActionResult is an action's answer. Message is a phrase key or text,
// localized by the framework and shown as a toast. Fill is the widget
// write-back; keys outside the field's declared fill keys and non-scalar
// values are dropped before the response is written.
// values are dropped before the response is written. Data is structured data
// for the widget: it is written to the response as `data` exactly as it
// encodes (any JSON value, capped at 256 KiB encoded; a larger or unencodable
// value is a 500), is not subject to the fill allowlist, and is omitted when
// nil. The SPA sets it on the element as the `data` attribute and announces it
// with a `summer-result` event.
type AdminActionResult struct {
Message string
Fill map[string]any
Data any
}
// HasAdminActions is implemented by an admin controller that registers named