Saved reports and runtime bindings
What to save
Section titled “What to save”A portable PivotReport contains field/measure IDs, axes, operation declarations, filters, ranking, totals and presentation. V2 explicitly admits derived fields and operation policies; opt into V2 parsing/execution. A native PivotConfig is a grid configuration, not the same persistence format.
Do not serialize source records, functions, DOM mounts, adapter instances, credentials or source revision callbacks as report authority. Restore named host bindings (sources, engines, aggregate callbacks, text callbacks) before executing a report that needs them. A missing binding is a recoverable integration error, not permission to fall back to a different engine.
const json = controller.exportReportJson();// Send json to the application's authorized persistence endpoint.// After restoring the matching runtime, parse/import with explicit V2 opt-in// when the document uses version 2. Execute before updating the saved record.The downloadable executable round-trips an executed V2 hierarchy through exportReportJson, explicit V2 parsing and serialization.
Use portable report APIs for exact V1/V2 parse/compile/serialize signatures. aggregationSemantics pins local/server execution; keep it with the existing operations when migrating. V1-only savePivotReport/loadPivotReport helpers are distinct from V1/V2 durable saved-view collections.
Attached saved views
Section titled “Attached saved views”The stock attached menu is session-only unless configured. The public browser adapter supplies durable origin storage:
import { createBrowserPivotViewStorage } from '@revolist/pivot';pivot.setSavedViewStorage({ scope: `${userId}/${workspaceId}/${reportId}`, storage: createBrowserPivotViewStorage(),});Browser scopes separate application data but are not an authorization boundary. Browser writes require a secure context with Web Locks for atomic comparisons; storage-disabled/quota/unsupported errors remain visible. On user change replace/clear the storage integration. setSavedViewStorage() returns to a new empty session list without deleting durable records. Replacing storage cancels pending restores and retires the previous manager.
For a server store implement public PivotViewStorage.read and compareAndSwap with an authorized atomic compare-and-swap on the entire stored string. A separate read then write is not atomic. See Persistent saved views for the interface and failure behavior.
| Setting/operation | Default/limit | Recovery |
|---|---|---|
| Attached storage | Session snapshots | Install a durable adapter explicitly |
| Collection size | 200 views, 2000000 UTF-16 code units | Delete/archive views; actual browser quota may be lower |
| Concurrent write | Compare-and-swap conflict | Reload views, inspect current state, retry explicitly |
| Failed save | Draft name retained | Repair storage/auth/quota and retry |
| Failed restore | Current report retained | Restore bindings/fields or repair/delete saved view |
| Malformed collection | Not overwritten | Back up and repair through host storage, then Reload views |
Standalone configurator actions.viewManager.save/rename/remove may return a Promise; resolve only after durable host acceptance and reject on failure. list() and subscribe() stay synchronous: maintain a cached list and notify after changes. A write committed before cancellation cannot be undone by retiring the UI callback.
Host-owned persistence policy
Section titled “Host-owned persistence policy”Authorize every server read/write against the authenticated user and workspace. Handle retention, sharing, encryption, conflict policy and schema migration in the application. Governed shared-view catalogs are deferred. Validate a restored report, reattach current runtime registries, execute it, inspect known KPIs, and only then announce success or save the migrated document.
For custom UIs attached to a grid use pivot.getAuthoringController() so history and committed state have one owner. It deliberately excludes source/runtime replacement and report.replace; use the plugin’s supported import path for report replacement. Standalone controllers expose their own full lifecycle. See custom integrations.