@j0hanz/superfetch 2.0.1 → 2.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.
Files changed (79) hide show
  1. package/README.md +120 -38
  2. package/dist/cache.d.ts +42 -0
  3. package/dist/cache.js +565 -0
  4. package/dist/config/env-parsers.d.ts +1 -0
  5. package/dist/config/env-parsers.js +12 -0
  6. package/dist/config/index.d.ts +7 -0
  7. package/dist/config/index.js +10 -3
  8. package/dist/config/types/content.d.ts +1 -0
  9. package/dist/config.d.ts +77 -0
  10. package/dist/config.js +261 -0
  11. package/dist/crypto.d.ts +2 -0
  12. package/dist/crypto.js +32 -0
  13. package/dist/errors.d.ts +10 -0
  14. package/dist/errors.js +28 -0
  15. package/dist/fetch.d.ts +40 -0
  16. package/dist/fetch.js +910 -0
  17. package/dist/http/base-middleware.d.ts +7 -0
  18. package/dist/http/base-middleware.js +143 -0
  19. package/dist/http/cors.d.ts +0 -5
  20. package/dist/http/cors.js +0 -6
  21. package/dist/http/download-routes.js +6 -2
  22. package/dist/http/error-handler.d.ts +2 -0
  23. package/dist/http/error-handler.js +55 -0
  24. package/dist/http/mcp-routes.js +2 -2
  25. package/dist/http/mcp-sessions.d.ts +3 -5
  26. package/dist/http/mcp-sessions.js +8 -8
  27. package/dist/http/server-tuning.d.ts +9 -0
  28. package/dist/http/server-tuning.js +45 -0
  29. package/dist/http/server.d.ts +0 -10
  30. package/dist/http/server.js +33 -333
  31. package/dist/http.d.ts +78 -0
  32. package/dist/http.js +1437 -0
  33. package/dist/index.js +3 -3
  34. package/dist/mcp.d.ts +3 -0
  35. package/dist/mcp.js +94 -0
  36. package/dist/observability.d.ts +16 -0
  37. package/dist/observability.js +78 -0
  38. package/dist/server.js +20 -5
  39. package/dist/services/cache.d.ts +1 -1
  40. package/dist/services/context.d.ts +2 -0
  41. package/dist/services/context.js +3 -0
  42. package/dist/services/extractor.d.ts +1 -0
  43. package/dist/services/extractor.js +28 -2
  44. package/dist/services/fetcher.d.ts +2 -0
  45. package/dist/services/fetcher.js +35 -14
  46. package/dist/services/logger.js +4 -1
  47. package/dist/services/telemetry.d.ts +19 -0
  48. package/dist/services/telemetry.js +43 -0
  49. package/dist/services/transform-worker-pool.d.ts +10 -3
  50. package/dist/services/transform-worker-pool.js +213 -184
  51. package/dist/tools/handlers/fetch-url.tool.js +8 -6
  52. package/dist/tools/index.d.ts +1 -0
  53. package/dist/tools/index.js +13 -1
  54. package/dist/tools/schemas.d.ts +2 -0
  55. package/dist/tools/schemas.js +8 -0
  56. package/dist/tools/utils/content-transform-core.d.ts +5 -0
  57. package/dist/tools/utils/content-transform-core.js +180 -0
  58. package/dist/tools/utils/content-transform-workers.d.ts +1 -0
  59. package/dist/tools/utils/content-transform-workers.js +1 -0
  60. package/dist/tools/utils/content-transform.d.ts +3 -5
  61. package/dist/tools/utils/content-transform.js +35 -148
  62. package/dist/tools/utils/raw-markdown.js +15 -1
  63. package/dist/tools.d.ts +104 -0
  64. package/dist/tools.js +421 -0
  65. package/dist/transform.d.ts +69 -0
  66. package/dist/transform.js +1509 -0
  67. package/dist/transformers/markdown.d.ts +4 -1
  68. package/dist/transformers/markdown.js +182 -53
  69. package/dist/utils/cancellation.d.ts +1 -0
  70. package/dist/utils/cancellation.js +18 -0
  71. package/dist/utils/code-language.d.ts +0 -9
  72. package/dist/utils/code-language.js +5 -5
  73. package/dist/utils/host-normalizer.d.ts +1 -0
  74. package/dist/utils/host-normalizer.js +37 -0
  75. package/dist/utils/url-redactor.d.ts +1 -0
  76. package/dist/utils/url-redactor.js +13 -0
  77. package/dist/utils/url-validator.js +8 -5
  78. package/dist/workers/transform-worker.js +82 -38
  79. package/package.json +8 -7
