@nextoolsolutions/mcp-glpi-core 0.0.0-stage → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +88 -0
- package/LICENSE +21 -0
- package/README.md +70 -2
- package/dist/errors.d.ts +28 -0
- package/dist/errors.js +40 -0
- package/dist/format.d.ts +76 -0
- package/dist/format.js +398 -0
- package/dist/http.d.ts +76 -0
- package/dist/http.js +177 -0
- package/dist/idempotency.d.ts +35 -0
- package/dist/idempotency.js +92 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +21 -0
- package/dist/pagination.d.ts +74 -0
- package/dist/pagination.js +125 -0
- package/dist/policy.d.ts +59 -0
- package/dist/policy.js +134 -0
- package/dist/result.d.ts +41 -0
- package/dist/result.js +66 -0
- package/dist/server-format.d.ts +94 -0
- package/dist/server-format.js +298 -0
- package/dist/server-idempotency.d.ts +30 -0
- package/dist/server-idempotency.js +71 -0
- package/dist/server-policy.d.ts +50 -0
- package/dist/server-policy.js +73 -0
- package/package.json +49 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Changelog — @nextoolsolutions/mcp-glpi-core
|
|
2
|
+
|
|
3
|
+
Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versions follow semver.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [1.3.1] - 2026-10-04
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- **Raw HTML in markdown tables for nested richtext.** `pickFields` flattens richtext only at the top
|
|
11
|
+
level, so the API v2 timeline (`{type, item: {content: "<p>…</p>"}}`) reached the markdown summary
|
|
12
|
+
as `<p>Olá <strong></strong>…`. In `format: "markdown"` (default `fields: "essential"`) the payload
|
|
13
|
+
now goes through `flattenRichtext` before the view and the table: richtext fields are turned into
|
|
14
|
+
text at any depth (up to 3 levels). The JSON result and `fields: "all"` keep the HTML as GLPI sent it.
|
|
15
|
+
- Table cells turn a run of line breaks into one space (a flattened paragraph left double spaces).
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- `flattenRichtext(value, maxDepth = 3)` and `MARKDOWN_RICHTEXT_FIELDS` (`RICHTEXT_FIELDS` plus the
|
|
19
|
+
validation comments `comment_submission`, `comment_validation`, `submission_comment`,
|
|
20
|
+
`approval_comment`, which GLPI 11 stores as HTML).
|
|
21
|
+
|
|
22
|
+
## [1.3.0] - 2026-10-04
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- `markdownViews` option of `installPayloadFormatting` and `columnView` / `MarkdownColumn` /
|
|
26
|
+
`MarkdownView`: per-tool column projection for listings in `format: "markdown"` (only with the
|
|
27
|
+
default `fields: "essential"`; the JSON result is never projected). The size budget measures the
|
|
28
|
+
projected table, so more rows fit.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
- `ESSENTIAL_FIELDS.Ticket` keeps `requesters` and `assigned` (the `{id, name}` lists
|
|
32
|
+
`mcp-glpi` 3.5.0 adds to ticket listings).
|
|
33
|
+
|
|
34
|
+
## [1.2.0] - 2026-10-04
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
- Size budget for every read tool (`installPayloadFormatting`): texts longer than
|
|
38
|
+
`LIST_TEXT_MAX_CHARS` (300, `GLPI_LIST_TEXT_MAX_CHARS`) are cut in listings (except the ticket
|
|
39
|
+
history listings in `FULL_TEXT_LISTS`, and with `fields: "all"`); an answer longer than
|
|
40
|
+
`MAX_RESPONSE_CHARS` (50000, `GLPI_MAX_RESPONSE_CHARS`) is trimmed from the end of the list.
|
|
41
|
+
- `nextPageNote`: the pagination note names only the parameters the tool has (`range`, or
|
|
42
|
+
`start`/`limit`) with the values of the next page, and uses `total` when the result carries it.
|
|
43
|
+
- `cutLongTexts`, `FULL_TEXT_LISTS`.
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
- **`format: "markdown"` is carried in `structuredContent` too** (`{ data: "<markdown>",
|
|
47
|
+
format: "markdown", count?, note? }`). Clients that support structured results hand that to
|
|
48
|
+
the model, so a markdown text block alone was ignored. Sibling keys of `data` (e.g. `total`)
|
|
49
|
+
are kept in the rendering. A single item renders as `key: value` lines without cutting texts;
|
|
50
|
+
`{id, name}` references render as "name (id)".
|
|
51
|
+
- Results whose payload is the structured object itself (API v2 single items) are formatted
|
|
52
|
+
too; before, `fields` and `format` were silently ignored on them.
|
|
53
|
+
- `TOOL_ITEMTYPES` holds API v1 tools only: v2 payloads use other field names and the v1
|
|
54
|
+
whitelists stripped their relations (`entity`, `category`, `team`). v2 tools use the generic
|
|
55
|
+
blocklist, which now also drops `*_duration` and `internal_*`.
|
|
56
|
+
- Whitelists keep keys ending in `_name` (names resolved by the server) and numeric keys (search
|
|
57
|
+
columns). `glpi_search` is no longer a generic-itemtype tool.
|
|
58
|
+
- `MAX_PAGE_SIZE` default 200 -> 100. `paginationNote` no longer names a parameter.
|
|
59
|
+
- `errorResult` has no `structuredContent`: the MCP SDK client validates it against the output
|
|
60
|
+
schema even on errors, which replaced every GLPI error message with a schema error.
|
|
61
|
+
- `sanitizeId` refuses an empty ID (it built the collection URL).
|
|
62
|
+
|
|
63
|
+
## [1.1.0] - 2026-10-04
|
|
64
|
+
|
|
65
|
+
### Added
|
|
66
|
+
- `FetchImpl` type and an optional `fetchImpl` argument on `fetchFn` / `fetchWithTimeout`:
|
|
67
|
+
callers inject their own `fetch` (e.g. one that validates the resolved IP).
|
|
68
|
+
- `GlpiRedirectError` (extends `GlpiHttpError`, with `targetHost`) and `assertNotRedirect`.
|
|
69
|
+
- `titleFromToolName` and the `ToolAnnotationHints` type.
|
|
70
|
+
|
|
71
|
+
### Changed
|
|
72
|
+
- **Redirects are never followed.** Every request is sent with `redirect: "manual"`; a 3xx (or an
|
|
73
|
+
opaque redirect) throws `redirect not followed: <status> -> <host>`. Only the host of the
|
|
74
|
+
Location is reported, never its path or query. The Node polyfill now exposes `location`.
|
|
75
|
+
- **Annotations.** `annotationsFor(kind, toolName?)`: `openWorldHint` is now `false`;
|
|
76
|
+
`idempotentHint` is `true` for reads, destructive tools and `update_*` / `set_*` writes.
|
|
77
|
+
`destructiveHint` is `true` for deletes and for every write that is not additive
|
|
78
|
+
(`update_*`, `set_*`, `change_*`, `retry_*`): the MCP spec defines `false` as "only additive
|
|
79
|
+
updates", so only `create_*` / `add_*` stay non-destructive.
|
|
80
|
+
`installWritePolicy` also sets `annotations.title` (the tool's `title`, or one derived from
|
|
81
|
+
its name).
|
|
82
|
+
- LICENSE (MIT, NexTool Solutions) and CHANGELOG ship in the package.
|
|
83
|
+
|
|
84
|
+
## [1.0.0] - 2026-08-31
|
|
85
|
+
|
|
86
|
+
### Added
|
|
87
|
+
- Shared HTTP transport with retry/timeout, typed errors, tool-result helpers, write policy,
|
|
88
|
+
payload formatting, pagination and create idempotency for the GLPI MCP servers.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 NexTool Solutions
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,71 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @nextoolsolutions/mcp-glpi-core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Shared infrastructure for NexTool MCP for GLPI (`@nextoolsolutions/mcp-glpi`, API v1 and v2
|
|
4
|
+
families). It started as the byte-identical code the old `mcp-glpi` and `mcp-glpi-v2` servers
|
|
5
|
+
both carried.
|
|
6
|
+
|
|
7
|
+
> Not affiliated with Teclib'. GLPI is a registered trademark of Teclib'.
|
|
8
|
+
|
|
9
|
+
| Module | What it holds |
|
|
10
|
+
|--------|---------------|
|
|
11
|
+
| `http.ts` | Injectable fetch (`FetchImpl`), polyfill, timeout, redirect refusal, retry/backoff helpers, URL helpers, ID sanitisation |
|
|
12
|
+
| `errors.ts` | `GlpiHttpError` base class, `GlpiRedirectError`, `GlpiPolicyError` |
|
|
13
|
+
| `result.ts` | `toolResult` / `jsonResult` / `errorResult` / `makeWrap` |
|
|
14
|
+
| `policy.ts` | Write policy: read-only mode, delete gating, tool classification by name |
|
|
15
|
+
| `server-policy.ts` | Installs the policy and MCP annotations (title, read-only, destructive, idempotent, closed world) by wrapping `registerTool` |
|
|
16
|
+
| `format.ts` | HTML stripping, per-itemtype field whitelists (API v1), markdown rendering |
|
|
17
|
+
| `pagination.ts` | Range/limit defaults and ceiling, size budget constants, next-page notes |
|
|
18
|
+
| `server-format.ts` | Installs payload formatting, pagination and the size budget on read tools; markdown goes in the text and in `structuredContent` |
|
|
19
|
+
| `idempotency.ts` | Create idempotency key and store |
|
|
20
|
+
| `server-idempotency.ts` | Installs the create guard, including in-flight collapsing |
|
|
21
|
+
|
|
22
|
+
## Design note
|
|
23
|
+
|
|
24
|
+
The three `install*` functions wrap `server.registerTool` once, right after the server is
|
|
25
|
+
constructed, instead of editing 144 call sites. Every tool registered afterwards — present or
|
|
26
|
+
future — is covered, and misclassification fails the test suite rather than leaking a write.
|
|
27
|
+
They compose in any order.
|
|
28
|
+
|
|
29
|
+
## Transport guarantees
|
|
30
|
+
|
|
31
|
+
- `fetchFn(url, init, fetchImpl?)` / `fetchWithTimeout(...)` use the injected `fetchImpl`, else
|
|
32
|
+
the global `fetch`, else a Node `http`/`https` polyfill.
|
|
33
|
+
- Requests always go out with `redirect: "manual"`. A 3xx throws `GlpiRedirectError`
|
|
34
|
+
(`redirect not followed: <status> -> <host>`); the Location path and query never reach the
|
|
35
|
+
message. Clients treat it as final (no retry).
|
|
36
|
+
|
|
37
|
+
## Read tool output (1.2.0)
|
|
38
|
+
|
|
39
|
+
- `format: "markdown"` is returned in the text block and in `structuredContent`
|
|
40
|
+
(`{ data: "<markdown>", format: "markdown", count, note }`), for wrapped (`{ data }`) and
|
|
41
|
+
unwrapped (API v2 item) payloads alike.
|
|
42
|
+
- Listings: at most `GLPI_MAX_PAGE_SIZE` (100) items, texts cut at `GLPI_LIST_TEXT_MAX_CHARS`
|
|
43
|
+
(300) except in `FULL_TEXT_LISTS`, answer trimmed to `GLPI_MAX_RESPONSE_CHARS` (50000). The
|
|
44
|
+
`note` names only the tool's own pagination parameters.
|
|
45
|
+
- `errorResult` carries no `structuredContent`, so validating clients show the error message.
|
|
46
|
+
- Markdown views (1.3.0): `installPayloadFormatting({ markdownViews })` maps a tool name to a row
|
|
47
|
+
projection (`columnView([{ label, from }])`, `from` = a key, candidate keys or a function). In
|
|
48
|
+
`format: "markdown"` with `fields: "essential"` the listing table shows only those columns; the
|
|
49
|
+
JSON result and `fields: "all"` keep every field. The `Ticket` whitelist keeps `requesters` and
|
|
50
|
+
`assigned` (people resolved by the server).
|
|
51
|
+
- Nested richtext in markdown (1.3.1): `flattenRichtext` turns HTML into text in richtext fields at
|
|
52
|
+
any depth (the API v2 timeline's `item.content`, validation comments) before a markdown table or
|
|
53
|
+
item is drawn; the JSON result and `fields: "all"` keep the HTML.
|
|
54
|
+
|
|
55
|
+
## Consuming it
|
|
56
|
+
|
|
57
|
+
`@nextoolsolutions/mcp-glpi` declares it as `^1.3.1`. Inside the repo its lockfile links the
|
|
58
|
+
sibling folder, so build here first — `tsx` does not transpile TypeScript inside
|
|
59
|
+
`node_modules`, the server loads the compiled `dist/`:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
cd mcp-glpi-core && npm ci && npm run build # after every change
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Publish this package before any `mcp-glpi` release that needs a new core version.
|
|
66
|
+
|
|
67
|
+
## Tests
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm test # node --test via tsx, no extra dependency
|
|
71
|
+
```
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared error types for the GLPI MCP servers.
|
|
3
|
+
*
|
|
4
|
+
* Both mcp-glpi (REST v1) and mcp-glpi-v2 (API v2 / OAuth2) used to declare an
|
|
5
|
+
* identical error class. They now extend this base so callers can catch a
|
|
6
|
+
* single type while each server keeps its own `name` for readable messages.
|
|
7
|
+
*/
|
|
8
|
+
export declare class GlpiHttpError extends Error {
|
|
9
|
+
readonly status: number;
|
|
10
|
+
readonly method: string;
|
|
11
|
+
readonly path: string;
|
|
12
|
+
constructor(message: string, status: number, method: string, path: string, name?: string);
|
|
13
|
+
}
|
|
14
|
+
/** Raised when the write policy blocks an operation before it reaches GLPI. */
|
|
15
|
+
export declare class GlpiPolicyError extends Error {
|
|
16
|
+
constructor(message: string);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Raised when GLPI answers a request with a 3xx. Requests go out with
|
|
20
|
+
* `redirect: "manual"`, so a redirect is never followed: following one would
|
|
21
|
+
* send the session/OAuth headers to whatever host the Location names. Only the
|
|
22
|
+
* target host is kept in the message — the Location path and query may carry
|
|
23
|
+
* tokens.
|
|
24
|
+
*/
|
|
25
|
+
export declare class GlpiRedirectError extends GlpiHttpError {
|
|
26
|
+
readonly targetHost: string;
|
|
27
|
+
constructor(status: number, method: string, path: string, targetHost: string);
|
|
28
|
+
}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared error types for the GLPI MCP servers.
|
|
3
|
+
*
|
|
4
|
+
* Both mcp-glpi (REST v1) and mcp-glpi-v2 (API v2 / OAuth2) used to declare an
|
|
5
|
+
* identical error class. They now extend this base so callers can catch a
|
|
6
|
+
* single type while each server keeps its own `name` for readable messages.
|
|
7
|
+
*/
|
|
8
|
+
export class GlpiHttpError extends Error {
|
|
9
|
+
status;
|
|
10
|
+
method;
|
|
11
|
+
path;
|
|
12
|
+
constructor(message, status, method, path, name = "GlpiHttpError") {
|
|
13
|
+
super(message);
|
|
14
|
+
this.status = status;
|
|
15
|
+
this.method = method;
|
|
16
|
+
this.path = path;
|
|
17
|
+
this.name = name;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** Raised when the write policy blocks an operation before it reaches GLPI. */
|
|
21
|
+
export class GlpiPolicyError extends Error {
|
|
22
|
+
constructor(message) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.name = "GlpiPolicyError";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Raised when GLPI answers a request with a 3xx. Requests go out with
|
|
29
|
+
* `redirect: "manual"`, so a redirect is never followed: following one would
|
|
30
|
+
* send the session/OAuth headers to whatever host the Location names. Only the
|
|
31
|
+
* target host is kept in the message — the Location path and query may carry
|
|
32
|
+
* tokens.
|
|
33
|
+
*/
|
|
34
|
+
export class GlpiRedirectError extends GlpiHttpError {
|
|
35
|
+
targetHost;
|
|
36
|
+
constructor(status, method, path, targetHost) {
|
|
37
|
+
super(`redirect not followed: ${status} -> ${targetHost}`, status, method, path, "GlpiRedirectError");
|
|
38
|
+
this.targetHost = targetHost;
|
|
39
|
+
}
|
|
40
|
+
}
|
package/dist/format.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payload formatting for the GLPI MCP servers.
|
|
3
|
+
*
|
|
4
|
+
* GLPI returns everything it has: a single ticket carries 45 fields, roughly
|
|
5
|
+
* twenty of which are SLA/OLA bookkeeping and delay statistics no agent ever
|
|
6
|
+
* reads, and richtext fields arrive as double-escaped TinyMCE HTML
|
|
7
|
+
* (`<div class="elementToProof">Teste</div>`). Returning that raw
|
|
8
|
+
* burns context on every call.
|
|
9
|
+
*
|
|
10
|
+
* `essential` mode keeps the fields that describe the item; `all` returns the
|
|
11
|
+
* untouched payload for the cases where a rare field is genuinely needed.
|
|
12
|
+
*/
|
|
13
|
+
export type FieldMode = "essential" | "all";
|
|
14
|
+
export type OutputFormat = "json" | "markdown";
|
|
15
|
+
/**
|
|
16
|
+
* Turns a TinyMCE richtext field into plain text.
|
|
17
|
+
*
|
|
18
|
+
* Entities are decoded twice on purpose: GLPI stores the markup escaped, so a
|
|
19
|
+
* first pass turns `<div>` into real tags that the tag stripper can
|
|
20
|
+
* remove, and a second pass decodes the entities that were inside the text.
|
|
21
|
+
*/
|
|
22
|
+
export declare function stripHtml(value: string): string;
|
|
23
|
+
/** Fields whose content is richtext and should be flattened to plain text. */
|
|
24
|
+
export declare const RICHTEXT_FIELDS: Set<string>;
|
|
25
|
+
/**
|
|
26
|
+
* Richtext fields flattened for markdown: RICHTEXT_FIELDS plus the validation
|
|
27
|
+
* comments (v1 comment_submission / comment_validation, v2 submission_comment /
|
|
28
|
+
* approval_comment), which GLPI 11 also stores as HTML.
|
|
29
|
+
*/
|
|
30
|
+
export declare const MARKDOWN_RICHTEXT_FIELDS: Set<string>;
|
|
31
|
+
/**
|
|
32
|
+
* Flattens richtext fields to plain text at any depth up to `maxDepth` nested
|
|
33
|
+
* objects/arrays. `pickFields` only flattens the top level, so the API v2
|
|
34
|
+
* timeline (`{type, item: {content: "<p>…</p>"}}`) reached the markdown table
|
|
35
|
+
* as raw HTML. Used for markdown only: the JSON result keeps nested fields as
|
|
36
|
+
* GLPI sent them. Returns a copy; the input is not modified.
|
|
37
|
+
*/
|
|
38
|
+
export declare function flattenRichtext(value: unknown, maxDepth?: number): unknown;
|
|
39
|
+
/**
|
|
40
|
+
* Per-itemtype whitelists. Derived from real payloads: what is dropped for
|
|
41
|
+
* Ticket is the SLA/OLA block, the *_delay_stat counters and the HAL `links`.
|
|
42
|
+
*/
|
|
43
|
+
export declare const ESSENTIAL_FIELDS: Record<string, string[]>;
|
|
44
|
+
export declare function pickFields(itemtype: string | undefined, obj: Record<string, unknown>, mode: FieldMode): Record<string, unknown>;
|
|
45
|
+
/**
|
|
46
|
+
* Formats a whole payload. Objects and arrays of objects are filtered;
|
|
47
|
+
* anything else is returned untouched.
|
|
48
|
+
*/
|
|
49
|
+
export declare function formatPayload(itemtype: string | undefined, data: unknown, mode: FieldMode): unknown;
|
|
50
|
+
/**
|
|
51
|
+
* Renders rows as a markdown table. Columns come from the union of the keys
|
|
52
|
+
* present, in first-seen order, so a sparse row does not lose its data.
|
|
53
|
+
* Long cells are cut at 80 characters: a table is for scanning a listing;
|
|
54
|
+
* open the item (or ask for JSON) to read a long text in full.
|
|
55
|
+
*/
|
|
56
|
+
export declare function toMarkdownTable(rows: Record<string, unknown>[]): string;
|
|
57
|
+
/**
|
|
58
|
+
* One column of a conversational markdown view: `label` heads the column and
|
|
59
|
+
* `from` says where the value comes from — a key, a list of candidate keys
|
|
60
|
+
* (the first non-empty one wins) or a function of the row.
|
|
61
|
+
*/
|
|
62
|
+
export interface MarkdownColumn {
|
|
63
|
+
label: string;
|
|
64
|
+
from: string | readonly string[] | ((row: Record<string, unknown>) => unknown);
|
|
65
|
+
}
|
|
66
|
+
/** Projects a full row onto the columns of a view. */
|
|
67
|
+
export type MarkdownView = (row: Record<string, unknown>) => Record<string, unknown>;
|
|
68
|
+
/**
|
|
69
|
+
* Builds a view from a column list. A listing in markdown is read in a
|
|
70
|
+
* conversation: the 22 columns of a raw ticket row (bare IDs such as
|
|
71
|
+
* `users_id_lastupdater: 368` among them) bury the 8 a person scans for.
|
|
72
|
+
* The JSON result keeps every field; only the markdown table is projected.
|
|
73
|
+
*/
|
|
74
|
+
export declare function columnView(columns: readonly MarkdownColumn[]): MarkdownView;
|
|
75
|
+
/** Renders a payload as markdown; falls back to JSON for non-tabular shapes. */
|
|
76
|
+
export declare function renderMarkdown(data: unknown): string;
|