Documentation
¶
Overview ¶
Package datagrid is the GoFastr data-grid plugin: AG Grid Community's infinite row model inside an opaque-origin sandboxed iframe, with sort, filter and paging executed by the HOST's Go rows source instead of the frame.
Why this shape: every other plugin in this repo moves ONE small document across the postMessage bridge. A grid moves rows by the thousand, and the framed CSP sets connect-src 'none' (the frame can never fetch its own rows), so the plugin is only interesting if the data arrives page-by-page from the host. Server-side sort/filter/paging over a correlated fire-and-forget event pair — requestRows → rowsResult, exactly the richtext requestUpload → uploadResult pattern — IS the product here; a grid that loads all rows up front would prove nothing about the platform.
The canonical doc (schema datagrid-v1) is VIEW STATE ONLY: {columns[], sort, filter, pageSize}. Rows are never part of the doc, never saved into it and never echoed back out of it — the doc round-trips through the hidden form field like every other plugin's, while the data keeps flowing one page per bridge round trip.
Capabilities: data:read + theme:read are always granted; data:write (cell edits and view-state saves) and data:export (CSV egress) are optional, granted exactly when the host supplies the matching handler option — the same shape as pdf's pdf:export. pluginhost.Allow is a capability gate, NOT authentication: it passes for anonymous callers, so any route that WRITES (POST /cell) must be documented as requiring the host to check the session in its own handler. See docs/datagrid.md.
Index ¶
- Constants
- func DefaultCapabilities() []string
- func Mount(cfg MountConfig) render.HTML
- func UIHostOption() uihost.Option
- type CellWriteRequest
- type Column
- type Doc
- type ExportRequest
- type MountConfig
- type Option
- func WithCapabilities(caps ...string) Option
- func WithCellWriteHandler(fn func(ctx context.Context, req CellWriteRequest) error) Option
- func WithDemoDoc(doc Doc) Option
- func WithDemoPage() Option
- func WithDemoRoute(path string) Option
- func WithDevGrantAll() Option
- func WithExportHandler(fn func(ctx context.Context, req ExportRequest) (string, error)) Option
- func WithRowsSource(fn func(ctx context.Context, q RowsQuery) (RowsPage, error)) Option
- func WithSaveHandler(fn func(ctx context.Context, req SaveRequest) error) Option
- type Plugin
- type Row
- type RowsPage
- type RowsQuery
- type SaveRequest
- type SortModel
Constants ¶
const ( Name = "datagrid" Version = "0.1.0" RoutePrefix = "/__gofastr/plugin/datagrid" GridHTMLURL = RoutePrefix + "/grid.html" GridJSURL = RoutePrefix + "/grid.js" GridCSSURL = RoutePrefix + "/grid.css" AdapterScriptURL = RoutePrefix + "/adapter.js" ConfigScriptURL = RoutePrefix + "/config.js" RowsURL = RoutePrefix + "/rows" CellWriteURL = RoutePrefix + "/cell" ExportURL = RoutePrefix + "/export" SaveURL = RoutePrefix + "/save" DemoURL = "/datagrid" SchemaVersion = "datagrid-v1" // CapDataWrite gates POST /cell and POST /save. It is OPTIONAL — it is // NOT in [DefaultCapabilities]. [WithCellWriteHandler] appends it (the // pdf WithExportHandler pattern), because editing rows is egress the // host explicitly turned on. CapDataWrite = "data:write" // CapDataExport gates POST /export. Optional for the same reason: CSV // export produces bytes on the host (a sandboxed frame cannot start a // download), and [WithExportHandler] is what turns that on. CapDataExport = "data:export" )
Identity and route constants. Both this plugin and host/adapter.js hard-code these exactly (protocol-v1.md §2/§10). The demo lives at /datagrid.
These mirror the datagrid row in plugins.json; internal/registry tests pin Name + RoutePrefix against that row, so they MUST NOT drift.
Variables ¶
This section is empty.
Functions ¶
func DefaultCapabilities ¶
func DefaultCapabilities() []string
DefaultCapabilities is the always-on grant set advertised to the grid: reading pages of rows and bridging theme tokens. data:write and data:export are deliberately absent — they are optionalCapabilities, appended by WithCellWriteHandler / WithExportHandler.
func Mount ¶
func Mount(cfg MountConfig) render.HTML
Mount renders the generic mount marker plus the hidden input the adapter syncs on docChanged. Drop it into a form. All interpolated values are HTML-escaped inside pluginhost.MountMarker.
func UIHostOption ¶
UIHostOption injects the platform broker, this plugin's config script, and this plugin's adapter (in that order — the adapter reads the config global the config script publishes, and registers with the broker the former defines).
Types ¶
type CellWriteRequest ¶
CellWriteRequest is one cell edit relayed from the frame via POST /cell.
type Column ¶
type Column struct {
Field string `json:"field"`
Header string `json:"header"`
// Width is the initial column width in px (0 = AG Grid default).
Width int `json:"width,omitempty"`
// Type is "text" (default) or "number". Numbers right-align in the
// frame and sort numerically in the host's rows source.
Type string `json:"type,omitempty"`
// Sortable / Editable toggle the obvious frame affordances. Sortable
// columns trigger server-side sorts; editable ones (only when data:write
// is granted) open a cell editor whose result round-trips through
// POST /cell.
Sortable bool `json:"sortable,omitempty"`
// Filterable is accepted for symmetry with the doc shape; the demo
// filter box filters every column, so it is currently unused by the
// frame.
Filterable bool `json:"filterable,omitempty"`
Editable bool `json:"editable,omitempty"`
}
Column describes one grid column. Columns are part of the canonical (view-state) doc: the host declares the schema, the frame renders it, and the rows requests carry it back so the host's rows source knows which fields are numeric when sorting server-side.
type Doc ¶
type Doc struct {
SchemaVersion string `json:"schemaVersion"`
Columns []Column `json:"columns"`
Sort []SortModel `json:"sort,omitempty"`
Filter string `json:"filter,omitempty"`
PageSize int `json:"pageSize,omitempty"`
}
Doc is the canonical datagrid-v1 view-state document. It describes HOW the table is viewed — never the rows themselves.
type ExportRequest ¶
type ExportRequest struct {
DocID string
Format string // always "csv" today
// Columns the export covers, normalised by /export before the scan.
Columns []Column
Sort []SortModel
Filter string
// CSV streams the export body. The scan pages through the rows source
// in 5,000-row chunks and spills to a temp file as it goes, so peak
// memory stays at one chunk regardless of table size, and a mid-scan
// source error aborts the whole export (an error response, never a
// short file). The handler MUST fully consume CSV before returning —
// the underlying temp file is removed once the handler returns.
CSV io.Reader
// RowCount is the number of data rows in CSV (the header is excluded).
RowCount int
}
ExportRequest is handed to the host's export handler: the CSV the plugin generated host-side from the rows source under the request's sort/filter, plus the view-state context. The handler stores the CSV (io.Copy or io.ReadAll into whatever storage the host uses) and returns a URL the host page can download from — the frame cannot download.
type MountConfig ¶
type MountConfig struct {
// DocID is the persistence key for the view-state doc.
DocID string
// Doc is the initial view-state doc JSON server-rendered into the
// marker (data-fui-plugin-doc) — the frame reads columns, sort, filter
// and pageSize out of it on init.
Doc string
// Field is the hidden-input name the adapter mirrors the current
// view-state doc into on docChanged, so a normal form POST round-trips
// it. Defaults to "datagrid_doc". The name is published on the marker
// as data-fui-plugin-field, which is how the adapter finds it — the
// input itself is rendered after the marker, not inside it.
Field string
// MinHeight is the initial iframe height before the grid sizes itself.
MinHeight string
// Capabilities is an optional CSV grant override.
Capabilities string
}
MountConfig configures Mount.
type Option ¶
type Option func(*Plugin)
Option configures a Plugin.
func WithCapabilities ¶
WithCapabilities overrides the grant set advertised to the grid. Default: DefaultCapabilities. data:write / data:export are appended separately by their handler options even when this fully replaces the set — the gate is on egress the host explicitly enabled, so silently dropping it would just break editing with a 403.
Grants are matched with the framework's scope grammar at runtime, so wildcards are legal here: "data:*" and "*:*" imply data:write and data:export. New requires the matching handler for anything the grant set implies — a wildcard that implies an optional capability without its handler is a construction panic, exactly like the literal capability.
func WithCellWriteHandler ¶
func WithCellWriteHandler(fn func(ctx context.Context, req CellWriteRequest) error) Option
WithCellWriteHandler installs the persistence hook behind POST /cell AND opts the plugin into the optional data:write capability (appended to the grant set if not already present — the pdf WithExportHandler pattern). The handler is the host's chance to authorize the write against the real session: pluginhost.Allow is a capability gate, not authentication, and it passes for anonymous callers.
func WithDemoDoc ¶
WithDemoDoc sets the view state the demo page mounts when nothing has been saved yet — the demo's column set, since the plugin itself owns no schema.
func WithDemoPage ¶
func WithDemoPage() Option
WithDemoPage registers the self-contained themed demo page at DemoURL.
func WithDemoRoute ¶
WithDemoRoute overrides where WithDemoPage mounts the demo (default DemoURL, "/datagrid").
func WithDevGrantAll ¶
func WithDevGrantAll() Option
WithDevGrantAll short-circuits the capability gate (Phase-0 demo / tests). It bypasses BOTH gate sides, so the write routes behind it must still fail closed on an unwired handler (a clear error, never a panic) — see handlers.go.
func WithExportHandler ¶
WithExportHandler installs the export hook behind POST /export AND opts the plugin into the optional data:export capability. The plugin generates the CSV host-side from the rows source (streamed through ExportRequest's CSV reader); the handler stores it and returns a URL the host page turns into a download.
func WithRowsSource ¶
WithRowsSource installs the server-side data source backing POST /rows and the CSV export scan. There is NO default: unlike pdf (which ships a sample document), a grid has no meaningful embedded dataset, so New panics without this option. The function runs in the host process with the caller's context — sorting, filtering and paging are the point, so the source must apply them, not return everything.
func WithSaveHandler ¶
func WithSaveHandler(fn func(ctx context.Context, req SaveRequest) error) Option
WithSaveHandler overrides the view-state persistence hook. The default stores the canonical doc JSON in an in-memory map keyed by DocID.
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin is the data-grid plugin. It implements framework.Plugin and mirrors the mermaid/pdf shape: opaque-origin sandboxed iframe, protocol v1 over postMessage, go:embed'd frame bundle, capability gate, host-side RPC routes. The difference is the traffic profile — many small correlated event round trips instead of one document push.
func (*Plugin) Capabilities ¶
Capabilities returns the grant set this plugin advertises to the grid.
func (*Plugin) LoadDoc ¶
LoadDoc returns the last-saved view-state JSON for docID (demo round-trip). ok is false when the doc has never been saved.
func (*Plugin) Manifest ¶
func (p *Plugin) Manifest() pluginhost.Manifest
type Row ¶
Row is one data row as it crosses the bridge: a stable id plus string cells keyed by column field. Cell typing is a host concern — the bridge carries display strings, and the rows source sorts its own typed data.
type RowsPage ¶
RowsPage is one server-side page. LastRow is the total row count under the current sort/filter, or -1 when unknown (AG Grid's infinite model contract).
type RowsQuery ¶
type RowsQuery struct {
DocID string
StartRow int
EndRow int
Sort []SortModel
Filter string
Columns []Column
}
RowsQuery is one server-side page request, the Go twin of the frame's requestRows params. Columns ride along so the source can sort typed columns correctly (numbers numerically) without the plugin hard-coding a schema.
type SaveRequest ¶
type SaveRequest struct {
DocID string
Doc Doc
// DocJSON is the canonical JSON of the VALIDATED, normalised doc — the
// same shape /save hands the save handler. The raw request body's JSON
// is never persisted: a doc that failed a bound (page size, sort keys,
// filter length) cannot be saved verbatim and reloaded later.
DocJSON string
SchemaVersion string
}
SaveRequest is the view-state persist signal (POST /save).