package/dist/tools.js ADDED
@@ -0,0 +1,421 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { z } from 'zod';
3
+ import * as cache from './cache.js';
4
+ import { config } from './config.js';
5
+ import { FetchError, isSystemError } from './errors.js';
6
+ import { fetchNormalizedUrl, normalizeUrl, transformToRawUrl, } from './fetch.js';
7
+ import { getRequestId, logDebug, logError, runWithRequestContext, } from './observability.js';
8
+ import { transformHtmlToMarkdown, } from './transform.js';
9
+ const TRUNCATION_MARKER = '...[truncated]';
10
+ const fetchUrlInputSchema = z.strictObject({
11
+ url: z.url({ protocol: /^https?$/i }).describe('The URL to fetch'),
12
+ });
13
+ const fetchUrlOutputSchema = z.strictObject({
14
+ url: z.string().describe('The fetched URL'),
15
+ inputUrl: z
16
+ .string()
17
+ .optional()
18
+ .describe('The original URL provided by the caller'),
19
+ resolvedUrl: z
20
+ .string()
21
+ .optional()
22
+ .describe('The normalized or transformed URL that was fetched'),
23
+ title: z.string().optional().describe('Page title'),
24
+ markdown: z
25
+ .string()
26
+ .optional()
27
+ .describe('The extracted content in Markdown format'),
28
+ error: z.string().optional().describe('Error message if the request failed'),
29
+ });
30
+ export const FETCH_URL_TOOL_NAME = 'fetch-url';
31
+ export const FETCH_URL_TOOL_DESCRIPTION = 'Fetches a webpage and converts it to clean Markdown format';
32
+ function isRecord(value) {
33
+ return typeof value === 'object' && value !== null;
34
+ }
35
+ function serializeStructuredContent(structuredContent, fromCache) {
36
+ return JSON.stringify(structuredContent, fromCache ? undefined : null, fromCache ? undefined : 2);
37
+ }
38
+ function buildResourceLink(inlineResult, name) {
39
+ if (!inlineResult.resourceUri) {
40
+ return null;
41
+ }
42
+ const block = {
43
+ type: 'resource_link',
44
+ uri: inlineResult.resourceUri,
45
+ name,
46
+ description: `Content exceeds inline limit (${config.constants.maxInlineContentChars} chars)`,
47
+ };
48
+ if (inlineResult.resourceMimeType !== undefined) {
49
+ block.mimeType = inlineResult.resourceMimeType;
50
+ }
51
+ return block;
52
+ }
53
+ function buildEmbeddedResource(content, url, title) {
54
+ if (!content) {
55
+ return null;
56
+ }
57
+ const filename = cache.generateSafeFilename(url, title, undefined, '.md');
58
+ const uri = `file:///${filename}`;
59
+ return {
60
+ type: 'resource',
61
+ resource: {
62
+ uri,
63
+ mimeType: 'text/markdown',
64
+ text: content,
65
+ },
66
+ };
67
+ }
68
+ function resolveContentToEmbed(inlineResult, fullContent, useInlineInHttpMode) {
69
+ if (useInlineInHttpMode) {
70
+ return inlineResult.content;
71
+ }
72
+ return fullContent ?? inlineResult.content;
73
+ }
74
+ function maybeAppendEmbeddedResource(blocks, contentToEmbed, url, title) {
75
+ if (typeof contentToEmbed !== 'string')
76
+ return;
77
+ if (!url)
78
+ return;
79
+ const embeddedResource = buildEmbeddedResource(contentToEmbed, url, title);
80
+ if (embeddedResource) {
81
+ blocks.push(embeddedResource);
82
+ }
83
+ }
84
+ function maybeAppendResourceLink(blocks, inlineResult, resourceName) {
85
+ const resourceLink = buildResourceLink(inlineResult, resourceName);
86
+ if (resourceLink) {
87
+ blocks.push(resourceLink);
88
+ }
89
+ }
90
+ function buildTextBlock(structuredContent, fromCache) {
91
+ return {
92
+ type: 'text',
93
+ text: serializeStructuredContent(structuredContent, fromCache),
94
+ };
95
+ }
96
+ function buildToolContentBlocks(structuredContent, fromCache, inlineResult, resourceName, cacheKey, fullContent, url, title) {
97
+ const blocks = [
98
+ buildTextBlock(structuredContent, fromCache),
99
+ ];
100
+ const contentToEmbed = resolveContentToEmbed(inlineResult, fullContent, config.runtime.httpMode);
101
+ maybeAppendEmbeddedResource(blocks, contentToEmbed, url, title);
102
+ maybeAppendResourceLink(blocks, inlineResult, resourceName);
103
+ return blocks;
104
+ }
105
+ function applyInlineContentLimit(content, cacheKey) {
106
+ const contentSize = content.length;
107
+ const inlineLimit = config.constants.maxInlineContentChars;
108
+ if (contentSize <= inlineLimit) {
109
+ return { content, contentSize };
110
+ }
111
+ const resourceUri = resolveResourceUri(cacheKey);
112
+ if (!resourceUri) {
113
+ return buildTruncatedFallback(content, contentSize, inlineLimit);
114
+ }
115
+ return {
116
+ contentSize,
117
+ resourceUri,
118
+ resourceMimeType: 'text/markdown',
119
+ };
120
+ }
121
+ function resolveResourceUri(cacheKey) {
122
+ if (!cache.isEnabled() || !cacheKey)
123
+ return null;
124
+ return cache.toResourceUri(cacheKey);
125
+ }
126
+ function buildTruncatedFallback(content, contentSize, inlineLimit) {
127
+ const maxContentLength = Math.max(0, inlineLimit - TRUNCATION_MARKER.length);
128
+ const truncatedContent = content.length > inlineLimit
129
+ ? `${content.substring(0, maxContentLength)}${TRUNCATION_MARKER}`
130
+ : content;
131
+ return {
132
+ content: truncatedContent,
133
+ contentSize,
134
+ truncated: true,
135
+ };
136
+ }
137
+ function attemptCacheRetrieval({ cacheKey, deserialize, cacheNamespace, normalizedUrl, }) {
138
+ if (!cacheKey)
139
+ return null;
140
+ const cached = cache.get(cacheKey);
141
+ if (!cached)
142
+ return null;
143
+ if (!deserialize) {
144
+ logCacheMiss('missing deserializer', cacheNamespace, normalizedUrl);
145
+ return null;
146
+ }
147
+ const data = deserialize(cached.content);
148
+ if (data === undefined) {
149
+ logCacheMiss('deserialize failure', cacheNamespace, normalizedUrl);
150
+ return null;
151
+ }
152
+ logDebug('Cache hit', { namespace: cacheNamespace, url: normalizedUrl });
153
+ return {
154
+ data,
155
+ fromCache: true,
156
+ url: normalizedUrl,
157
+ fetchedAt: cached.fetchedAt,
158
+ cacheKey,
159
+ };
160
+ }
161
+ function resolveNormalizedUrl(url) {
162
+ const { normalizedUrl: validatedUrl } = normalizeUrl(url);
163
+ const { url: normalizedUrl, transformed } = transformToRawUrl(validatedUrl);
164
+ return { normalizedUrl, originalUrl: validatedUrl, transformed };
165
+ }
166
+ export async function executeFetchPipeline(options) {
167
+ const resolvedUrl = resolveNormalizedUrl(options.url);
168
+ logRawUrlTransformation(resolvedUrl);
169
+ const cacheKey = cache.createCacheKey(options.cacheNamespace, resolvedUrl.normalizedUrl, options.cacheVary);
170
+ const cachedResult = attemptCacheRetrieval({
171
+ cacheKey,
172
+ deserialize: options.deserialize,
173
+ cacheNamespace: options.cacheNamespace,
174
+ normalizedUrl: resolvedUrl.normalizedUrl,
175
+ });
176
+ if (cachedResult)
177
+ return cachedResult;
178
+ logDebug('Fetching URL', { url: resolvedUrl.normalizedUrl });
179
+ const fetchOptions = options.signal === undefined ? {} : { signal: options.signal };
180
+ const html = await fetchNormalizedUrl(resolvedUrl.normalizedUrl, fetchOptions);
181
+ const data = await options.transform(html, resolvedUrl.normalizedUrl);
182
+ if (cache.isEnabled()) {
183
+ persistCache({
184
+ cacheKey,
185
+ data,
186
+ serialize: options.serialize,
187
+ normalizedUrl: resolvedUrl.normalizedUrl,
188
+ });
189
+ }
190
+ return {
191
+ data,
192
+ fromCache: false,
193
+ url: resolvedUrl.normalizedUrl,
194
+ fetchedAt: new Date().toISOString(),
195
+ cacheKey,
196
+ };
197
+ }
198
+ function persistCache({ cacheKey, data, serialize, normalizedUrl, }) {
199
+ if (!cacheKey)
200
+ return;
201
+ const serializer = serialize ?? JSON.stringify;
202
+ const title = extractTitle(data);
203
+ const metadata = {
204
+ url: normalizedUrl,
205
+ ...(title === undefined ? {} : { title }),
206
+ };
207
+ cache.set(cacheKey, serializer(data), metadata);
208
+ }
209
+ function extractTitle(value) {
210
+ if (!isRecord(value))
211
+ return undefined;
212
+ const { title } = value;
213
+ return typeof title === 'string' ? title : undefined;
214
+ }
215
+ function logCacheMiss(reason, cacheNamespace, normalizedUrl) {
216
+ logDebug(`Cache miss due to ${reason}`, {
217
+ namespace: cacheNamespace,
218
+ url: normalizedUrl,
219
+ });
220
+ }
221
+ function logRawUrlTransformation(resolvedUrl) {
222
+ if (!resolvedUrl.transformed)
223
+ return;
224
+ logDebug('Using transformed raw content URL', {
225
+ original: resolvedUrl.originalUrl,
226
+ });
227
+ }
228
+ function applyOptionalPipelineSerialization(pipelineOptions, options) {
229
+ if (options.serialize !== undefined) {
230
+ pipelineOptions.serialize = options.serialize;
231
+ }
232
+ if (options.deserialize !== undefined) {
233
+ pipelineOptions.deserialize = options.deserialize;
234
+ }
235
+ }
236
+ export async function performSharedFetch(options, deps = {}) {
237
+ const executePipeline = deps.executeFetchPipeline ?? executeFetchPipeline;
238
+ const pipelineOptions = {
239
+ url: options.url,
240
+ cacheNamespace: 'markdown',
241
+ transform: options.transform,
242
+ };
243
+ applyOptionalPipelineSerialization(pipelineOptions, options);
244
+ const pipeline = await executePipeline(pipelineOptions);
245
+ const inlineResult = applyInlineContentLimit(pipeline.data.content, pipeline.cacheKey ?? null);
246
+ return { pipeline, inlineResult };
247
+ }
248
+ export function createToolErrorResponse(message, url) {
249
+ const structuredContent = {
250
+ error: message,
251
+ url,
252
+ };
253
+ return {
254
+ content: [buildTextBlock(structuredContent, true)],
255
+ structuredContent,
256
+ isError: true,
257
+ };
258
+ }
259
+ export function handleToolError(error, url, fallbackMessage = 'Operation failed') {
260
+ const message = resolveToolErrorMessage(error, fallbackMessage);
261
+ return createToolErrorResponse(message, url);
262
+ }
263
+ function isValidationError(error) {
264
+ return (error instanceof Error &&
265
+ isSystemError(error) &&
266
+ error.code === 'VALIDATION_ERROR');
267
+ }
268
+ function resolveToolErrorMessage(error, fallbackMessage) {
269
+ if (isValidationError(error) || error instanceof FetchError) {
270
+ return error.message;
271
+ }
272
+ if (error instanceof Error) {
273
+ return `${fallbackMessage}: ${error.message}`;
274
+ }
275
+ return `${fallbackMessage}: Unknown error`;
276
+ }
277
+ function parseJsonRecord(input) {
278
+ try {
279
+ const parsed = JSON.parse(input);
280
+ return isRecord(parsed) ? parsed : undefined;
281
+ }
282
+ catch {
283
+ return undefined;
284
+ }
285
+ }
286
+ function resolveMarkdownContent(parsed) {
287
+ const { markdown } = parsed;
288
+ if (typeof markdown === 'string')
289
+ return markdown;
290
+ const { content } = parsed;
291
+ if (typeof content === 'string')
292
+ return content;
293
+ return undefined;
294
+ }
295
+ function resolveOptionalTitle(parsed) {
296
+ const { title } = parsed;
297
+ if (title === undefined)
298
+ return undefined;
299
+ return typeof title === 'string' ? title : undefined;
300
+ }
301
+ function resolveTruncatedFlag(parsed) {
302
+ const { truncated } = parsed;
303
+ return typeof truncated === 'boolean' ? truncated : false;
304
+ }
305
+ export function parseCachedMarkdownResult(cached) {
306
+ const parsed = parseJsonRecord(cached);
307
+ if (!parsed)
308
+ return undefined;
309
+ const resolvedContent = resolveMarkdownContent(parsed);
310
+ if (resolvedContent === undefined)
311
+ return undefined;
312
+ const title = resolveOptionalTitle(parsed);
313
+ if (parsed.title !== undefined && title === undefined)
314
+ return undefined;
315
+ return {
316
+ content: resolvedContent,
317
+ markdown: resolvedContent,
318
+ title,
319
+ truncated: resolveTruncatedFlag(parsed),
320
+ };
321
+ }
322
+ function deserializeMarkdownResult(cached) {
323
+ return parseCachedMarkdownResult(cached);
324
+ }
325
+ function buildMarkdownTransform() {
326
+ return async (html, url) => {
327
+ const result = await transformHtmlToMarkdown(html, url, {
328
+ includeMetadata: true,
329
+ });
330
+ return { ...result, content: result.markdown };
331
+ };
332
+ }
333
+ function serializeMarkdownResult(result) {
334
+ return JSON.stringify({
335
+ markdown: result.markdown,
336
+ title: result.title,
337
+ truncated: result.truncated,
338
+ });
339
+ }
340
+ function buildStructuredContent(pipeline, inlineResult, inputUrl) {
341
+ return {
342
+ url: pipeline.url,
343
+ resolvedUrl: pipeline.url,
344
+ inputUrl,
345
+ title: pipeline.data.title,
346
+ markdown: inlineResult.content,
347
+ };
348
+ }
349
+ function buildFetchUrlContentBlocks(structuredContent, pipeline, inlineResult) {
350
+ return buildToolContentBlocks(structuredContent, pipeline.fromCache, inlineResult, 'Fetched markdown', pipeline.cacheKey, pipeline.data.content, pipeline.url, pipeline.data.title);
351
+ }
352
+ function logFetchStart(url) {
353
+ logDebug('Fetching URL', { url });
354
+ }
355
+ async function fetchPipeline(url) {
356
+ return performSharedFetch({
357
+ url,
358
+ transform: buildMarkdownTransform(),
359
+ serialize: serializeMarkdownResult,
360
+ deserialize: deserializeMarkdownResult,
361
+ });
362
+ }
363
+ function buildResponse(pipeline, inlineResult, inputUrl) {
364
+ const structuredContent = buildStructuredContent(pipeline, inlineResult, inputUrl);
365
+ const content = buildFetchUrlContentBlocks(structuredContent, pipeline, inlineResult);
366
+ return {
367
+ content,
368
+ structuredContent,
369
+ };
370
+ }
371
+ async function executeFetch(input) {
372
+ const { url } = input;
373
+ if (!url) {
374
+ return createToolErrorResponse('URL is required', '');
375
+ }
376
+ logFetchStart(url);
377
+ const { pipeline, inlineResult } = await fetchPipeline(url);
378
+ if (inlineResult.error) {
379
+ return createToolErrorResponse(inlineResult.error, url);
380
+ }
381
+ return buildResponse(pipeline, inlineResult, url);
382
+ }
383
+ export async function fetchUrlToolHandler(input) {
384
+ return executeFetch(input).catch((error) => {
385
+ logError('fetch-url tool error', error instanceof Error ? error : undefined);
386
+ return handleToolError(error, input.url, 'Failed to fetch URL');
387
+ });
388
+ }
389
+ const TOOL_DEFINITION = {
390
+ name: FETCH_URL_TOOL_NAME,
391
+ title: 'Fetch URL',
392
+ description: FETCH_URL_TOOL_DESCRIPTION,
393
+ inputSchema: fetchUrlInputSchema,
394
+ outputSchema: fetchUrlOutputSchema,
395
+ handler: fetchUrlToolHandler,
396
+ annotations: {
397
+ readOnlyHint: true,
398
+ destructiveHint: false,
399
+ idempotentHint: true,
400
+ openWorldHint: true,
401
+ },
402
+ };
403
+ export function withRequestContextIfMissing(handler) {
404
+ return async (params) => {
405
+ const existingRequestId = getRequestId();
406
+ if (existingRequestId) {
407
+ return handler(params);
408
+ }
409
+ const requestId = randomUUID();
410
+ return runWithRequestContext({ requestId, operationId: requestId }, () => handler(params));
411
+ };
412
+ }
413
+ export function registerTools(server) {
414
+ server.registerTool(TOOL_DEFINITION.name, {
415
+ title: TOOL_DEFINITION.title,
416
+ description: TOOL_DEFINITION.description,
417
+ inputSchema: TOOL_DEFINITION.inputSchema,
418
+ outputSchema: TOOL_DEFINITION.outputSchema,
419
+ annotations: TOOL_DEFINITION.annotations,
420
+ }, withRequestContextIfMissing(TOOL_DEFINITION.handler));
421
+ }
@@ -0,0 +1,69 @@
1
+ export interface MetadataBlock {
2
+ type: 'metadata';
3
+ title?: string;
4
+ description?: string;
5
+ author?: string;
6
+ url: string;
7
+ fetchedAt: string;
8
+ }
9
+ export interface ExtractedArticle {
10
+ title?: string;
11
+ byline?: string;
12
+ content: string;
13
+ textContent: string;
14
+ excerpt?: string;
15
+ siteName?: string;
16
+ }
17
+ export interface ExtractedMetadata {
18
+ title?: string;
19
+ description?: string;
20
+ author?: string;
21
+ }
22
+ export interface ExtractionResult {
23
+ article: ExtractedArticle | null;
24
+ metadata: ExtractedMetadata;
25
+ }
26
+ export interface MarkdownTransformResult {
27
+ markdown: string;
28
+ title: string | undefined;
29
+ truncated: boolean;
30
+ }
31
+ export interface TransformOptions {
32
+ includeMetadata: boolean;
33
+ signal?: AbortSignal;
34
+ }
35
+ export interface TransformStageEvent {
36
+ v: 1;
37
+ type: 'stage';
38
+ stage: string;
39
+ durationMs: number;
40
+ url: string;
41
+ requestId?: string;
42
+ operationId?: string;
43
+ truncated?: boolean;
44
+ }
45
+ export interface TransformStageContext {
46
+ readonly stage: string;
47
+ readonly startTime: number;
48
+ readonly url: string;
49
+ }
50
+ export declare function startTransformStage(url: string, stage: string): TransformStageContext | null;
51
+ export declare function endTransformStage(context: TransformStageContext | null, options?: {
52
+ truncated?: boolean;
53
+ }): void;
54
+ export declare function extractContent(html: string, url: string, options?: {
55
+ extractArticle?: boolean;
56
+ signal?: AbortSignal;
57
+ }): ExtractionResult;
58
+ export declare function detectLanguageFromCode(code: string): string | undefined;
59
+ export declare function resolveLanguageFromAttributes(className: string, dataLang: string): string | undefined;
60
+ export declare function htmlToMarkdown(html: string, metadata?: MetadataBlock, options?: {
61
+ url?: string;
62
+ signal?: AbortSignal;
63
+ }): string;
64
+ export declare function isExtractionSufficient(article: ExtractedArticle | null, originalHtml: string): boolean;
65
+ export declare function determineContentExtractionSource(article: ExtractedArticle | null): article is ExtractedArticle;
66
+ export declare function createContentMetadataBlock(url: string, article: ExtractedArticle | null, extractedMeta: ExtractedMetadata, shouldExtractFromArticle: boolean, includeMetadata: boolean): MetadataBlock | undefined;
67
+ export declare function transformHtmlToMarkdownInProcess(html: string, url: string, options: TransformOptions): MarkdownTransformResult;
68
+ export declare function shutdownTransformWorkerPool(): Promise<void>;
69
+ export declare function transformHtmlToMarkdown(html: string, url: string, options: TransformOptions): Promise<MarkdownTransformResult>;