@wootsup/mcp 0.1.0-rc.9 → 0.3.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.
Files changed (240) hide show
  1. package/CHANGELOG.md +148 -83
  2. package/README.md +36 -32
  3. package/SECURITY.md +15 -6
  4. package/dist/auth/keychain.d.ts +27 -1
  5. package/dist/auth/keychain.js +48 -2
  6. package/dist/auth/keychain.js.map +1 -1
  7. package/dist/cli-hint.d.ts +22 -0
  8. package/dist/cli-hint.js +55 -0
  9. package/dist/cli-hint.js.map +1 -0
  10. package/dist/index.d.ts +19 -0
  11. package/dist/index.js +163 -22
  12. package/dist/index.js.map +1 -1
  13. package/dist/install-skill.js +1 -1
  14. package/dist/modules/apimapper/cache.d.ts +2 -2
  15. package/dist/modules/apimapper/cache.js +119 -29
  16. package/dist/modules/apimapper/cache.js.map +1 -1
  17. package/dist/modules/apimapper/client.d.ts +102 -1
  18. package/dist/modules/apimapper/client.js +631 -297
  19. package/dist/modules/apimapper/client.js.map +1 -1
  20. package/dist/modules/apimapper/connections-format.d.ts +51 -0
  21. package/dist/modules/apimapper/connections-format.js +261 -0
  22. package/dist/modules/apimapper/connections-format.js.map +1 -0
  23. package/dist/modules/apimapper/connections-trim.d.ts +82 -0
  24. package/dist/modules/apimapper/connections-trim.js +224 -0
  25. package/dist/modules/apimapper/connections-trim.js.map +1 -0
  26. package/dist/modules/apimapper/connections.d.ts +14 -2
  27. package/dist/modules/apimapper/connections.js +612 -153
  28. package/dist/modules/apimapper/connections.js.map +1 -1
  29. package/dist/modules/apimapper/credential-sanitizer.d.ts +5 -0
  30. package/dist/modules/apimapper/credential-sanitizer.js +60 -1
  31. package/dist/modules/apimapper/credential-sanitizer.js.map +1 -1
  32. package/dist/modules/apimapper/credentials-format.d.ts +21 -0
  33. package/dist/modules/apimapper/credentials-format.js +145 -0
  34. package/dist/modules/apimapper/credentials-format.js.map +1 -0
  35. package/dist/modules/apimapper/credentials.d.ts +12 -2
  36. package/dist/modules/apimapper/credentials.js +226 -73
  37. package/dist/modules/apimapper/credentials.js.map +1 -1
  38. package/dist/modules/apimapper/diagnose.d.ts +54 -2
  39. package/dist/modules/apimapper/diagnose.js +213 -12
  40. package/dist/modules/apimapper/diagnose.js.map +1 -1
  41. package/dist/modules/apimapper/elicitation.d.ts +54 -0
  42. package/dist/modules/apimapper/elicitation.js +90 -0
  43. package/dist/modules/apimapper/elicitation.js.map +1 -0
  44. package/dist/modules/apimapper/flows-format.d.ts +50 -0
  45. package/dist/modules/apimapper/flows-format.js +318 -0
  46. package/dist/modules/apimapper/flows-format.js.map +1 -0
  47. package/dist/modules/apimapper/flows.d.ts +13 -2
  48. package/dist/modules/apimapper/flows.js +312 -122
  49. package/dist/modules/apimapper/flows.js.map +1 -1
  50. package/dist/modules/apimapper/gateway/advanced-tool.d.ts +9 -0
  51. package/dist/modules/apimapper/gateway/advanced-tool.js +265 -0
  52. package/dist/modules/apimapper/gateway/advanced-tool.js.map +1 -0
  53. package/dist/modules/apimapper/gateway/capturing-server.d.ts +81 -0
  54. package/dist/modules/apimapper/gateway/capturing-server.js +87 -0
  55. package/dist/modules/apimapper/gateway/capturing-server.js.map +1 -0
  56. package/dist/modules/apimapper/gateway/essentials.d.ts +4 -0
  57. package/dist/modules/apimapper/gateway/essentials.js +35 -0
  58. package/dist/modules/apimapper/gateway/essentials.js.map +1 -0
  59. package/dist/modules/apimapper/gateway/test-support.d.ts +17 -0
  60. package/dist/modules/apimapper/gateway/test-support.js +43 -0
  61. package/dist/modules/apimapper/gateway/test-support.js.map +1 -0
  62. package/dist/modules/apimapper/get-skill.d.ts +3 -3
  63. package/dist/modules/apimapper/get-skill.js +47 -7
  64. package/dist/modules/apimapper/get-skill.js.map +1 -1
  65. package/dist/modules/apimapper/graph-builder.js +1 -1
  66. package/dist/modules/apimapper/graph-builder.js.map +1 -1
  67. package/dist/modules/apimapper/graph.d.ts +2 -2
  68. package/dist/modules/apimapper/graph.js +170 -35
  69. package/dist/modules/apimapper/graph.js.map +1 -1
  70. package/dist/modules/apimapper/index.d.ts +17 -1
  71. package/dist/modules/apimapper/index.js +68 -17
  72. package/dist/modules/apimapper/index.js.map +1 -1
  73. package/dist/modules/apimapper/inspect.d.ts +3 -2
  74. package/dist/modules/apimapper/inspect.js +97 -13
  75. package/dist/modules/apimapper/inspect.js.map +1 -1
  76. package/dist/modules/apimapper/library.d.ts +2 -2
  77. package/dist/modules/apimapper/library.js +665 -80
  78. package/dist/modules/apimapper/library.js.map +1 -1
  79. package/dist/modules/apimapper/license-format.d.ts +22 -0
  80. package/dist/modules/apimapper/license-format.js +149 -0
  81. package/dist/modules/apimapper/license-format.js.map +1 -0
  82. package/dist/modules/apimapper/license.d.ts +16 -2
  83. package/dist/modules/apimapper/license.js +62 -38
  84. package/dist/modules/apimapper/license.js.map +1 -1
  85. package/dist/modules/apimapper/local-sources.d.ts +2 -2
  86. package/dist/modules/apimapper/local-sources.js +44 -30
  87. package/dist/modules/apimapper/local-sources.js.map +1 -1
  88. package/dist/modules/apimapper/misc.d.ts +30 -2
  89. package/dist/modules/apimapper/misc.js +114 -49
  90. package/dist/modules/apimapper/misc.js.map +1 -1
  91. package/dist/modules/apimapper/node-schema.d.ts +52 -0
  92. package/dist/modules/apimapper/node-schema.js +70 -2
  93. package/dist/modules/apimapper/node-schema.js.map +1 -1
  94. package/dist/modules/apimapper/normalizers.d.ts +1 -0
  95. package/dist/modules/apimapper/normalizers.js +51 -0
  96. package/dist/modules/apimapper/normalizers.js.map +1 -1
  97. package/dist/modules/apimapper/onboarding.d.ts +78 -3
  98. package/dist/modules/apimapper/onboarding.js +428 -26
  99. package/dist/modules/apimapper/onboarding.js.map +1 -1
  100. package/dist/modules/apimapper/read-cache.d.ts +31 -2
  101. package/dist/modules/apimapper/read-cache.js +20 -6
  102. package/dist/modules/apimapper/read-cache.js.map +1 -1
  103. package/dist/modules/apimapper/render/_shared.d.ts +24 -0
  104. package/dist/modules/apimapper/render/_shared.js +84 -0
  105. package/dist/modules/apimapper/render/_shared.js.map +1 -0
  106. package/dist/modules/apimapper/render/dag.d.ts +18 -0
  107. package/dist/modules/apimapper/render/dag.js +70 -0
  108. package/dist/modules/apimapper/render/dag.js.map +1 -0
  109. package/dist/modules/apimapper/render/index.d.ts +2 -0
  110. package/dist/modules/apimapper/render/index.js +112 -0
  111. package/dist/modules/apimapper/render/index.js.map +1 -0
  112. package/dist/modules/apimapper/render/renderers/chart-bar.d.ts +2 -0
  113. package/dist/modules/apimapper/render/renderers/chart-bar.js +70 -0
  114. package/dist/modules/apimapper/render/renderers/chart-bar.js.map +1 -0
  115. package/dist/modules/apimapper/render/renderers/chart-line.d.ts +2 -0
  116. package/dist/modules/apimapper/render/renderers/chart-line.js +71 -0
  117. package/dist/modules/apimapper/render/renderers/chart-line.js.map +1 -0
  118. package/dist/modules/apimapper/render/renderers/diff.d.ts +2 -0
  119. package/dist/modules/apimapper/render/renderers/diff.js +154 -0
  120. package/dist/modules/apimapper/render/renderers/diff.js.map +1 -0
  121. package/dist/modules/apimapper/render/renderers/flow-diagram.d.ts +1 -0
  122. package/dist/modules/apimapper/render/renderers/flow-diagram.js +180 -0
  123. package/dist/modules/apimapper/render/renderers/flow-diagram.js.map +1 -0
  124. package/dist/modules/apimapper/render/renderers/json-tree.d.ts +2 -0
  125. package/dist/modules/apimapper/render/renderers/json-tree.js +87 -0
  126. package/dist/modules/apimapper/render/renderers/json-tree.js.map +1 -0
  127. package/dist/modules/apimapper/render/renderers/schema-diagram.d.ts +2 -0
  128. package/dist/modules/apimapper/render/renderers/schema-diagram.js +83 -0
  129. package/dist/modules/apimapper/render/renderers/schema-diagram.js.map +1 -0
  130. package/dist/modules/apimapper/render/renderers/table.d.ts +2 -0
  131. package/dist/modules/apimapper/render/renderers/table.js +75 -0
  132. package/dist/modules/apimapper/render/renderers/table.js.map +1 -0
  133. package/dist/modules/apimapper/render/schemas.d.ts +23 -0
  134. package/dist/modules/apimapper/render/schemas.js +56 -0
  135. package/dist/modules/apimapper/render/schemas.js.map +1 -0
  136. package/dist/modules/apimapper/render/secret-masking.d.ts +5 -0
  137. package/dist/modules/apimapper/render/secret-masking.js +51 -0
  138. package/dist/modules/apimapper/render/secret-masking.js.map +1 -0
  139. package/dist/modules/apimapper/render/sidecar.d.ts +21 -0
  140. package/dist/modules/apimapper/render/sidecar.js +66 -0
  141. package/dist/modules/apimapper/render/sidecar.js.map +1 -0
  142. package/dist/modules/apimapper/render/token-cap.d.ts +21 -0
  143. package/dist/modules/apimapper/render/token-cap.js +57 -0
  144. package/dist/modules/apimapper/render/token-cap.js.map +1 -0
  145. package/dist/modules/apimapper/schema.d.ts +2 -2
  146. package/dist/modules/apimapper/schema.js +92 -33
  147. package/dist/modules/apimapper/schema.js.map +1 -1
  148. package/dist/modules/apimapper/settings-format.d.ts +23 -0
  149. package/dist/modules/apimapper/settings-format.js +135 -0
  150. package/dist/modules/apimapper/settings-format.js.map +1 -0
  151. package/dist/modules/apimapper/settings.d.ts +2 -2
  152. package/dist/modules/apimapper/settings.js +100 -42
  153. package/dist/modules/apimapper/settings.js.map +1 -1
  154. package/dist/modules/apimapper/sites-tools.d.ts +29 -0
  155. package/dist/modules/apimapper/sites-tools.js +165 -0
  156. package/dist/modules/apimapper/sites-tools.js.map +1 -0
  157. package/dist/modules/apimapper/skill-resources.d.ts +2 -2
  158. package/dist/modules/apimapper/skill-resources.js.map +1 -1
  159. package/dist/modules/apimapper/token-baseline.harness.d.ts +91 -0
  160. package/dist/modules/apimapper/token-baseline.harness.js +291 -0
  161. package/dist/modules/apimapper/token-baseline.harness.js.map +1 -0
  162. package/dist/modules/apimapper/tool-result.d.ts +46 -0
  163. package/dist/modules/apimapper/tool-result.js +63 -0
  164. package/dist/modules/apimapper/tool-result.js.map +1 -0
  165. package/dist/modules/apimapper/toolslist-size.d.ts +56 -0
  166. package/dist/modules/apimapper/toolslist-size.js +192 -0
  167. package/dist/modules/apimapper/toolslist-size.js.map +1 -0
  168. package/dist/modules/apimapper/types.d.ts +44 -8
  169. package/dist/modules/apimapper/types.js +26 -1
  170. package/dist/modules/apimapper/types.js.map +1 -1
  171. package/dist/modules/apimapper/use-profile.d.ts +21 -0
  172. package/dist/modules/apimapper/use-profile.js +56 -2
  173. package/dist/modules/apimapper/use-profile.js.map +1 -1
  174. package/dist/modules/apimapper/whitelist-drift.d.ts +85 -0
  175. package/dist/modules/apimapper/whitelist-drift.js +360 -0
  176. package/dist/modules/apimapper/whitelist-drift.js.map +1 -0
  177. package/dist/modules/apimapper/workflows.d.ts +2 -2
  178. package/dist/modules/apimapper/workflows.js +202 -20
  179. package/dist/modules/apimapper/workflows.js.map +1 -1
  180. package/dist/modules/apimapper/yootheme-binding.d.ts +35 -0
  181. package/dist/modules/apimapper/yootheme-binding.js +186 -0
  182. package/dist/modules/apimapper/yootheme-binding.js.map +1 -0
  183. package/dist/platform/index.d.ts +56 -0
  184. package/dist/platform/index.js +195 -7
  185. package/dist/platform/index.js.map +1 -1
  186. package/dist/setup/detect-clients.d.ts +40 -1
  187. package/dist/setup/detect-clients.js +148 -1
  188. package/dist/setup/detect-clients.js.map +1 -1
  189. package/dist/setup/probe-handshake.js +40 -7
  190. package/dist/setup/probe-handshake.js.map +1 -1
  191. package/dist/setup/remove-config.d.ts +8 -0
  192. package/dist/setup/remove-config.js +145 -0
  193. package/dist/setup/remove-config.js.map +1 -0
  194. package/dist/setup/uninstall.d.ts +34 -0
  195. package/dist/setup/uninstall.js +147 -0
  196. package/dist/setup/uninstall.js.map +1 -0
  197. package/dist/setup-cli.d.ts +60 -0
  198. package/dist/setup-cli.js +155 -5
  199. package/dist/setup-cli.js.map +1 -1
  200. package/dist/sites/loader.d.ts +41 -0
  201. package/dist/sites/loader.js +119 -0
  202. package/dist/sites/loader.js.map +1 -0
  203. package/dist/sites/schema.d.ts +69 -0
  204. package/dist/sites/schema.js +71 -0
  205. package/dist/sites/schema.js.map +1 -0
  206. package/dist/sites/secret-resolver.d.ts +47 -0
  207. package/dist/sites/secret-resolver.js +150 -0
  208. package/dist/sites/secret-resolver.js.map +1 -0
  209. package/dist/skill-instructions.d.ts +1 -1
  210. package/dist/skill-instructions.js +5 -0
  211. package/dist/skill-instructions.js.map +1 -1
  212. package/dist/transports/stdio.js +4 -4
  213. package/dist/transports/stdio.js.map +1 -1
  214. package/dist/uninstall-skill.d.ts +27 -0
  215. package/dist/uninstall-skill.js +89 -0
  216. package/dist/uninstall-skill.js.map +1 -0
  217. package/docs/architecture.md +22 -22
  218. package/docs/customgraph-internal-migration.md +4 -4
  219. package/docs/security.md +2 -21
  220. package/docs/tools.md +40 -12
  221. package/manifest.json +77 -70
  222. package/package.json +68 -60
  223. package/skills/apimapper/SKILL.md +53 -7
  224. package/skills/apimapper/reference/conditional-style-multi-items.md +114 -0
  225. package/skills/apimapper/reference/jmespath-pitfalls.md +108 -0
  226. package/skills/apimapper/reference/joomla.md +1 -1
  227. package/skills/apimapper/reference/library-template-discovery.md +65 -0
  228. package/skills/apimapper/reference/merge-two-sources-on-key.md +99 -0
  229. package/skills/apimapper/reference/render.md +132 -0
  230. package/skills/apimapper/reference/troubleshooting.md +21 -1
  231. package/skills/apimapper/reference/yootheme.md +1 -1
  232. package/dist/auth/oauth-provider.d.ts +0 -68
  233. package/dist/auth/oauth-provider.js +0 -232
  234. package/dist/auth/oauth-provider.js.map +0 -1
  235. package/dist/server-http.d.ts +0 -22
  236. package/dist/server-http.js +0 -159
  237. package/dist/server-http.js.map +0 -1
  238. package/dist/transports/http.d.ts +0 -29
  239. package/dist/transports/http.js +0 -267
  240. package/dist/transports/http.js.map +0 -1
