Elyra
Elyra The coding agent eTerm The terminal that knows where each command ends Huskarl AI colleagues on your own Mac Starf An activity monitor for Apple silicon that never invents a number etrans An SSH and SFTP client for macOS Elyra VM Virtual machines for macOS, Linux and Windows on your Mac Litr A small, native web browser for macOS Notr A notebook for macOS e The native code editor Elyra Grove Native local development environment Askr The real server for Laravel & PHP Elyra Framework Rust + Svelte 5 framework for desktop apps Elyra Conductor Local project conductor Refr Local-first PDF workspace for macOS Elyra Workspace A desktop workspace for coding agents Elyra SQL Server MySQL-compatible SQL server in Rust Elyra Félagi Agents as teammates on one board Elyra SQL Client Native desktop SQL workbench Elyra SQL Anywhere Replication-ready SQL engine Elyra Sjá SEO & GEO workspace for macOS Elyra DataGrid Server-driven data grid for Laravel
Elyra
Protocol reference

Protocol reference

The protocol is a small, framework-neutral JSON contract. It is defined once as JSON Schema and mirrored as TypeScript types (@elyra/datagrid-protocol) and PHP DTOs (elyra/datagrid-protocol). The mirrors are hand-written; a schema-consistency check (npm run test:protocol, run in CI) asserts the enumerations that matter for correctness and security — filter operators, sort direction, search modes, aggregate functions, meta flags — stay identical across the schema, the TypeScript unions, and the PHP FilterCompiler.

  • Schemas: packages/protocol/schema/grid-request.schema.json, grid-response.schema.json
  • TypeScript: import type { GridRequest, GridResponse } from '@elyra/datagrid-protocol'
  • PHP: Elyra\DataGrid\Protocol\{GridRequest, GridMutation}

GridRequest

interface GridRequest {
  columns?: string[];         // fields to return (default: all declared)
  sort?: SortSpec[];          // multi-column, applied in order
  filter?: FilterGroup;       // structured filter tree
  search?: Search;            // global search
  group?: GroupSpec[];        // grouping with per-group aggregates
  aggregates?: AggregateSpec[]; // footer aggregates over the whole result
  view: Viewport;             // required — the viewport window
  meta?: ('total' | 'facets' | 'timing')[];  // opt-in extras
  facets?: FacetSpec[];       // facet specs (with meta: ['facets'])
}

interface Viewport { skip: number; take: number; }   // take: visible rows, ≤ 1000
interface SortSpec { field: string; dir: 'asc' | 'desc'; nulls?: 'first' | 'last'; }

Filters

interface FilterGroup { logic: 'and' | 'or'; filters: (FilterCondition | FilterGroup)[]; }
interface FilterCondition { field: string; op: FilterOp; value?: Scalar | Scalar[]; }

type FilterOp =
  | 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte'
  | 'in' | 'nin' | 'between'
  | 'contains' | 'ncontains' | 'startsWith' | 'endsWith'
  | 'isNull' | 'isNotNull';
  • in/nin take arrays; between takes [min, max]; isNull/isNotNull take no value.

Search

interface Search {
  query: string;
  mode?: 'hybrid' | 'fulltext' | 'substring';   // default substring
  fields?: string[];
  vectorField?: string;      // embedding column for hybrid mode
}

Grouping & aggregates

interface GroupSpec { field: string; aggregates?: AggregateSpec[]; }

// Page the OUTERMOST grouping level. Descendants of a selected outer group come
// with it, so the group tree stays whole. The response then carries groupTotal.
groupView?: Viewport;
interface AggregateSpec {
  field: string;
  fn: 'sum' | 'avg' | 'min' | 'max' | 'count' | 'countDistinct' | 'median' | 'percentile';
  p?: number;    // fraction in (0,1) for percentile
  as?: string;   // output alias
}
interface FacetSpec { field: string; top?: number; }   // top-N most frequent

GridResponse

interface GridResponse {
  rows: Row[];                          // the viewport slice
  view: Viewport;                       // echo of skip/take served
  total?: number | null;               // present if meta included 'total'
  groups?: GroupRow[];                  // when grouping was requested
  aggregates?: Record<string, number | null>;  // footer aggregates
  facets?: Record<string, FacetBucket[]>;       // per-field value lists
  timing?: { elapsedMs?: number; backend?: string; usedIndex?: boolean | null; fullScan?: boolean | null; };
  notice?: { level: 'info' | 'warn'; text: string; url?: string };  // e.g. unlicensed reminder
}

interface FacetBucket { value: Scalar; count: number; }
interface GroupRow {
  field: string; value: Scalar; count: number;
  level?: number;   // depth in the hierarchy (0 = outermost)
  path?: string;    // stable key for collapse tracking
  aggregates?: Record<string, number | null>;
}

GridMutation

type MutationOp = 'create' | 'update' | 'delete';
interface GridMutation { op: MutationOp; id?: Scalar; changes?: Record<string, unknown>; }
interface GridMutationResult {
  ok: boolean;
  id?: Scalar;                          // resulting key (e.g. after create)
  row?: Row;                            // persisted row echoed back
  errors?: Record<string, string[]>;   // per-field validation errors
}

Cost model in the contract

  • view.take is a viewport window, not a page size.
  • Expensive extras are opt-in via meta: total (row count), facets (distinct value lists), timing. Nothing is computed unless requested.