src/types/schema.ts had drifted from the official schema contract: a `clientOnly` field had been added to `FilterEnum` to support client-side log filtering. Restored it to match upstream exactly and moved the deviation-only type into a new intersection type in src/lib/schemaDeviationTypes.ts instead. Audited the codebase for every place the UI does something the official schema doesn't support and tagged each with `// SCHEMA-DEVIATION: <id>`, documented in the new SCHEMA_DEVIATIONS.md registry (what, why, and the ideal server-side fix): - log-client-filters: Level/Event filters on Log Entries, applied client-side because x:Log/query rejects them as JMAP filters. - account-quota-usage-column: synthetic quotaUsage column on the Accounts list. - mailbox-client-hierarchy-sort: full-fetch + client-side sort to reconstruct mailbox parent/child hierarchy. - webapp-enabled-column-fallback: synthetic "Enabled" column label for x:Application when the schema list doesn't define one. Other viewName/objectName special cases (x:OtpAuth, x:Expression, x:Rate, x:Action, x:Trace, CustomComponent/*) were checked against upstream and are pre-existing architecture, not fork deviations.
85 lines
6.2 KiB
Markdown
85 lines
6.2 KiB
Markdown
# Schema deviations
|
|
|
|
Stalwart WebUI is schema-driven: the server's JSON schema is the single
|
|
source of truth for what forms, fields, filters, and columns exist. This
|
|
fork tries to stay aligned with that philosophy (see
|
|
[stalwartlabs/webui#discussion](https://github.com/stalwartlabs/webui) and
|
|
the maintainer's note on why exceptions belong server-side, not in the UI).
|
|
|
|
Everything in this file is a deliberate exception: a place where the UI
|
|
does something the schema doesn't (yet) describe, because the equivalent
|
|
server-side capability doesn't exist in [stalwartlabs/stalwart](https://github.com/stalwartlabs/stalwart)
|
|
or [stalwartlabs/webui](https://github.com/stalwartlabs/webui). Each entry
|
|
is tagged in code with `// SCHEMA-DEVIATION: <id>` so they're greppable
|
|
(`grep -rn "SCHEMA-DEVIATION" src/`).
|
|
|
|
The goal is **not** to remove these — they're real functionality this fork
|
|
wants to keep — but to track them separately from schema-driven code, so
|
|
it's always clear which is which, and so each one can be dropped the day
|
|
the server (or upstream webui) grows the equivalent native capability.
|
|
|
|
## `src/types/schema.ts` is never touched for a deviation
|
|
|
|
`src/types/schema.ts` mirrors the server's schema contract exactly and
|
|
must stay aligned with the official webui/server types — it is **never**
|
|
edited to accommodate a deviation, even to add an extra optional field.
|
|
|
|
If a deviation needs to carry extra data on an otherwise-official schema
|
|
shape (e.g. a flag consumed only by the deviation's own code), the
|
|
augmented type lives in the deviation's own module or in
|
|
[`src/lib/schemaDeviationTypes.ts`](src/lib/schemaDeviationTypes.ts), as
|
|
an intersection with the official type (`OfficialType & { extra?: ... }`),
|
|
and is imported only where the deviation is actually used. `schema.ts`
|
|
itself stays byte-for-byte alignable with upstream's version of the file.
|
|
|
|
## Status legend
|
|
|
|
- 🟡 **Workaround** — client-only, would be removed if the server supported it natively.
|
|
- 🔵 **Upstream tracked** — an issue has been filed upstream; link included.
|
|
|
|
## Deviations
|
|
|
|
### `log-client-filters` 🟡
|
|
|
|
- **Where**: [`src/lib/logFilters.ts`](src/lib/logFilters.ts), type augmentation in [`src/lib/schemaDeviationTypes.ts`](src/lib/schemaDeviationTypes.ts)
|
|
- **What**: injects `level` and `event` as filterable columns on the `x:Log` list, applied entirely client-side (`clientOnly` flag consumed by `DynamicList`). The `clientOnly` flag is declared as `ClientOnlyFilterEnum` (an intersection type), not on the official `FilterEnum` in `schema.ts`.
|
|
- **Why**: Stalwart's JMAP `x:Log/query` returns `unsupportedFilter` for both properties today, even though they're returned per row.
|
|
- **Ideal fix**: `stalwartlabs/stalwart` accepts `level`/`event` as real query filters; the schema then advertises them normally and `logFilters.ts` + the `ClientOnlyFilterEnum` augmentation are deleted.
|
|
|
|
### `account-quota-usage-column` 🟡
|
|
|
|
- **Where**: [`src/lib/accountColumns.ts`](src/lib/accountColumns.ts)
|
|
- **What**: adds a synthetic `quotaUsage` column to the `x:Account/User` list that isn't a real schema property — `DynamicList` resolves it from the `usedDiskQuota` + `quotas.maxDiskQuota` pair and formats it specially. Also re-adds `roles` (a real property, just not in the list's default columns).
|
|
- **Why**: the Accounts list schema doesn't expose usage/role as list columns, only as detail-view fields.
|
|
- **Ideal fix**: the server's `x:Account/User` list schema includes `roles` and a computed usage/quota column natively; this file is deleted.
|
|
|
|
### `mailbox-client-hierarchy-sort` 🟡
|
|
|
|
- **Where**: [`src/components/lists/DynamicList.tsx`](src/components/lists/DynamicList.tsx) — `sortMailboxesByHierarchy` and the `isMailboxList` branch
|
|
- **What**: for the `Mailbox` list, fetches the *entire* result set (bypassing normal server pagination) and sorts it client-side so each parent mailbox is immediately followed by its children, with indentation depth tracked in React state.
|
|
- **Why**: the server returns mailboxes in whatever order the query produces, not grouped by parent/child, and a mailbox's parent can land on a different page than the mailbox itself, so hierarchy can't be reconstructed one page at a time.
|
|
- **Ideal fix**: the server offers a native tree/hierarchical ordering (or a `sort` that groups by ancestry) for `Mailbox/query`; the client-side full-fetch-and-sort is deleted in favor of normal paginated queries.
|
|
|
|
### `webapp-enabled-column-fallback` 🟡
|
|
|
|
- **Where**: [`src/components/lists/DynamicList.tsx`](src/components/lists/DynamicList.tsx) — `displayColumns` in the `isWebApplications` branch
|
|
- **What**: reordering the `x:Application` list's real schema columns (Description first, Enabled second) is not a deviation, but if the schema's `list.columns` doesn't include an `enabled` column at all, a fallback column definition with a hardcoded label is fabricated client-side so the toggle still renders.
|
|
- **Why**: the `x:Application` list schema is not guaranteed to expose `enabled` as a list column, even though it's a real object property (fetched separately via `properties.push('enabled')`).
|
|
- **Ideal fix**: the server's `x:Application` list schema always includes `enabled` as a real column; the fallback branch is deleted (only the reordering logic remains, which is not a deviation).
|
|
|
|
## Not a deviation (for reference)
|
|
|
|
A few other `viewName === '...'` / `objectName === '...'` checks exist in
|
|
`DynamicList.tsx`, `MainContent.tsx`, `Sidebar.tsx`, `layout.ts`, and
|
|
`FieldWidget.tsx` (e.g. `x:OtpAuth`, `x:Expression`, `x:Rate`, `x:Action`,
|
|
`x:Trace`, `CustomComponent/Dashboard` and other `CustomComponent/*`
|
|
pages, the `x:Application` column reordering itself, the active-WebApp
|
|
info card). These are **not** tracked here: they render real schema data
|
|
with a custom widget or extra display, the same pattern already used
|
|
upstream for special object types — they don't fabricate data or bypass
|
|
the server's filtering/pagination. Verified against `upstream/main` for
|
|
each: the object/view names above already drive special-cased rendering
|
|
there too, except `x:Application`/`Mailbox`/`x:Log`/`x:Account/User`
|
|
which are fork-only and covered by the entries above (or explicitly
|
|
noted as presentation-only, e.g. the Web Applications column reorder).
|