@@ -1,5 +1,27 @@
1
1
  export declare const READ_CACHE_TTL_MS = 60000;
2
2
  export declare const READ_CACHE_MAX_ENTRIES = 200;
3
+ /**
4
+ * Per-entry opts that influence the cache key. Currently only `sanitize`
5
+ * but kept as an object so future flags (e.g. shape variants) can be added
6
+ * without breaking the existing call-sites.
7
+ */
8
+ export interface CacheKeyOpts {
9
+ /**
10
+ * When the request will run its response through `sanitizeSecrets()`,
11
+ * the cached payload looks materially different (redacted) vs the raw
12
+ * variant. Without this bit in the key, two callers with different
13
+ * `sanitize` flags would see each other's responses — a Defense-in-Depth
14
+ * gap (F-02, A5). Default = false (raw) preserves prior behaviour.
15
+ *
16
+ * Contract: `undefined` and explicit `false` are intentionally equivalent
17
+ * (both map to the `R` (raw) cache slot — see read-cache.test.ts
18
+ * "default behaviour (no flag) treats as sanitize=false" for the pinned
19
+ * contract). Only literal `true` selects the `S` (sanitized) slot —
20
+ * non-boolean truthy values (e.g. `1`, `"yes"`) map to `R` per strict-
21
+ * equality guard in `cacheKey()`.
22
+ */
23
+ sanitize?: boolean;
24
+ }
3
25
  /**
4
26
  * Is the request eligible for caching? GET only, and the path must
5
27
  * match at least one allowlist prefix. We compare against the path-
@@ -10,15 +32,22 @@ export declare function isCacheableRequest(method: string, url: string): boolean
10
32
  /**
11
33
  * Retrieve a cached entry. Returns undefined on miss or expired entry —
12
34
  * expired entries are also evicted to keep the store small.
35
+ *
36
+ * The optional `opts.sanitize` bit must match the value used at `setCached`
37
+ * time, otherwise this is a miss (intentional — sanitized and raw variants
38
+ * are isolated, see F-02).
13
39
  */
