@wootsup/mcp 0.1.0-rc.8 → 0.1.0
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/README.md +7 -7
- package/dist/index.d.ts +19 -0
- package/dist/index.js +72 -6
- package/dist/index.js.map +1 -1
- package/dist/modules/apimapper/cache.d.ts +2 -2
- package/dist/modules/apimapper/cache.js +107 -25
- package/dist/modules/apimapper/cache.js.map +1 -1
- package/dist/modules/apimapper/client.d.ts +40 -0
- package/dist/modules/apimapper/client.js +82 -12
- package/dist/modules/apimapper/client.js.map +1 -1
- package/dist/modules/apimapper/connections-format.d.ts +51 -0
- package/dist/modules/apimapper/connections-format.js +261 -0
- package/dist/modules/apimapper/connections-format.js.map +1 -0
- package/dist/modules/apimapper/connections-trim.d.ts +82 -0
- package/dist/modules/apimapper/connections-trim.js +224 -0
- package/dist/modules/apimapper/connections-trim.js.map +1 -0
- package/dist/modules/apimapper/connections.d.ts +14 -2
- package/dist/modules/apimapper/connections.js +447 -143
- package/dist/modules/apimapper/connections.js.map +1 -1
- package/dist/modules/apimapper/credentials-format.d.ts +21 -0
- package/dist/modules/apimapper/credentials-format.js +145 -0
- package/dist/modules/apimapper/credentials-format.js.map +1 -0
- package/dist/modules/apimapper/credentials.d.ts +12 -2
- package/dist/modules/apimapper/credentials.js +253 -72
- package/dist/modules/apimapper/credentials.js.map +1 -1
- package/dist/modules/apimapper/diagnose.d.ts +54 -2
- package/dist/modules/apimapper/diagnose.js +193 -11
- package/dist/modules/apimapper/diagnose.js.map +1 -1
- package/dist/modules/apimapper/elicitation.d.ts +54 -0
- package/dist/modules/apimapper/elicitation.js +90 -0
- package/dist/modules/apimapper/elicitation.js.map +1 -0
- package/dist/modules/apimapper/flows-format.d.ts +50 -0
- package/dist/modules/apimapper/flows-format.js +318 -0
- package/dist/modules/apimapper/flows-format.js.map +1 -0
- package/dist/modules/apimapper/flows.d.ts +13 -2
- package/dist/modules/apimapper/flows.js +325 -118
- package/dist/modules/apimapper/flows.js.map +1 -1
- package/dist/modules/apimapper/gateway/advanced-tool.d.ts +9 -0
- package/dist/modules/apimapper/gateway/advanced-tool.js +214 -0
- package/dist/modules/apimapper/gateway/advanced-tool.js.map +1 -0
- package/dist/modules/apimapper/gateway/capturing-server.d.ts +81 -0
- package/dist/modules/apimapper/gateway/capturing-server.js +87 -0
- package/dist/modules/apimapper/gateway/capturing-server.js.map +1 -0
- package/dist/modules/apimapper/gateway/essentials.d.ts +4 -0
- package/dist/modules/apimapper/gateway/essentials.js +28 -0
- package/dist/modules/apimapper/gateway/essentials.js.map +1 -0
- package/dist/modules/apimapper/gateway/test-support.d.ts +17 -0
- package/dist/modules/apimapper/gateway/test-support.js +43 -0
- package/dist/modules/apimapper/gateway/test-support.js.map +1 -0
- package/dist/modules/apimapper/get-skill.d.ts +3 -3
- package/dist/modules/apimapper/get-skill.js +4 -2
- package/dist/modules/apimapper/get-skill.js.map +1 -1
- package/dist/modules/apimapper/graph-builder.js +1 -1
- package/dist/modules/apimapper/graph-builder.js.map +1 -1
- package/dist/modules/apimapper/graph.d.ts +2 -2
- package/dist/modules/apimapper/graph.js +165 -34
- package/dist/modules/apimapper/graph.js.map +1 -1
- package/dist/modules/apimapper/index.d.ts +17 -1
- package/dist/modules/apimapper/index.js +66 -17
- package/dist/modules/apimapper/index.js.map +1 -1
- package/dist/modules/apimapper/inspect.d.ts +3 -2
- package/dist/modules/apimapper/inspect.js +97 -13
- package/dist/modules/apimapper/inspect.js.map +1 -1
- package/dist/modules/apimapper/library.d.ts +2 -2
- package/dist/modules/apimapper/library.js +303 -60
- package/dist/modules/apimapper/library.js.map +1 -1
- package/dist/modules/apimapper/license-format.d.ts +22 -0
- package/dist/modules/apimapper/license-format.js +149 -0
- package/dist/modules/apimapper/license-format.js.map +1 -0
- package/dist/modules/apimapper/license.d.ts +16 -2
- package/dist/modules/apimapper/license.js +85 -37
- package/dist/modules/apimapper/license.js.map +1 -1
- package/dist/modules/apimapper/local-sources.d.ts +2 -2
- package/dist/modules/apimapper/local-sources.js +58 -30
- package/dist/modules/apimapper/local-sources.js.map +1 -1
- package/dist/modules/apimapper/misc.d.ts +30 -2
- package/dist/modules/apimapper/misc.js +129 -50
- package/dist/modules/apimapper/misc.js.map +1 -1
- package/dist/modules/apimapper/node-schema.d.ts +52 -0
- package/dist/modules/apimapper/node-schema.js +70 -2
- package/dist/modules/apimapper/node-schema.js.map +1 -1
- package/dist/modules/apimapper/normalizers.d.ts +1 -0
- package/dist/modules/apimapper/normalizers.js +51 -0
- package/dist/modules/apimapper/normalizers.js.map +1 -1
- package/dist/modules/apimapper/onboarding.d.ts +48 -2
- package/dist/modules/apimapper/onboarding.js +324 -17
- package/dist/modules/apimapper/onboarding.js.map +1 -1
- package/dist/modules/apimapper/read-cache.d.ts +31 -2
- package/dist/modules/apimapper/read-cache.js +20 -6
- package/dist/modules/apimapper/read-cache.js.map +1 -1
- package/dist/modules/apimapper/render/_shared.d.ts +24 -0
- package/dist/modules/apimapper/render/_shared.js +84 -0
- package/dist/modules/apimapper/render/_shared.js.map +1 -0
- package/dist/modules/apimapper/render/dag.d.ts +18 -0
- package/dist/modules/apimapper/render/dag.js +70 -0
- package/dist/modules/apimapper/render/dag.js.map +1 -0
- package/dist/modules/apimapper/render/index.d.ts +2 -0
- package/dist/modules/apimapper/render/index.js +112 -0
- package/dist/modules/apimapper/render/index.js.map +1 -0
- package/dist/modules/apimapper/render/renderers/chart-bar.d.ts +2 -0
- package/dist/modules/apimapper/render/renderers/chart-bar.js +70 -0
- package/dist/modules/apimapper/render/renderers/chart-bar.js.map +1 -0
- package/dist/modules/apimapper/render/renderers/chart-line.d.ts +2 -0
- package/dist/modules/apimapper/render/renderers/chart-line.js +71 -0
- package/dist/modules/apimapper/render/renderers/chart-line.js.map +1 -0
- package/dist/modules/apimapper/render/renderers/diff.d.ts +2 -0
- package/dist/modules/apimapper/render/renderers/diff.js +154 -0
- package/dist/modules/apimapper/render/renderers/diff.js.map +1 -0
- package/dist/modules/apimapper/render/renderers/flow-diagram.d.ts +1 -0
- package/dist/modules/apimapper/render/renderers/flow-diagram.js +180 -0
- package/dist/modules/apimapper/render/renderers/flow-diagram.js.map +1 -0
- package/dist/modules/apimapper/render/renderers/json-tree.d.ts +2 -0
- package/dist/modules/apimapper/render/renderers/json-tree.js +87 -0
- package/dist/modules/apimapper/render/renderers/json-tree.js.map +1 -0
- package/dist/modules/apimapper/render/renderers/schema-diagram.d.ts +2 -0
- package/dist/modules/apimapper/render/renderers/schema-diagram.js +83 -0
- package/dist/modules/apimapper/render/renderers/schema-diagram.js.map +1 -0
- package/dist/modules/apimapper/render/renderers/table.d.ts +2 -0
- package/dist/modules/apimapper/render/renderers/table.js +75 -0
- package/dist/modules/apimapper/render/renderers/table.js.map +1 -0
- package/dist/modules/apimapper/render/schemas.d.ts +23 -0
- package/dist/modules/apimapper/render/schemas.js +56 -0
- package/dist/modules/apimapper/render/schemas.js.map +1 -0
- package/dist/modules/apimapper/render/secret-masking.d.ts +5 -0
- package/dist/modules/apimapper/render/secret-masking.js +51 -0
- package/dist/modules/apimapper/render/secret-masking.js.map +1 -0
- package/dist/modules/apimapper/render/sidecar.d.ts +21 -0
- package/dist/modules/apimapper/render/sidecar.js +66 -0
- package/dist/modules/apimapper/render/sidecar.js.map +1 -0
- package/dist/modules/apimapper/render/token-cap.d.ts +21 -0
- package/dist/modules/apimapper/render/token-cap.js +57 -0
- package/dist/modules/apimapper/render/token-cap.js.map +1 -0
- package/dist/modules/apimapper/schema.d.ts +2 -2
- package/dist/modules/apimapper/schema.js +100 -32
- package/dist/modules/apimapper/schema.js.map +1 -1
- package/dist/modules/apimapper/settings-format.d.ts +23 -0
- package/dist/modules/apimapper/settings-format.js +135 -0
- package/dist/modules/apimapper/settings-format.js.map +1 -0
- package/dist/modules/apimapper/settings.d.ts +2 -2
- package/dist/modules/apimapper/settings.js +101 -40
- package/dist/modules/apimapper/settings.js.map +1 -1
- package/dist/modules/apimapper/skill-resources.d.ts +2 -2
- package/dist/modules/apimapper/skill-resources.js.map +1 -1
- package/dist/modules/apimapper/token-baseline.harness.d.ts +91 -0
- package/dist/modules/apimapper/token-baseline.harness.js +291 -0
- package/dist/modules/apimapper/token-baseline.harness.js.map +1 -0
- package/dist/modules/apimapper/toolslist-size.d.ts +55 -0
- package/dist/modules/apimapper/toolslist-size.js +190 -0
- package/dist/modules/apimapper/toolslist-size.js.map +1 -0
- package/dist/modules/apimapper/types.d.ts +23 -8
- package/dist/modules/apimapper/types.js +26 -1
- package/dist/modules/apimapper/types.js.map +1 -1
- package/dist/modules/apimapper/use-profile.d.ts +21 -0
- package/dist/modules/apimapper/use-profile.js +56 -2
- package/dist/modules/apimapper/use-profile.js.map +1 -1
- package/dist/modules/apimapper/workflows.d.ts +2 -2
- package/dist/modules/apimapper/workflows.js +143 -16
- package/dist/modules/apimapper/workflows.js.map +1 -1
- package/dist/platform/index.js +44 -5
- package/dist/platform/index.js.map +1 -1
- package/dist/setup-cli.d.ts +53 -0
- package/dist/setup-cli.js +135 -6
- package/dist/setup-cli.js.map +1 -1
- package/docs/architecture.md +1 -1
- package/docs/tools.md +1 -1
- package/manifest.json +12 -3
- package/package.json +9 -4
- package/skills/apimapper/SKILL.md +1 -1
- package/skills/apimapper/reference/render.md +132 -0
- package/skills/apimapper/reference/troubleshooting.md +1 -1
- package/skills/apimapper/reference/yootheme.md +1 -1
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// renderers/diff.ts — { old, new } → recursive JSON diff renderer.
|
|
2
|
+
//
|
|
3
|
+
// Walks the two payloads in lock-step and classifies every leaf difference:
|
|
4
|
+
// + added — key/index present in `new` but not `old`
|
|
5
|
+
// - removed — key/index present in `old` but not `new`
|
|
6
|
+
// ~ changed — same key, different primitive value or type
|
|
7
|
+
//
|
|
8
|
+
// Paths use dot + bracket notation: object keys join with ".", array indices
|
|
9
|
+
// with "[i]" — e.g. "fields[2].type", "credential.api_key".
|
|
10
|
+
//
|
|
11
|
+
// Secret-masking (design D13): when the LAST path segment is an allowlisted
|
|
12
|
+
// secret field name, the value is shown as "***". Masking is on by default
|
|
13
|
+
// and switched off via `options.mask_secrets === false`.
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
import { isSecretFieldName } from "../secret-masking.js";
|
|
16
|
+
const DiffSchema = z.object({
|
|
17
|
+
old: z
|
|
18
|
+
.unknown()
|
|
19
|
+
.describe("Baseline dataset — fetch first (e.g. apimapper_schema_profile)."),
|
|
20
|
+
new: z
|
|
21
|
+
.unknown()
|
|
22
|
+
.describe("Changed dataset — fetch separately (e.g. a second apimapper_schema_profile call)."),
|
|
23
|
+
label_old: z.string().max(80).optional(),
|
|
24
|
+
label_new: z.string().max(80).optional(),
|
|
25
|
+
}, {
|
|
26
|
+
// Surfaces when `data` is not an object at all (missing / wrong type) —
|
|
27
|
+
// points the caller at the required shape and how to obtain each half.
|
|
28
|
+
error: "diff requires { old, new } — fetch both datasets first " +
|
|
29
|
+
"(e.g. two apimapper_schema_profile calls).",
|
|
30
|
+
});
|
|
31
|
+
/** True for a plain object (not null, not an array). */
|
|
32
|
+
function isPlainObject(v) {
|
|
33
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
34
|
+
}
|
|
35
|
+
/** Render a value for diff output, or "***" when the field is masked. */
|
|
36
|
+
function formatValue(v, masked) {
|
|
37
|
+
if (masked)
|
|
38
|
+
return "***";
|
|
39
|
+
if (v === null)
|
|
40
|
+
return "null";
|
|
41
|
+
if (v === undefined)
|
|
42
|
+
return "undefined";
|
|
43
|
+
if (typeof v === "string")
|
|
44
|
+
return JSON.stringify(v);
|
|
45
|
+
if (typeof v === "object")
|
|
46
|
+
return JSON.stringify(v);
|
|
47
|
+
return String(v);
|
|
48
|
+
}
|
|
49
|
+
/** Last path segment — used for the secret-masking field-name lookup. */
|
|
50
|
+
function lastSegment(path) {
|
|
51
|
+
return path.split(/[.[\]]/).filter(Boolean).pop() ?? "";
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Hard recursion cap for `walk()`. MCP `data` is JSON-deserialized, hence
|
|
55
|
+
* acyclic — real diffs are <10 deep, so 100 is never hit by legitimate
|
|
56
|
+
* input. This is defense-in-depth against pathological / malicious deep
|
|
57
|
+
* nesting causing a stack overflow, NOT an expected path.
|
|
58
|
+
*/
|
|
59
|
+
const MAX_DIFF_DEPTH = 100;
|
|
60
|
+
/** Recursively collect every leaf-level difference between two values. */
|
|
61
|
+
function walk(oldV, newV, path, changes, depth) {
|
|
62
|
+
if (oldV === newV)
|
|
63
|
+
return;
|
|
64
|
+
// Defense-in-depth: stop before the call stack can overflow on
|
|
65
|
+
// pathologically deep input. Emits one marker change and bails.
|
|
66
|
+
if (depth > MAX_DIFF_DEPTH) {
|
|
67
|
+
changes.push({ kind: "~", path, note: "max diff depth exceeded" });
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (isPlainObject(oldV) && isPlainObject(newV)) {
|
|
71
|
+
const keys = new Set([...Object.keys(oldV), ...Object.keys(newV)]);
|
|
72
|
+
for (const key of keys) {
|
|
73
|
+
const subPath = path ? `${path}.${key}` : key;
|
|
74
|
+
const ov = oldV[key];
|
|
75
|
+
const nv = newV[key];
|
|
76
|
+
if (ov === undefined && nv !== undefined) {
|
|
77
|
+
changes.push({ kind: "+", path: subPath, newVal: nv });
|
|
78
|
+
}
|
|
79
|
+
else if (nv === undefined && ov !== undefined) {
|
|
80
|
+
changes.push({ kind: "-", path: subPath, oldVal: ov });
|
|
81
|
+
}
|
|
82
|
+
else if (JSON.stringify(ov) !== JSON.stringify(nv)) {
|
|
83
|
+
walk(ov, nv, subPath, changes, depth + 1);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (Array.isArray(oldV) && Array.isArray(newV)) {
|
|
89
|
+
const maxLen = Math.max(oldV.length, newV.length);
|
|
90
|
+
for (let i = 0; i < maxLen; i++) {
|
|
91
|
+
const subPath = `${path}[${i}]`;
|
|
92
|
+
const ov = oldV[i];
|
|
93
|
+
const nv = newV[i];
|
|
94
|
+
if (ov === undefined && nv !== undefined) {
|
|
95
|
+
changes.push({ kind: "+", path: subPath, newVal: nv });
|
|
96
|
+
}
|
|
97
|
+
else if (nv === undefined && ov !== undefined) {
|
|
98
|
+
// Symmetric with the object branch above: only emit a removal when
|
|
99
|
+
// the OLD slot actually held a value. Guards against a spurious "-"
|
|
100
|
+
// when both array slots are undefined (e.g. a sparse-array hole).
|
|
101
|
+
changes.push({ kind: "-", path: subPath, oldVal: ov });
|
|
102
|
+
}
|
|
103
|
+
else if (JSON.stringify(ov) !== JSON.stringify(nv)) {
|
|
104
|
+
walk(ov, nv, subPath, changes, depth + 1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
// Primitive change or a structural type mismatch (object vs array vs scalar).
|
|
110
|
+
changes.push({ kind: "~", path, oldVal: oldV, newVal: newV });
|
|
111
|
+
}
|
|
112
|
+
const RULE_WIDTH = 60;
|
|
113
|
+
export function renderDiff(input, options) {
|
|
114
|
+
const { old: oldV, new: newV, label_old, label_new } = DiffSchema.parse(input);
|
|
115
|
+
const changes = [];
|
|
116
|
+
walk(oldV, newV, "", changes, 0);
|
|
117
|
+
// Masking is on unless explicitly disabled.
|
|
118
|
+
const maskEnabled = options.mask_secrets !== false;
|
|
119
|
+
const lines = [];
|
|
120
|
+
if (options.title)
|
|
121
|
+
lines.push(options.title);
|
|
122
|
+
lines.push(`Diff: ${label_old ?? "old"} → ${label_new ?? "new"}`);
|
|
123
|
+
lines.push("─".repeat(RULE_WIDTH));
|
|
124
|
+
if (changes.length === 0) {
|
|
125
|
+
lines.push("(no changes)");
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
for (const change of changes) {
|
|
129
|
+
const masked = maskEnabled && isSecretFieldName(lastSegment(change.path));
|
|
130
|
+
if (change.note) {
|
|
131
|
+
// Structural marker (e.g. depth cap) — no old/new values to show.
|
|
132
|
+
lines.push(` ~ ${change.path} (${change.note})`);
|
|
133
|
+
}
|
|
134
|
+
else if (change.kind === "+") {
|
|
135
|
+
lines.push(` + ${change.path} ${formatValue(change.newVal, masked)}`);
|
|
136
|
+
}
|
|
137
|
+
else if (change.kind === "-") {
|
|
138
|
+
lines.push(` - ${change.path} ${formatValue(change.oldVal, masked)}`);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
lines.push(` ~ ${change.path} ${formatValue(change.oldVal, masked)} → ` +
|
|
142
|
+
formatValue(change.newVal, masked));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
lines.push("─".repeat(RULE_WIDTH));
|
|
147
|
+
const added = changes.filter((c) => c.kind === "+").length;
|
|
148
|
+
const removed = changes.filter((c) => c.kind === "-").length;
|
|
149
|
+
const modified = changes.filter((c) => c.kind === "~").length;
|
|
150
|
+
lines.push(`${changes.length} changes • ${added} added • ${removed} removed • ` +
|
|
151
|
+
`${modified} modified`);
|
|
152
|
+
return lines.join("\n");
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../../../../src/modules/apimapper/render/renderers/diff.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function renderFlowDiagram(input: unknown): string;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// renderers/flow-diagram.ts — ASCII flow-diagram renderer.
|
|
2
|
+
//
|
|
3
|
+
// Input: a flow object with `nodes` and `edges`. Each node has an id and a
|
|
4
|
+
// type; the renderer derives layers via topological sort and joins them
|
|
5
|
+
// horizontally with arrow connectors. Multi-line node-boxes encode
|
|
6
|
+
// `{display-name, type}`.
|
|
7
|
+
//
|
|
8
|
+
// Layout overview:
|
|
9
|
+
//
|
|
10
|
+
// ┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
11
|
+
// │ src_pex │ ─► │ merge_1 │ ─► │ filter_1 │
|
|
12
|
+
// │ source │ │ merge │ │ filter │
|
|
13
|
+
// └──────────┘ └──────────┘ └──────────┘
|
|
14
|
+
// ┌──────────┐
|
|
15
|
+
// │ src_ig │
|
|
16
|
+
// │ source │
|
|
17
|
+
// └──────────┘
|
|
18
|
+
//
|
|
19
|
+
// 5 nodes • 4 edges
|
|
20
|
+
//
|
|
21
|
+
// Edge-cases handled:
|
|
22
|
+
// - Cycle: unreachable nodes appended as a trailing layer with a footer
|
|
23
|
+
// warning "⚠ cycle detected".
|
|
24
|
+
// - 50-node soft cap: slice nodes, filter edges to the kept set,
|
|
25
|
+
// footer "showing first 50 of N".
|
|
26
|
+
// - Disconnected sub-graphs: layers contain nodes from multiple chains,
|
|
27
|
+
// boxes stack vertically within a layer.
|
|
28
|
+
// - Empty nodes array: Zod rejects (caller sees a clear validation error).
|
|
29
|
+
import { z } from "zod";
|
|
30
|
+
import { box, padRight, truncate, graphemeLength } from "../_shared.js";
|
|
31
|
+
import { topoLayers } from "../dag.js";
|
|
32
|
+
const FlowVizSchema = z
|
|
33
|
+
.object({
|
|
34
|
+
id: z.string().optional(),
|
|
35
|
+
name: z.string().optional(),
|
|
36
|
+
nodes: z
|
|
37
|
+
.array(z
|
|
38
|
+
.object({
|
|
39
|
+
id: z.string(),
|
|
40
|
+
type: z.string(),
|
|
41
|
+
data: z.record(z.string(), z.unknown()).optional(),
|
|
42
|
+
})
|
|
43
|
+
.passthrough())
|
|
44
|
+
.min(1, "flow must have at least one node"),
|
|
45
|
+
edges: z.array(z
|
|
46
|
+
.object({
|
|
47
|
+
source: z.string(),
|
|
48
|
+
target: z.string(),
|
|
49
|
+
})
|
|
50
|
+
.passthrough()),
|
|
51
|
+
})
|
|
52
|
+
.passthrough();
|
|
53
|
+
const MAX_NODES = 50;
|
|
54
|
+
// Box width math: inside = BOX_WIDTH - 2 (borders). Content takes inside - 2
|
|
55
|
+
// (leading space + at-least-one-trailing-pad). At BOX_WIDTH=14 content fits
|
|
56
|
+
// up to 10 graphemes — generous enough for typical node ids and the type
|
|
57
|
+
// label without sacrificing horizontal real-estate at 4 layers.
|
|
58
|
+
const BOX_WIDTH = 14;
|
|
59
|
+
// COL_SPACING must be >= 4 to fit the " ─► " arrow with leading/trailing
|
|
60
|
+
// breathing space.
|
|
61
|
+
const COL_SPACING = 4;
|
|
62
|
+
function nodeLabel(node) {
|
|
63
|
+
// Prefer `data.name` for outputs (YOOtheme source name) — falls back to the
|
|
64
|
+
// node id, which is always present and stable.
|
|
65
|
+
const dataObj = node.data && typeof node.data === "object" ? node.data : undefined;
|
|
66
|
+
const dataName = dataObj && typeof dataObj.name === "string" ? dataObj.name : undefined;
|
|
67
|
+
const primary = dataName ?? node.id;
|
|
68
|
+
const secondary = node.type;
|
|
69
|
+
// box() truncates internally too, but we pre-truncate here so the snapshot
|
|
70
|
+
// shows the exact label the renderer chose (and so the line2 type-label
|
|
71
|
+
// never out-competes line1 for width).
|
|
72
|
+
const contentMax = Math.max(0, BOX_WIDTH - 4);
|
|
73
|
+
return {
|
|
74
|
+
line1: truncate(primary, contentMax),
|
|
75
|
+
line2: truncate(secondary, contentMax),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function renderNodeBox(node) {
|
|
79
|
+
const { line1, line2 } = nodeLabel(node);
|
|
80
|
+
return box([line1, line2], BOX_WIDTH).split("\n");
|
|
81
|
+
}
|
|
82
|
+
function renderLayerBlock(nodes) {
|
|
83
|
+
// Stack node-boxes vertically with one blank gap line between them.
|
|
84
|
+
// topoLayers only ever pushes non-empty layers (it advances the frontier
|
|
85
|
+
// while `frontier.length > 0`) and the unreachable layer is appended only
|
|
86
|
+
// when it has members — so an empty `nodes` array never reaches here.
|
|
87
|
+
/* v8 ignore next -- topoLayers never emits an empty layer; defensive guard */
|
|
88
|
+
if (nodes.length === 0)
|
|
89
|
+
return [];
|
|
90
|
+
const result = [];
|
|
91
|
+
nodes.forEach((node, i) => {
|
|
92
|
+
if (i > 0)
|
|
93
|
+
result.push(padRight("", BOX_WIDTH));
|
|
94
|
+
result.push(...renderNodeBox(node));
|
|
95
|
+
});
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
function padBlockHeight(block, height) {
|
|
99
|
+
if (block.length >= height)
|
|
100
|
+
return block;
|
|
101
|
+
const pad = padRight("", BOX_WIDTH);
|
|
102
|
+
const out = [...block];
|
|
103
|
+
while (out.length < height)
|
|
104
|
+
out.push(pad);
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
107
|
+
function joinLayersHorizontally(blocks) {
|
|
108
|
+
// Every valid flow has >=1 node, hence >=1 topo layer, hence >=1 block —
|
|
109
|
+
// an empty `blocks` array never reaches here.
|
|
110
|
+
/* v8 ignore next -- callers always pass >=1 layer block; defensive guard */
|
|
111
|
+
if (blocks.length === 0)
|
|
112
|
+
return [];
|
|
113
|
+
const maxHeight = Math.max(...blocks.map((b) => b.length));
|
|
114
|
+
const padded = blocks.map((b) => padBlockHeight(b, maxHeight));
|
|
115
|
+
// Arrow appears in the geometric middle row of the tallest block, which
|
|
116
|
+
// visually aligns with the centre of single-node columns.
|
|
117
|
+
const arrowRow = Math.floor(maxHeight / 2);
|
|
118
|
+
const arrowGap = " ─► ";
|
|
119
|
+
const blankGap = padRight("", COL_SPACING);
|
|
120
|
+
// Defensive: confirm gap widths match COL_SPACING contract. `arrowGap`
|
|
121
|
+
// is the literal " ─► " whose grapheme length is exactly COL_SPACING (4),
|
|
122
|
+
// so `graphemeLength(arrowGap) === COL_SPACING` is statically always true
|
|
123
|
+
// and the padRight arm is a guard against a future literal edit only.
|
|
124
|
+
/* v8 ignore next 4 -- arrowGap literal is exactly COL_SPACING graphemes; false arm dead */
|
|
125
|
+
const arrowGapPadded = graphemeLength(arrowGap) === COL_SPACING
|
|
126
|
+
? arrowGap
|
|
127
|
+
: padRight(arrowGap, COL_SPACING);
|
|
128
|
+
const lines = [];
|
|
129
|
+
for (let row = 0; row < maxHeight; row++) {
|
|
130
|
+
const parts = [];
|
|
131
|
+
padded.forEach((block, i) => {
|
|
132
|
+
parts.push(block[row]);
|
|
133
|
+
if (i < padded.length - 1) {
|
|
134
|
+
parts.push(row === arrowRow ? arrowGapPadded : blankGap);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
lines.push(parts.join(""));
|
|
138
|
+
}
|
|
139
|
+
return lines;
|
|
140
|
+
}
|
|
141
|
+
export function renderFlowDiagram(input) {
|
|
142
|
+
const flow = FlowVizSchema.parse(input);
|
|
143
|
+
const totalNodes = flow.nodes.length;
|
|
144
|
+
const totalEdges = flow.edges.length;
|
|
145
|
+
// 50-node soft cap. Keep order stable (first N nodes), then filter edges to
|
|
146
|
+
// those whose source AND target survive the cap so topo doesn't see dangling
|
|
147
|
+
// refs.
|
|
148
|
+
let workingNodes = flow.nodes;
|
|
149
|
+
let truncated = false;
|
|
150
|
+
if (workingNodes.length > MAX_NODES) {
|
|
151
|
+
workingNodes = workingNodes.slice(0, MAX_NODES);
|
|
152
|
+
truncated = true;
|
|
153
|
+
}
|
|
154
|
+
const allowedIds = new Set(workingNodes.map((n) => n.id));
|
|
155
|
+
const workingEdges = flow.edges.filter((e) => allowedIds.has(e.source) && allowedIds.has(e.target));
|
|
156
|
+
const topo = topoLayers(workingNodes, workingEdges);
|
|
157
|
+
const idToNode = new Map(workingNodes.map((n) => [n.id, n]));
|
|
158
|
+
const layers = topo.layers.map((idList) => idList
|
|
159
|
+
.map((id) => idToNode.get(id))
|
|
160
|
+
.filter((n) => n !== undefined));
|
|
161
|
+
// Append unreachable nodes as a trailing layer so cycle members still
|
|
162
|
+
// render — annotated by the footer warning.
|
|
163
|
+
if (topo.unreachable.length > 0) {
|
|
164
|
+
layers.push(topo.unreachable
|
|
165
|
+
.map((id) => idToNode.get(id))
|
|
166
|
+
.filter((n) => n !== undefined));
|
|
167
|
+
}
|
|
168
|
+
const layerBlocks = layers.map(renderLayerBlock);
|
|
169
|
+
const lines = joinLayersHorizontally(layerBlocks);
|
|
170
|
+
// Footer: "N nodes • M edges [• ⚠ cycle detected] [• showing first 50 of K]"
|
|
171
|
+
const footerParts = [`${totalNodes} nodes • ${totalEdges} edges`];
|
|
172
|
+
if (topo.cycle)
|
|
173
|
+
footerParts.push("⚠ cycle detected");
|
|
174
|
+
if (truncated)
|
|
175
|
+
footerParts.push(`showing first ${MAX_NODES} of ${totalNodes}`);
|
|
176
|
+
lines.push("");
|
|
177
|
+
lines.push(footerParts.join(" • "));
|
|
178
|
+
return lines.join("\n");
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=flow-diagram.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-diagram.js","sourceRoot":"","sources":["../../../../../src/modules/apimapper/render/renderers/flow-diagram.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { truncate } from "../_shared.js";
|
|
2
|
+
const DEFAULT_MAX_DEPTH = 3;
|
|
3
|
+
const MAX_ARRAY_ITEMS = 5;
|
|
4
|
+
const MAX_OBJECT_KEYS = 8;
|
|
5
|
+
const STRING_MAX_GRAPHEMES = 60;
|
|
6
|
+
const BRANCH_LAST = "└─ ";
|
|
7
|
+
const BRANCH_MID = "├─ ";
|
|
8
|
+
const PREFIX_LAST = " "; // 3 spaces — no continuation bar
|
|
9
|
+
const PREFIX_MID = "│ "; // bar + 2 spaces — continuation under a non-last node
|
|
10
|
+
/** Render a primitive (non-container) value as a compact display string. */
|
|
11
|
+
function formatPrimitive(value) {
|
|
12
|
+
if (value === null)
|
|
13
|
+
return "null";
|
|
14
|
+
if (value === undefined)
|
|
15
|
+
return "undefined";
|
|
16
|
+
if (typeof value === "string") {
|
|
17
|
+
return JSON.stringify(truncate(value, STRING_MAX_GRAPHEMES));
|
|
18
|
+
}
|
|
19
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
20
|
+
return String(value);
|
|
21
|
+
}
|
|
22
|
+
// Functions / symbols are not valid JSON but stay defensive.
|
|
23
|
+
return String(value);
|
|
24
|
+
}
|
|
25
|
+
function isContainer(value) {
|
|
26
|
+
return value !== null && typeof value === "object";
|
|
27
|
+
}
|
|
28
|
+
export function renderJsonTree(input, options) {
|
|
29
|
+
const maxDepth = options.depth ?? DEFAULT_MAX_DEPTH;
|
|
30
|
+
const lines = [];
|
|
31
|
+
/**
|
|
32
|
+
* Emit the subtree for `value`.
|
|
33
|
+
*
|
|
34
|
+
* @param value the JSON value at this node
|
|
35
|
+
* @param prefix accumulated indentation (bars + spaces) for THIS line
|
|
36
|
+
* @param isLast whether this node is the last among its siblings
|
|
37
|
+
* @param label key / index label, or null for the synthetic root
|
|
38
|
+
* @param depth 0-based depth (root children are depth 1)
|
|
39
|
+
*/
|
|
40
|
+
function walk(value, prefix, isLast, label, depth) {
|
|
41
|
+
const branch = label === null ? "" : isLast ? BRANCH_LAST : BRANCH_MID;
|
|
42
|
+
const labelPart = label === null ? "" : `${label}: `;
|
|
43
|
+
const childPrefix = label === null ? prefix : prefix + (isLast ? PREFIX_LAST : PREFIX_MID);
|
|
44
|
+
if (!isContainer(value)) {
|
|
45
|
+
lines.push(prefix + branch + labelPart + formatPrimitive(value));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (Array.isArray(value)) {
|
|
49
|
+
if (depth >= maxDepth) {
|
|
50
|
+
lines.push(`${prefix}${branch}${labelPart}[ ${value.length} items, depth-limited ]`);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
lines.push(`${prefix}${branch}${labelPart}[${value.length} items]`);
|
|
54
|
+
const shown = value.slice(0, MAX_ARRAY_ITEMS);
|
|
55
|
+
const overflow = value.length - shown.length;
|
|
56
|
+
shown.forEach((item, i) => {
|
|
57
|
+
const childIsLast = i === shown.length - 1 && overflow === 0;
|
|
58
|
+
walk(item, childPrefix, childIsLast, `[${i}]`, depth + 1);
|
|
59
|
+
});
|
|
60
|
+
if (overflow > 0) {
|
|
61
|
+
lines.push(`${childPrefix}${BRANCH_LAST}… ${overflow} more items`);
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// Plain object.
|
|
66
|
+
const obj = value;
|
|
67
|
+
const keys = Object.keys(obj);
|
|
68
|
+
if (depth >= maxDepth) {
|
|
69
|
+
lines.push(`${prefix}${branch}${labelPart}{ ${keys.length} fields, depth-limited }`);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
lines.push(`${prefix}${branch}${labelPart}{${keys.length} fields}`);
|
|
73
|
+
const shownKeys = keys.slice(0, MAX_OBJECT_KEYS);
|
|
74
|
+
const overflow = keys.length - shownKeys.length;
|
|
75
|
+
shownKeys.forEach((key, i) => {
|
|
76
|
+
const childIsLast = i === shownKeys.length - 1 && overflow === 0;
|
|
77
|
+
walk(obj[key], childPrefix, childIsLast, key, depth + 1);
|
|
78
|
+
});
|
|
79
|
+
if (overflow > 0) {
|
|
80
|
+
lines.push(`${childPrefix}${BRANCH_LAST}… ${overflow} more keys`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// The root node carries no label/branch — its children start at depth 1.
|
|
84
|
+
walk(input, "", true, null, 0);
|
|
85
|
+
return lines.join("\n");
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=json-tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-tree.js","sourceRoot":"","sources":["../../../../../src/modules/apimapper/render/renderers/json-tree.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// renderers/schema-diagram.ts — schema-profile → text field-table renderer.
|
|
2
|
+
//
|
|
3
|
+
// Input is the shape returned by `apimapper_schema_profile`: a `fields[]`
|
|
4
|
+
// array where each field carries a `name`, a `type` and optional `samples`.
|
|
5
|
+
// The renderer projects this into a 3-column Field / Type / Sample table via
|
|
6
|
+
// the toolkit's `autoFormatTable`.
|
|
7
|
+
//
|
|
8
|
+
// Sample handling: the first sample value is JSON-stringified (so strings are
|
|
9
|
+
// quoted, numbers/booleans render literally) and truncated to 40 graphemes
|
|
10
|
+
// with the shared grapheme-safe `truncate`. Fields with no samples show "—".
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import { autoFormatTable } from "@getimo/mcp-toolkit";
|
|
13
|
+
import { truncate } from "../_shared.js";
|
|
14
|
+
const SchemaProfileSchema = z
|
|
15
|
+
.object({
|
|
16
|
+
fields: z
|
|
17
|
+
.array(z
|
|
18
|
+
.object({
|
|
19
|
+
name: z.string(),
|
|
20
|
+
type: z.string(),
|
|
21
|
+
samples: z.array(z.unknown()).optional(),
|
|
22
|
+
})
|
|
23
|
+
.passthrough())
|
|
24
|
+
.min(1, "schema profile must have at least one field")
|
|
25
|
+
// Input cap for consistency with every other renderer (table 500,
|
|
26
|
+
// chart 20/64, flow 50) and as defense against a pathological
|
|
27
|
+
// 100k-field profile blowing up the field table.
|
|
28
|
+
.max(500, "schema profile is capped at 500 fields"),
|
|
29
|
+
item_count: z.number().optional(),
|
|
30
|
+
source_count: z.number().optional(),
|
|
31
|
+
})
|
|
32
|
+
.passthrough();
|
|
33
|
+
const SAMPLE_MAX_GRAPHEMES = 40;
|
|
34
|
+
const NO_SAMPLE_PLACEHOLDER = "—";
|
|
35
|
+
/**
|
|
36
|
+
* Width handed to autoFormatTable for the Sample column. Kept >=
|
|
37
|
+
* SAMPLE_MAX_GRAPHEMES so the toolkit's own column clip (which appends a
|
|
38
|
+
* bare ".") never re-truncates a cell the renderer already truncated with
|
|
39
|
+
* "…" — the renderer's grapheme-safe ellipsis always wins.
|
|
40
|
+
*/
|
|
41
|
+
const SAMPLE_COLUMN_WIDTH = SAMPLE_MAX_GRAPHEMES + 2;
|
|
42
|
+
/** Render the first sample as a single truncated cell, or the placeholder. */
|
|
43
|
+
function sampleCell(samples) {
|
|
44
|
+
if (!samples || samples.length === 0 || samples[0] === undefined) {
|
|
45
|
+
return NO_SAMPLE_PLACEHOLDER;
|
|
46
|
+
}
|
|
47
|
+
const first = samples[0];
|
|
48
|
+
// `first` is never undefined here (guarded above) and `samples` is
|
|
49
|
+
// JSON-deserialized MCP data, so it can never hold a function or symbol.
|
|
50
|
+
// JSON.stringify therefore always returns a string for non-string values.
|
|
51
|
+
const raw = typeof first === "string" ? first : JSON.stringify(first);
|
|
52
|
+
return truncate(raw, SAMPLE_MAX_GRAPHEMES);
|
|
53
|
+
}
|
|
54
|
+
export function renderSchemaDiagram(input, options) {
|
|
55
|
+
const schema = SchemaProfileSchema.parse(input);
|
|
56
|
+
const rows = schema.fields.map((field) => ({
|
|
57
|
+
Field: field.name,
|
|
58
|
+
Type: field.type,
|
|
59
|
+
Sample: sampleCell(field.samples),
|
|
60
|
+
}));
|
|
61
|
+
const result = autoFormatTable(rows, {
|
|
62
|
+
columns: [
|
|
63
|
+
{ key: "Field", label: "Field" },
|
|
64
|
+
{ key: "Type", label: "Type" },
|
|
65
|
+
// Explicit width >= the renderer's truncation length so the toolkit
|
|
66
|
+
// never re-clips an already-truncated sample with a bare ".".
|
|
67
|
+
{ key: "Sample", label: "Sample", width: SAMPLE_COLUMN_WIDTH },
|
|
68
|
+
],
|
|
69
|
+
header: () => options.title ??
|
|
70
|
+
`${schema.fields.length} fields • profile from ${schema.item_count ?? "?"} sampled items`,
|
|
71
|
+
});
|
|
72
|
+
// autoFormatTable returns a CallToolResult — extract the text payload.
|
|
73
|
+
const first = result.content[0];
|
|
74
|
+
// The JSON.stringify arm is an unreachable defensive fallback — the
|
|
75
|
+
// toolkit always returns a text content block, so only the `first.text`
|
|
76
|
+
// arm ever executes. Kept so a future toolkit change can never make this
|
|
77
|
+
// renderer return `undefined`.
|
|
78
|
+
/* v8 ignore next 3 -- autoFormatTable always returns a text block; false arm dead */
|
|
79
|
+
return first && first.type === "text" && typeof first.text === "string"
|
|
80
|
+
? first.text
|
|
81
|
+
: JSON.stringify(rows);
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=schema-diagram.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-diagram.js","sourceRoot":"","sources":["../../../../../src/modules/apimapper/render/renderers/schema-diagram.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// renderers/table.ts — array-of-objects → text table renderer.
|
|
2
|
+
//
|
|
3
|
+
// Thin adapter around the toolkit's `autoFormatTable`, which already handles
|
|
4
|
+
// column sizing, header/footer composition and detail-level auto-scaling.
|
|
5
|
+
// This renderer's job is the two things the toolkit does NOT do:
|
|
6
|
+
// 1. Validate that `data` really is an array of objects (Zod).
|
|
7
|
+
// 2. Auto-detect a sensible column set from the first item when the caller
|
|
8
|
+
// did not pass `options.columns`.
|
|
9
|
+
//
|
|
10
|
+
// Column auto-detection rule: take the first item's top-level keys whose
|
|
11
|
+
// values are primitives (string / number / boolean) or null — nested
|
|
12
|
+
// objects/arrays do not belong in a flat table cell — and keep the first 8.
|
|
13
|
+
// `options.columns` (when non-empty) overrides this entirely.
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
import { autoFormatTable } from "@getimo/mcp-toolkit";
|
|
16
|
+
const TableSchema = z
|
|
17
|
+
.array(z.record(z.string(), z.unknown()), {
|
|
18
|
+
// Surfaces when `data` is not an array (missing / wrong type) — points
|
|
19
|
+
// the caller at the expected shape and a concrete tool that produces it.
|
|
20
|
+
error: "table requires an array of objects — e.g. the payload from " +
|
|
21
|
+
"apimapper_connection_data or apimapper_graph_preview.",
|
|
22
|
+
})
|
|
23
|
+
.min(0)
|
|
24
|
+
.max(500);
|
|
25
|
+
const DEFAULT_MAX_ROWS = 50;
|
|
26
|
+
const MAX_AUTO_COLUMNS = 8;
|
|
27
|
+
/** Cell value types that render cleanly in a single flat table column. */
|
|
28
|
+
function isPrimitiveCell(value) {
|
|
29
|
+
return (value === null ||
|
|
30
|
+
typeof value === "string" ||
|
|
31
|
+
typeof value === "number" ||
|
|
32
|
+
typeof value === "boolean");
|
|
33
|
+
}
|
|
34
|
+
/** Pick up to 8 primitive-typed top-level keys from a sample item. */
|
|
35
|
+
function autoDetectColumns(sample) {
|
|
36
|
+
return Object.keys(sample)
|
|
37
|
+
.filter((key) => isPrimitiveCell(sample[key]))
|
|
38
|
+
.slice(0, MAX_AUTO_COLUMNS);
|
|
39
|
+
}
|
|
40
|
+
export function renderTable(input, options) {
|
|
41
|
+
const items = TableSchema.parse(input);
|
|
42
|
+
const maxRows = options.max_rows ?? DEFAULT_MAX_ROWS;
|
|
43
|
+
const sliced = items.slice(0, maxRows);
|
|
44
|
+
// `options.columns` wins when non-empty; otherwise derive from the first
|
|
45
|
+
// surviving row. An empty array yields an empty column list — handled below.
|
|
46
|
+
const columns = options.columns && options.columns.length > 0
|
|
47
|
+
? options.columns
|
|
48
|
+
: sliced[0]
|
|
49
|
+
? autoDetectColumns(sliced[0])
|
|
50
|
+
: [];
|
|
51
|
+
if (columns.length === 0) {
|
|
52
|
+
// No rows (or a row with no primitive keys): degrade gracefully rather
|
|
53
|
+
// than handing autoFormatTable an empty column spec.
|
|
54
|
+
return options.title
|
|
55
|
+
? `${options.title}\n(no rows)`
|
|
56
|
+
: `(no rows — 0 of ${items.length})`;
|
|
57
|
+
}
|
|
58
|
+
const result = autoFormatTable(sliced, {
|
|
59
|
+
columns: columns.map((key) => ({ key, label: key })),
|
|
60
|
+
header: (count) => options.title
|
|
61
|
+
? `${options.title} (${count} of ${items.length} rows)`
|
|
62
|
+
: `${count} of ${items.length} rows`,
|
|
63
|
+
});
|
|
64
|
+
// autoFormatTable returns a CallToolResult — extract the text payload.
|
|
65
|
+
const first = result.content[0];
|
|
66
|
+
// The JSON.stringify arm is an unreachable defensive fallback — the
|
|
67
|
+
// toolkit always returns a text content block, so only the `first.text`
|
|
68
|
+
// arm ever executes. Kept so a future toolkit change can never make this
|
|
69
|
+
// renderer return `undefined`.
|
|
70
|
+
/* v8 ignore next 3 -- autoFormatTable always returns a text block; false arm dead */
|
|
71
|
+
return first && first.type === "text" && typeof first.text === "string"
|
|
72
|
+
? first.text
|
|
73
|
+
: JSON.stringify(sliced);
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table.js","sourceRoot":"","sources":["../../../../../src/modules/apimapper/render/renderers/table.ts"],"names":[],"mappings":"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"}
|