@dudousxd/nestjs-telescope
Version:
Laravel Telescope-style observability console for NestJS — core: watchers, recorder, correlation, SQLite store, headless API.
87 lines • 3.81 kB
TypeScript
/**
* The wire contract for a `table` panel's sort/filter state, and the helper a
* provider uses to read it back.
*
* Sorting and filtering a dashboard table are done **server-side**: the UI never
* holds more than the page it is showing, so sorting in the browser would sort
* 50 rows out of 50,000 and quietly present the result as "the top of the list".
* The state therefore travels to the provider the same way `page`/`limit`
* already do — merged into the panel's `data.query` and serialized as query
* params on `GET /telescope/api/ext/<ext>/data/<provider>`.
*
* Everything here is **additive**: a provider that never reads these params
* behaves exactly as it did before they existed, and a panel whose columns
* declare no `sortable`/`filterable` flag never sends them.
*
* The wire shape, flat because the query object is serialized through
* `URLSearchParams` (a nested object would arrive as the string
* `"[object Object]"`):
*
* ```text
* ?page=2&limit=50&sort=duration&dir=desc&filter.status=failed
* ```
*
* Note that params arrive as **strings** — the host controller passes
* `@Query()` through verbatim — which is exactly the trap {@link readTableQuery}
* exists to close: `query.page > 1` is `false` for the string `'2'`, silently,
* with no type error to catch it.
*/
/** Query-param key naming the column the table is sorted by. */
export declare const TABLE_SORT_PARAM = "sort";
/** Query-param key carrying the sort direction (`'asc'` / `'desc'`). */
export declare const TABLE_SORT_DIR_PARAM = "dir";
/**
* Prefix that namespaces a per-column filter — `filter.status=failed` filters
* the column whose `key` is `status`.
*
* Prefixed rather than flat so a filterable column can never collide with a
* param the panel itself declared in `data.query` (a panel with a static
* `{ status: 'running' }` scope AND a filterable `status` column is an ordinary
* combination, and one silently overwriting the other would be a bug nobody
* could see from the outside).
*/
export declare const TABLE_FILTER_PREFIX = "filter.";
/** Which column a table is sorted by, and which way. */
export interface TableSort {
/** The `Column.key` of the sorted column. */
key: string;
dir: 'asc' | 'desc';
}
/** A table panel's paging + sort + filter request, normalized off the raw query. */
export interface TableQuery {
/** 1-based page, present only for a `paged: true` panel. */
page?: number;
/** Requested page size, present only for a `paged: true` panel. */
limit?: number;
/** Absent when the table is unsorted (the provider's own default order wins). */
sort?: TableSort;
/** Per-column filter text, keyed by `Column.key`. Empty when nothing is filtered. */
filters: Record<string, string>;
}
/**
* Normalizes the sort/filter/paging params out of a provider's raw `query`.
*
* Written as a guard-per-field rather than a cast: `query` is genuinely
* untrusted — it is whatever was in the URL — and every field has a safe absent
* behaviour, so a hand-typed `?sort=` or `?page=banana` degrades to "unsorted" /
* "first page" instead of reaching a SQL builder as `NaN`.
*
* @example
* ```ts
* const provider: DataProvider = {
* name: 'durable.runs',
* async resolve(query) {
* const { page = 1, limit = 50, sort, filters } = readTableQuery(query);
* const rows = await store.findRuns({
* offset: (page - 1) * limit,
* limit,
* orderBy: sort ? { [sort.key]: sort.dir } : { startedAt: 'desc' },
* where: filters,
* });
* return { rows: rows.items, total: rows.total, page, limit };
* },
* };
* ```
*/
export declare function readTableQuery(query: Record<string, unknown> | undefined): TableQuery;
//# sourceMappingURL=table-query.d.ts.map