14
- export declare function getCached<T = unknown>(method: string, url: string): T | undefined;
40
+ export declare function getCached<T = unknown>(method: string, url: string, opts?: CacheKeyOpts): T | undefined;
15
41
  /**
16
42
  * Store an entry. Caps the store at READ_CACHE_MAX_ENTRIES — when full,
17
43
  * we drop the OLDEST entry first (by storedAt). This is FIFO rather than
18
44
  * LRU; LRU would require an access-time update on every read which is
19
45
  * not worth it at 200-entry scale.
46
+ *
47
+ * The optional `opts.sanitize` bit becomes part of the cache key so the
48
+ * sanitized variant cannot leak into a raw-mode call (F-02, A5 D-i-D).
20
49
  */
21
- export declare function setCached<T = unknown>(method: string, url: string, data: T): void;
50
+ export declare function setCached<T = unknown>(method: string, url: string, data: T, opts?: CacheKeyOpts): void;
22
51
  /**
23
52
  * Invalidate cache entries whose request path overlaps with the mutated
24
53
  * resource. Conservative — clears every entry whose key includes the
@@ -66,9 +66,16 @@ const store = new Map();
66
66
  * The HTTP method is part of the key only for safety — non-GET will
67
67
  * never reach here, but we don't want a future caller to accidentally
68
68
  * cache a POST against the same path.
69
+ *
70
+ * F-02 (2026-05-19): a sanitize-bit (`S` sanitized / `R` raw) is appended
71
+ * so sanitized and raw variants of the same URL occupy different slots.
72
+ * Strict-equality (`=== true`) makes the intent self-documenting and
73
+ * prevents silent widening if a JS caller passes a non-boolean truthy
74
+ * value (e.g. `1`, `"yes"`) — those map to the `R` slot.
69
75
  */
