# Protocol reference

> Understand the InertiaX envelope, validation boundary, and versioning rules.

Source: https://docs.inertiax.dev/table/protocol/

The protocol defines the serialized table response. Use the Laravel builders for application code; use these schemas and the decoder when building validation or integration tooling.

The development protocol is draft `0.3.0` with checksum `d44c495114d1c4a3add438136683eca7c994662e8dc472cecd6dd8dc60081a39`.

Number presentation and table-local problems require a matching contract in the Laravel producer and frontend consumers. Repository development artifacts do not establish feature support in already-published package versions.

Declare `@inertiax/table-protocol` as a direct dependency if your source imports it. It owns the Table contract. `@inertiax/protocol` supplies common JSON types and validation primitives; it has no Table decoder or Table schema exports.

## Shipped reference files

The installed `@inertiax/table-protocol` package exposes the maintained contract without repository access:

| Package subpath                                                            | Purpose                                                        |
| -------------------------------------------------------------------------- | -------------------------------------------------------------- |
| `@inertiax/table-protocol/schemas/envelope.schema.json`                    | Envelope entry schema                                          |
| `@inertiax/table-protocol/schemas/table.schema.json`                       | Complete Table component schema                                |
| `@inertiax/table-protocol/schemas/common.schema.json`                      | Table definitions and references to common identifiers         |
| `@inertiax/table-protocol/schemas/foundation.schema.json`                  | Pinned common JSON definitions included in this Table artifact |
| `@inertiax/table-protocol/contract.json`                                   | Table contract identity and common-schema provenance           |
| `@inertiax/table-protocol/fixtures/manifest.json`                          | Accepted and rejected fixture inventory                        |
| `@inertiax/table-protocol/fixtures/valid/minimal.json`                     | Smallest complete valid Table example                          |
| `@inertiax/table-protocol/fixtures/valid/custom-type.json`                 | Namespaced custom leaf example                                 |
| `@inertiax/table-protocol/VERSION` and `@inertiax/table-protocol/CHECKSUM` | Installed artifact identity                                    |

Use these package subpaths in validators, tests, or tooling. Application code normally needs only the root package exports.

## Envelope

Every payload contains a protocol version and a component:

```json
{
  "protocolVersion": "0.3.0",
  "component": {
    "type": "table",
    "id": "users"
  }
}
```

The abbreviated component above is explanatory, not a valid complete Table. Use the shipped `fixtures/valid/minimal.json` subpath for executable minimal input and the shipped schemas for the complete contract.

## Table component shape

A successful Table component carries:

- a stable `type: "table"` and instance `id`;
- Column and Filter definitions;
- capabilities for pagination, sorting, selection, filter mode, and refresh;
- server state for pagination, ordered sorting, recursive filters, and search;
- result totals and last-page information;
- a `rowKey` and row-oriented `data.rows`.

Columns contain definitions only. Each row cell is a payload with `value` plus optional `icon`, `variant`, and `meta`. Capabilities constrain which state transitions are valid.

Selection and column visibility are local state, not server query state.

`data.rowKey` must name a declared Column even when `rows` is empty. Each row must contain that cell, and its value must be a string or JavaScript-safe integer. Laravel transports larger integer identities as exact decimal strings so JavaScript does not silently change them.

## Filter definitions

A filter is a field. Its clauses are the comparisons offered for it, and each clause’s input describes the value it takes, so a renderer draws and checks a value from the clause alone.

| Filter field    | Required            | Meaning                                                           |
| --------------- | ------------------- | ----------------------------------------------------------------- |
| `key`           | Yes                 | The filter’s ID in state, URLs, and links                         |
| `label`         | No                  | Shown in the field picker and chips; renderers fall back to `key` |
| `clauses`       | Yes, at least one   | The clauses offered for the field, in order, with unique keys     |
| `defaultClause` | Yes                 | The key of one of `clauses`, picked when the field is added       |
| `pinned`        | No, default `false` | Shown as a pinned control unless the page gives its own list      |
| `meta`          | No                  | Application data                                                  |

| Clause field | Required | Meaning                                               |
| ------------ | -------- | ----------------------------------------------------- |
| `key`        | Yes      | A built-in clause key or a `vendor/name` identifier   |
| `label`      | No       | Shown for the comparison                              |
| `input`      | No       | The value the clause takes; absent when it takes none |
| `meta`       | No       | Application data                                      |

| Input field        | Required                                | Meaning                                                                             |
| ------------------ | --------------------------------------- | ----------------------------------------------------------------------------------- |
| `valueType`        | Yes                                     | `text`, `number`, `boolean`, `select`, `date`, `datetime`, `time`, or `vendor/name` |
| `mode`             | Yes                                     | `single`, `multiple`, or `range`                                                    |
| `options`          | Yes for `select`; not allowed otherwise | `{ value, label }` entries whose scalar values are unique                           |
| `optionsTruncated` | No; `select` only                       | `true` when the producer stopped listing options at its limit                       |

Every value type allows `single`. Only `select` allows `multiple`, and `range` is allowed for `number`, `date`, `datetime`, `time`, and custom value types.

```json
{
  "key": "created_at",
  "label": "Created",
  "defaultClause": "between",
  "pinned": false,
  "clauses": [
    { "key": "between", "label": "Between", "input": { "valueType": "date", "mode": "range" } },
    { "key": "acme/last-days", "label": "In the last … days", "input": { "valueType": "number", "mode": "single" } },
    { "key": "is-not-set", "label": "Is empty" }
  ]
}
```

Each condition `{ key, clause, value }` in the filter state carries a value that matches its clause’s input:

- no input: `null`;
- `single`: one value of the value type;
- `multiple`: a non-empty list of values of the value type;
- `range`: `[from, to]`, both of the value type.

`text`, `date`, `datetime`, and `time` values are strings, `number` values finite numbers, `boolean` values Booleans, and `select` values strings, numbers, or Booleans. A custom value type accepts any JSON value except `null`. `null` is never a value.

## Table problem shape

`TableEnvelope` is the union of successful `Envelope` and `ProblemEnvelope`. A problem component contains only `type`, `id`, and `problem`:

```json
{
  "type": "table",
  "id": "orders",
  "problem": {
    "issues": [{ "field": "page", "code": "page-unavailable" }],
    "resettable": true
  }
}
```

Each issue names one unique field: `page`, `pageSize`, `sorting`, `filters`, or `search`. Codes are `invalid-url-state` or `page-unavailable` (page only). `resettable` records whether the producer permits a reset; the integration must also support it. Problem components have no rows, result metadata, or successful state. Use `isProblemEnvelope()` to narrow a decoded envelope before accessing result fields. The shipped `fixtures/valid/table-problem.json` is a complete example.

## Validation boundary

Consumers decode unknown input before creating a runtime session. Producers validate the complete envelope before returning it. A malformed known field reports schema-path context; an unsupported protocol version fails before a transformation or render.

Use `decodeTableEnvelope` when an application or integration receives unknown protocol data directly:

```ts
import { decodeTableEnvelope } from '@inertiax/table-protocol';

export function decodeInput(input: unknown) {
  return decodeTableEnvelope(input);
}
```

`decodeTableEnvelope` returns a deeply immutable clone. It throws `ProtocolDecodeError` with:

- `INERTIAX_PROTOCOL_VERSION_UNSUPPORTED` when the received protocol version is not supported;
- `INERTIAX_PROTOCOL_DECODE_FAILED` for invalid JSON-domain values, malformed known fields, or invalid Table semantics.

The error includes `path`, structured `diagnostics`, the supported version, and available received version/component context. Catch it only when the application can add useful context or recovery; do not continue with the rejected input.

## Compatibility rules

- Protocol versions are independent from npm and Composer package versions. A decoder-only patch can preserve the same wire contract and Laravel snapshot.
- Unsupported versions fail before transformation or rendering.
- Known malformed fields fail with schema-path diagnostics.
- Custom column types, value types, and clause keys must be namespaced as `vendor/name`.
- Unknown custom leaf identifiers remain valid protocol data, but the React renderer needs a matching registration or uses its configured diagnosed fallback.

A package patch or minor release does not imply a protocol change. Conversely, protocol evolution may require coordinated compatible package releases while their version numbers remain different.

## Integration tooling

Use the exported TypeScript declarations and runtime decoder; do not copy protocol types into application code. Validate unknown input before creating a session. See [Direct envelope](https://docs.inertiax.dev/table/guide/direct-rendering/) for custom integrations and their ownership contracts.