70
- function cacheKey(method, url) {
71
- return `${method.toUpperCase()}::${url}`;
76
+ function cacheKey(method, url, opts) {
77
+ const sanitizeBit = opts?.sanitize === true ? "S" : "R";
78
+ return `${method.toUpperCase()}::${sanitizeBit}::${url}`;
72
79
  }
73
80
  /**
74
81
  * Is the request eligible for caching? GET only, and the path must
@@ -91,9 +98,13 @@ export function isCacheableRequest(method, url) {
91
98
  /**
92
99
  * Retrieve a cached entry. Returns undefined on miss or expired entry —
93
100
  * expired entries are also evicted to keep the store small.
101
+ *
102
+ * The optional `opts.sanitize` bit must match the value used at `setCached`
103
+ * time, otherwise this is a miss (intentional — sanitized and raw variants
104
+ * are isolated, see F-02).
94
105
  */
95
- export function getCached(method, url) {
96
- const key = cacheKey(method, url);
106
+ export function getCached(method, url, opts) {
107
+ const key = cacheKey(method, url, opts);
97
108
  const entry = store.get(key);
98
109
  if (!entry)
99
110
  return undefined;
@@ -108,8 +119,11 @@ export function getCached(method, url) {
108
119
  * we drop the OLDEST entry first (by storedAt). This is FIFO rather than
109
120
  * LRU; LRU would require an access-time update on every read which is
110
121
  * not worth it at 200-entry scale.
122
+ *
123
+ * The optional `opts.sanitize` bit becomes part of the cache key so the
124
+ * sanitized variant cannot leak into a raw-mode call (F-02, A5 D-i-D).
111
125
  */
112
- export function setCached(method, url, data) {
126
+ export function setCached(method, url, data, opts) {
113
127
  if (store.size >= READ_CACHE_MAX_ENTRIES) {
114
128
  let oldestKey;
115
129
  let oldestTime = Infinity;
@@ -123,7 +137,7 @@ export function setCached(method, url, data) {
123
137
  store.delete(oldestKey);
124
138
  }
125
139
  const now = Date.now();
126
- store.set(cacheKey(method, url), {
140
+ store.set(cacheKey(method, url, opts), {
127
141
  data,
128
142
  expiresAt: now + READ_CACHE_TTL_MS,
129
143
  storedAt: now,
@@ -1 +1 @@
1
- {"version":3,"file":"read-cache.js","sourceRoot":"","sources":["../../../src/modules/apimapper/read-cache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"read-cache.js","sourceRoot":"","sources":["../../../src/modules/apimapper/read-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /** Counts user-perceived characters (graphemes), not UTF-16 code units. */
2
+ export declare function graphemeLength(s: string): number;
3
+ /** Right-pad with spaces until the string reaches `width` graphemes. */
4
+ export declare function padRight(s: string, width: number): string;
5
+ /** Left-pad with spaces until the string reaches `width` graphemes. */
6
+ export declare function padLeft(s: string, width: number): string;
7
+ /**
8
+ * Truncate to at most `maxGraphemes` graphemes, appending "…" when cut.
9
+ * Reserves 1 grapheme for the ellipsis itself, so the returned string is
10
+ * guaranteed never to exceed `maxGraphemes` in grapheme count.
11
+ */
12
+ export declare function truncate(s: string, maxGraphemes: number): string;
13
+ /**
14
+ * Draw an ASCII box around `lines`.
15
+ *
16
+ * Width contract:
17
+ * - If `width` is supplied, every returned line has exactly `width`
18
+ * graphemes (top/bottom = ─-fill, content = "│ <text> │" right-padded).
19
+ * - If omitted, width = max content grapheme length + 4 (2 padding chars
20
+ * on each side including borders).
21
+ *
22
+ * Content lines longer than the inside-width are truncated with `…`.
23
+ */
24
+ export declare function box(lines: string[], width?: number): string;
@@ -0,0 +1,84 @@
1
+ // _shared.ts — Grapheme-safe text helpers shared by every renderer.
2
+ //
3
+ // Critical invariant: `box()` returns lines whose grapheme-width equals the
4
+ // requested (or auto-derived) width. Renderers join multiple boxes
5
+ // horizontally and pad blocks vertically — every consumer assumes uniform
6
+ // box width. A 1-char drift in any line breaks horizontal alignment for
7
+ // every downstream layer.
8
+ //
9
+ // Why Intl.Segmenter: JavaScript's String#length counts UTF-16 code units,
10
+ // so "🇩🇪".length === 4 (two surrogate pairs) even though the user sees one
11
+ // glyph. We use Intl.Segmenter with granularity:"grapheme" to count what
12
+ // the user actually sees. This matters for connection labels that may
13
+ // contain emoji / CJK / right-to-left text and for the eventual
14
+ // non-ASCII flow names users will throw at us.
15
+ const segmenter = new Intl.Segmenter("en", { granularity: "grapheme" });
16
+ /** Counts user-perceived characters (graphemes), not UTF-16 code units. */
17
+ export function graphemeLength(s) {
18
+ let n = 0;
19
+ // Intl.Segmenter always yields truthy segment objects — no guard needed.
20
+ for (const _segment of segmenter.segment(s))
21
+ n++;
22
+ return n;
23
+ }
24
+ /** Right-pad with spaces until the string reaches `width` graphemes. */
25
+ export function padRight(s, width) {
26
+ const len = graphemeLength(s);
27
+ return len >= width ? s : s + " ".repeat(width - len);
28
+ }
29
+ /** Left-pad with spaces until the string reaches `width` graphemes. */
30
+ export function padLeft(s, width) {
31
+ const len = graphemeLength(s);
32
+ return len >= width ? s : " ".repeat(width - len) + s;
33
+ }
34
+ /**
35
+ * Truncate to at most `maxGraphemes` graphemes, appending "…" when cut.
36
+ * Reserves 1 grapheme for the ellipsis itself, so the returned string is
37
+ * guaranteed never to exceed `maxGraphemes` in grapheme count.
38
+ */
39
+ export function truncate(s, maxGraphemes) {
40
+ if (graphemeLength(s) <= maxGraphemes)
41
+ return s;
42
+ if (maxGraphemes <= 0)
43
+ return "";
44
+ if (maxGraphemes === 1)
45
+ return "…";
46
+ const room = maxGraphemes - 1;
47
+ let out = "";
48
+ let count = 0;
49
+ for (const seg of segmenter.segment(s)) {
50
+ if (count >= room)
51
+ break;
52
+ out += seg.segment;
53
+ count++;
54
+ }
55
+ return out + "…";
56
+ }
57
+ /**
58
+ * Draw an ASCII box around `lines`.
59
+ *
60
+ * Width contract:
61
+ * - If `width` is supplied, every returned line has exactly `width`
62
+ * graphemes (top/bottom = ─-fill, content = "│ <text> │" right-padded).
63
+ * - If omitted, width = max content grapheme length + 4 (2 padding chars
64
+ * on each side including borders).
65
+ *
66
+ * Content lines longer than the inside-width are truncated with `…`.
67
+ */
68
+ export function box(lines, width) {
69
+ const computedWidth = width ??
70
+ Math.max(...lines.map((l) => graphemeLength(l) + 4), 5);
71
+ const inside = computedWidth - 2; // space between the two side borders
72
+ const top = "┌" + "─".repeat(inside) + "┐";
73
+ const bot = "└" + "─".repeat(inside) + "┘";
74
+ // Each content row: "│" + " " + <truncated content> + right-pad + "│"
75
+ // We reserve 2 grapheme slots inside (one leading space, one trailing
76
+ // pad-or-space) so content max width = inside - 2.
77
+ const contentMax = Math.max(0, inside - 2);
78
+ const middle = lines.map((l) => {
79
+ const safe = truncate(l, contentMax);
80
+ return "│" + padRight(" " + safe, inside) + "│";
81
+ });
82
+ return [top, ...middle, bot].join("\n");
83
+ }
84
+ //# sourceMappingURL=_shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_shared.js","sourceRoot":"","sources":["../../../../src/modules/apimapper/render/_shared.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export interface DagNode {
2
+ id: string;
3
+ }
4
+ export interface DagEdge {
5
+ source: string;
6
+ target: string;
7
+ }
8
+ export interface TopoResult {
9
+ /** Node ids grouped by topological layer (depth from any source). */
10
+ layers: string[][];
11
+ /** True iff cycle detected — some nodes never reached in-degree 0. */
12
+ cycle: boolean;
13
+ /** Node ids that were never emitted (cycle members or orphans behind one). */
14
+ unreachable: string[];
15
+ }
16
+ export declare function topoLayers(nodes: DagNode[], edges: DagEdge[]): TopoResult;
17
+ /** Convenience predicate. Equivalent to `topoLayers(...).cycle`. */
18
+ export declare function detectCycle(nodes: DagNode[], edges: DagEdge[]): boolean;
@@ -0,0 +1,70 @@
1
+ // dag.ts — Topological layering + cycle detection for the flow-diagram
2
+ // renderer.
3
+ //
4
+ // Algorithm: Kahn's BFS. Start with nodes of in-degree 0 (sources), publish
5
+ // them as layer 0, decrement in-degrees of their successors, advance.
6
+ //
7
+ // A graph contains a cycle iff after the BFS terminates some nodes remain
8
+ // with non-zero in-degree (Kahn's theorem). We expose the surviving set as
9
+ // `unreachable` so the renderer can still draw cyclic nodes — appended as a
10
+ // trailing layer, with a footer warning — rather than silently dropping them.
11
+ //
12
+ // Edges pointing at or from unknown node ids are silently ignored: the
13
+ // fixture/JSON validator on the renderer side already rejects malformed
14
+ // flows; this helper stays defensive and never throws on stray edges.
15
+ export function topoLayers(nodes, edges) {
16
+ const ids = nodes.map((n) => n.id);
17
+ const idSet = new Set(ids);
18
+ const inDeg = new Map();
19
+ const adj = new Map();
20
+ for (const id of ids) {
21
+ inDeg.set(id, 0);
22
+ adj.set(id, []);
23
+ }
24
+ for (const e of edges) {
25
+ if (!idSet.has(e.source) || !idSet.has(e.target))
26
+ continue;
27
+ // `e.target` passed the idSet guard above, so inDeg always holds it —
28
+ // the `?? 0` is unreachable defensive code.
29
+ inDeg.set(e.target,
30
+ /* v8 ignore next -- e.target is in idSet (guarded), inDeg seeded for every id */
31
+ (inDeg.get(e.target) ?? 0) + 1);
32
+ const out = adj.get(e.source);
33
+ if (out)
34
+ out.push(e.target);
35
+ }
36
+ const layers = [];
37
+ let frontier = ids.filter(
38
+ /* v8 ignore next -- id comes from `ids`, inDeg seeded for every id above */
39
+ (id) => (inDeg.get(id) ?? 0) === 0);
40
+ const visited = new Set();
41
+ // in-degrees are consumed in-place as the frontier advances (map is
42
+ // function-local, safe)
43
+ while (frontier.length > 0) {
44
+ const layer = [...frontier];
45
+ for (const id of layer)
46
+ visited.add(id);
47
+ layers.push(layer);
48
+ const next = [];
49
+ for (const id of frontier) {
50
+ // `id` is a known node id, `n` an out-neighbour of a known id — both
51
+ // adj and inDeg are seeded for every id, so the fallbacks never fire.
52
+ /* v8 ignore next -- adj seeded for every id; fallback unreachable */
53
+ for (const n of adj.get(id) ?? []) {
54
+ /* v8 ignore next -- inDeg seeded for every id; fallback unreachable */
55
+ const remaining = (inDeg.get(n) ?? 0) - 1;
56
+ inDeg.set(n, remaining);
57
+ if (remaining === 0)
58
+ next.push(n);
59
+ }
60
+ }
61
+ frontier = next;
62
+ }
63
+ const unreachable = ids.filter((id) => !visited.has(id));
64
+ return { layers, cycle: unreachable.length > 0, unreachable };
65
+ }
66
+ /** Convenience predicate. Equivalent to `topoLayers(...).cycle`. */
67
+ export function detectCycle(nodes, edges) {
68
+ return topoLayers(nodes, edges).cycle;
69
+ }
70
+ //# sourceMappingURL=dag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dag.js","sourceRoot":"","sources":["../../../../src/modules/apimapper/render/dag.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { ToolRegistrar } from "../gateway/capturing-server.js";
2
+ export declare function registerRenderTool(server: ToolRegistrar): void;
@@ -0,0 +1,112 @@
1
+ import { z } from "zod";
2
+ import { formatResult, readOnly } from "@getimo/mcp-toolkit";
3
+ import { RENDER_TYPES, dispatchRenderer } from "./schemas.js";
4
+ import { capRendered, hintFor, HARD_CAP, RESERVE } from "./token-cap.js";
5
+ // Per-type recovery hint for the handler's catch-all error envelope.
6
+ // Sibling tools (flows.ts, graph.ts) attach a `hint` so the agent knows
7
+ // what shape to pass / which tool to fetch with — this gives apimapper_render
8
+ // the same affordance when a renderer's Zod schema rejects the payload.
9
+ const RENDER_ERROR_HINTS = {
10
+ table: "type:'table' expects an array of objects — fetch with " +
11
+ "apimapper_connection_data or apimapper_graph_preview.",
12
+ "chart-bar": "type:'chart-bar' expects { labels: string[], values: number[] } of equal length.",
13
+ "chart-line": "type:'chart-line' expects { labels: string[], values: number[] } of equal length.",
14
+ "schema-diagram": "type:'schema-diagram' expects a schema profile with fields[] — " +
15
+ "fetch with apimapper_schema_profile.",
16
+ "flow-diagram": "type:'flow-diagram' expects a flow object with nodes[]+edges[] — " +
17
+ "fetch with apimapper_flow_get.",
18
+ "json-tree": "type:'json-tree' accepts any JSON value — pass the fetched payload directly.",
19
+ diff: "type:'diff' expects { old, new } — fetch both datasets first " +
20
+ "(e.g. two apimapper_schema_profile calls).",
21
+ };
22
+ // MCP CallToolResult outer guard. DERIVED from token-cap.ts's HARD_CAP +
23
+ // RESERVE so the relationship is compiler-enforced — bumping HARD_CAP in
24
+ // token-cap.ts automatically widens this guard, no hand-audit needed.
25
+ // The extra +500 is belt-and-suspenders slack: capRendered already bounds
26
+ // the byte budget to (HARD_CAP - RESERVE) of content plus a short footer,
27
+ // so HARD_CAP + RESERVE already covers it; the +500 absorbs any future
28
+ // footer-wording growth without re-touching this call-site.
29
+ const TOOL_RESULT_MAX_CHARS = HARD_CAP + RESERVE + 500;
30
+ export function registerRenderTool(server) {
31
+ server.registerTool("apimapper_render", {
32
+ title: "Render Data",
33
+ description:
34
+ // F-LS-08a (W3.1 Stage 3): imperative trigger opener. Live-smoke
35
+ // (2026-05-20) showed callers composing ASCII tables in their own
36
+ // response when the user said "show as a table" instead of calling
37
+ // apimapper_render. Stating the trigger keywords up-front + the
38
+ // "prefer this over manually composing ASCII art" nudge (carefully
39
+ // avoiding the baseline-not-ceiling exclusion pin — see render-
40
+ // dispatcher.test.ts) routes the LLM to this tool.
41
+ "USE THIS when the user asks to 'visualize', 'show as diagram', " +
42
+ "'render', 'draw', or 'display as a table/chart/flow' — produces " +
43
+ "deterministic, pre-formatted text output (tables, flow-diagrams, " +
44
+ "charts, schema-trees, json-tree, diffs). " +
45
+ "Prefer this over manually composing ASCII art in your response: " +
46
+ "the output is drift-free and consistent across renders.\n\n" +
47
+ "Render data as a ready-to-display text visualization (tables, " +
48
+ "diagrams, trees, charts, diffs). Returns text the user can read " +
49
+ "directly in any MCP client.\n\n" +
50
+ "Prerequisite: fetch the data first with the appropriate source tool, " +
51
+ "then pass the result as 'data':\n" +
52
+ " - type:'table' | 'chart-bar' | 'chart-line' | 'json-tree'\n" +
53
+ " → use apimapper_connection_data / apimapper_graph_preview /\n" +
54
+ " apimapper_flow_trace first\n" +
55
+ " - type:'flow-diagram' → use apimapper_flow_get first; pass the flow object\n" +
56
+ " - type:'schema-diagram' → use apimapper_schema_profile first\n" +
57
+ " - type:'diff' → fetch both datasets, pass as { old: ..., new: ... }\n\n" +
58
+ "Pure function: deterministic, no network, no side-effects. The " +
59
+ "returned text is ready to show as-is. You are also free to enrich " +
60
+ "it however your client allows — artifacts, inline images, " +
61
+ "diagram-markdown, charts — whatever renders best for the user.\n\n" +
62
+ "Example:\n apimapper_render({ type: 'flow-diagram', data: flowObject })",
63
+ inputSchema: {
64
+ type: z.enum(RENDER_TYPES).describe("Visualization type"),
65
+ data: z.unknown().describe("Data to render. Shape depends on type — see description."),
66
+ options: z.object({
67
+ title: z.string().max(200).optional()
68
+ .describe("Optional title shown above the visualization"),
69
+ max_rows: z.number().int().min(1).max(500).default(50).optional()
70
+ .describe("Cap rows/items (table, json-tree). Default 50."),
71
+ columns: z.array(z.string()).max(20).optional()
72
+ .describe("Field whitelist for table. Default: auto-detect."),
73
+ depth: z.number().int().min(1).max(8).default(3).optional()
74
+ .describe("Depth cap for json-tree. Default 3."),
75
+ mask_secrets: z.boolean().default(true).optional()
76
+ .describe("Mask values for sensitive field names in diff. Default true."),
77
+ }).optional(),
78
+ },
79
+ annotations: readOnly({
80
+ title: "Render Data",
81
+ openWorld: false,
82
+ }),
83
+ }, async ({ type, data, options }) => {
84
+ // `data` is z.unknown() at the tool boundary — per-type validation
85
+ // happens inside dispatchRenderer via each renderer's inner Zod schema.
86
+ // This keeps the MCP tool surface a flat object (no discriminatedUnion
87
+ // in inputSchema) while still enforcing strict shape per type.
88
+ try {
89
+ const text = dispatchRenderer(type, data, options ?? {});
90
+ const capped = capRendered(text, hintFor(type));
91
+ return formatResult(capped, false, { maxChars: TOOL_RESULT_MAX_CHARS });
92
+ }
93
+ catch (err) {
94
+ // Renderers reject bad input by throwing a ZodError (a subclass of
95
+ // Error), so `err` is always an Error here. The string fallback arm
96
+ // is unreachable defensive code against a non-Error throw.
97
+ /* v8 ignore next 3 -- renderers always throw Error (ZodError); false arm dead */
98
+ const msg = err instanceof Error ? err.message : "Render failed";
99
+ // Defense (A5-04): cap the echoed error message. A renderer's Zod
100
+ // error can quote large fragments of the rejected `data` payload —
101
+ // capping at 500 chars avoids echoing oversized or sensitive input
102
+ // back into the error envelope.
103
+ const safeMsg = msg.length > 500 ? msg.slice(0, 500) + "…" : msg;
104
+ // Attach a per-type recovery hint — same affordance the flows.ts /
105
+ // graph.ts error envelopes provide. `type` is already validated
106
+ // against RENDER_TYPES at the tool boundary, so the lookup always
107
+ // resolves.
108
+ return formatResult({ error: safeMsg, type, hint: RENDER_ERROR_HINTS[type] }, true);
109
+ }
110
+ });
111
+ }
112
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/modules/apimapper/render/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { RenderOptions } from "../schemas.js";
2
+ export declare function renderChartBar(input: unknown, options: RenderOptions): string;
@@ -0,0 +1,70 @@
1
+ // renderers/chart-bar.ts — labels+values → horizontal `█` bar chart.
2
+ //
3
+ // Input is a parallel `{ labels, values }` pair (same length, enforced by a
4
+ // Zod refine). Each row is "<label> │ <bar> <value>" where the bar is a run
5
+ // of full-block characters scaled so the largest value fills BAR_WIDTH.
6
+ //
7
+ // Layout invariant: every label is right-padded to the widest label's
8
+ // grapheme width so the " │ " separators line up across all rows. The bar
9
+ // itself is right-padded too, so the value column stays aligned even when
10
+ // shorter bars leave trailing space.
11
+ import { z } from "zod";
12
+ import { padRight, graphemeLength } from "../_shared.js";
13
+ const ChartBarSchema = z
14
+ .object({
15
+ labels: z
16
+ .array(z.string())
17
+ .min(1)
18
+ .max(20)
19
+ .describe("Bar labels — parallel to values, same length (1-20)."),
20
+ values: z
21
+ .array(z.number())
22
+ .min(1)
23
+ .max(20)
24
+ .describe("Numeric bar values — parallel to labels, same length."),
25
+ unit: z.string().max(20).optional(),
26
+ title: z.string().max(200).optional(),
27
+ })
28
+ .refine((d) => d.labels.length === d.values.length, {
29
+ message: "labels and values must have the same length",
30
+ });
31
+ /**
32
+ * Width (in `█` chars) of a bar representing the maximum value.
33
+ * Independent of chart-line's MIN_RULE_WIDTH — coincidental equal value.
34
+ */
35
+ const BAR_WIDTH = 40;
36
+ /** Minimum label-column width so very short labels still read as a column. */
37
+ const MIN_LABEL_WIDTH = 5;
38
+ /**
39
+ * Extra rule width beyond `labelWidth + BAR_WIDTH`: accounts for the
40
+ * " │ " separator (3) + the two-space gap before the value (2) + slack
41
+ * for the value column itself, so the rule spans the whole row.
42
+ */
43
+ const RULE_SLACK = 10;
44
+ export function renderChartBar(input, options) {
45
+ const { labels, values, unit, title } = ChartBarSchema.parse(input);
46
+ // `Math.max(..., 1)` guards an all-zero dataset against a 0/0 division.
47
+ const max = Math.max(...values, 1);
48
+ const labelWidth = Math.max(...labels.map(graphemeLength), MIN_LABEL_WIDTH);
49
+ const lines = [];
50
+ // `options.title` wins over an inline `title` field; both are optional.
51
+ const heading = options.title ?? title;
52
+ if (heading)
53
+ lines.push(heading);
54
+ lines.push("─".repeat(labelWidth + BAR_WIDTH + RULE_SLACK));
55
+ labels.forEach((label, i) => {
56
+ const value = values[i];
57
+ const barLen = Math.floor((value / max) * BAR_WIDTH);
58
+ const bar = "█".repeat(barLen);
59
+ const valueStr = unit ? `${value} ${unit}` : String(value);
60
+ lines.push(padRight(label, labelWidth) +
61
+ " │ " +
62
+ padRight(bar, BAR_WIDTH) +
63
+ " " +
64
+ valueStr);
65
+ });
66
+ lines.push("─".repeat(labelWidth + BAR_WIDTH + RULE_SLACK));
67
+ lines.push(`${labels.length} series • max=${max}`);
68
+ return lines.join("\n");
69
+ }
70
+ //# sourceMappingURL=chart-bar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-bar.js","sourceRoot":"","sources":["../../../../../src/modules/apimapper/render/renderers/chart-bar.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { RenderOptions } from "../schemas.js";
2
+ export declare function renderChartLine(input: unknown, options: RenderOptions): string;
@@ -0,0 +1,71 @@
1
+ // renderers/chart-line.ts — labels+values → single-row sparkline.
2
+ //
3
+ // Input is a parallel `{ labels, values }` pair (same length, enforced by a
4
+ // Zod refine, up to 64 points). Each value is mapped onto one of eight
5
+ // block-element characters ▁▂▃▄▅▆▇█ scaled across the [min, max] range, and
6
+ // the chars are concatenated into a one-line dense curve.
7
+ //
8
+ // CRITICAL flat-line guard: when every value is equal, `max - min === 0`.
9
+ // `range || 1` substitutes 1 so the division never produces NaN/Infinity —
10
+ // a flat series collapses to a repeated bottom block, which is correct.
11
+ //
12
+ // The block-element chars are plain Unicode (U+2581..U+2588), not Braille;
13
+ // they render at a fixed single-cell width in every terminal and MCP client.
14
+ import { z } from "zod";
15
+ const ChartLineSchema = z
16
+ .object({
17
+ labels: z
18
+ .array(z.string())
19
+ .min(1)
20
+ .max(64)
21
+ .describe("Point labels — parallel to values, same length (1-64)."),
22
+ values: z
23
+ .array(z.number())
24
+ .min(1)
25
+ .max(64)
26
+ .describe("Numeric point values — parallel to labels, same length."),
27
+ unit: z.string().max(20).optional(),
28
+ title: z.string().max(200).optional(),
29
+ })
30
+ .refine((d) => d.labels.length === d.values.length, {
31
+ message: "labels and values must have the same length",
32
+ });
33
+ /** Block-element ramp from lowest (▁) to highest (█). */
34
+ const SPARK_CHARS = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"];
35
+ /**
36
+ * Minimum width of the horizontal rules above and below the sparkline.
37
+ * Independent of chart-bar's BAR_WIDTH — coincidental equal value.
38
+ */
39
+ const MIN_RULE_WIDTH = 40;
40
+ export function renderChartLine(input, options) {
41
+ const { labels, values, unit, title } = ChartLineSchema.parse(input);
42
+ const min = Math.min(...values);
43
+ const max = Math.max(...values);
44
+ // Flat-line guard: a zero range would make every (v-min)/range a 0/0 → NaN.
45
+ const range = max - min || 1;
46
+ const sparkline = values
47
+ .map((v) => {
48
+ // Clamp the index into [0, 7] — `(v-min)/range` is in [0,1], and
49
+ // exactly 1 would otherwise index SPARK_CHARS.length (out of bounds).
50
+ const idx = Math.min(SPARK_CHARS.length - 1, Math.floor(((v - min) / range) * SPARK_CHARS.length));
51
+ return SPARK_CHARS[idx];
52
+ })
53
+ .join("");
54
+ const ruleWidth = Math.max(MIN_RULE_WIDTH, sparkline.length + 4);
55
+ const unitSuffix = unit ? ` ${unit}` : "";
56
+ const lines = [];
57
+ const heading = options.title ?? title;
58
+ if (heading)
59
+ lines.push(heading);
60
+ lines.push("─".repeat(ruleWidth));
61
+ lines.push(" " + sparkline);
62
+ // Axis: min label left-aligned, max label right-aligned under the curve.
63
+ const minLabel = `${min}${unitSuffix}`;
64
+ const maxLabel = `${max}${unitSuffix}`;
65
+ const axisGap = Math.max(1, sparkline.length - minLabel.length - maxLabel.length);
66
+ lines.push(" " + minLabel + " ".repeat(axisGap) + maxLabel);
67
+ lines.push("─".repeat(ruleWidth));
68
+ lines.push(`${values.length} data points • range ${min}-${max}${unitSuffix}`);
69
+ return lines.join("\n");
70
+ }
71
+ //# sourceMappingURL=chart-line.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-line.js","sourceRoot":"","sources":["../../../../../src/modules/apimapper/render/renderers/chart-line.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { RenderOptions } from "../schemas.js";
2
+ export declare function renderDiff(input: unknown, options: RenderOptions): string;