@j0hanz/superfetch 2.3.0 → 2.4.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.
@@ -1,6 +1,10 @@
1
1
  import { AsyncLocalStorage } from 'node:async_hooks';
2
2
  import { config } from './config.js';
3
3
  const requestContext = new AsyncLocalStorage();
4
+ let mcpServer;
5
+ export function setMcpServer(server) {
6
+ mcpServer = server;
7
+ }
4
8
  export function runWithRequestContext(context, fn) {
5
9
  return requestContext.run(context, fn);
6
10
  }
@@ -43,10 +47,31 @@ function shouldLog(level) {
43
47
  // All other levels always log
44
48
  return true;
45
49
  }
50
+ function mapToMcpLevel(level) {
51
+ switch (level) {
52
+ case 'warn':
53
+ return 'warning';
54
+ case 'error':
55
+ return 'error';
56
+ case 'debug':
57
+ return 'debug';
58
+ case 'info':
59
+ default:
60
+ return 'info';
61
+ }
62
+ }
46
63
  function writeLog(level, message, meta) {
47
64
  if (!shouldLog(level))
48
65
  return;
49
66
  process.stderr.write(`${formatLogEntry(level, message, meta)}\n`);
67
+ if (mcpServer) {
68
+ mcpServer.server
69
+ .sendLoggingMessage({
70
+ level: mapToMcpLevel(level),
71
+ data: meta ? { message, ...meta } : message,
72
+ })
73
+ .catch(() => { });
74
+ }
50
75
  }
51
76
  export function logInfo(message, meta) {
52
77
  writeLog('info', message, meta);
package/dist/tools.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import { type MarkdownTransformResult } from './transform.js';
2
+ import type { MarkdownTransformResult } from './transform-types.js';
3
3
  export interface FetchUrlInput {
4
4
  url: string;
5
5
  }
@@ -25,7 +25,6 @@ export interface ToolContentResourceBlock {
25
25
  }
26
26
  export type ToolContentBlockUnion = ToolContentBlock | ToolContentResourceLinkBlock | ToolContentResourceBlock;
27
27
  export interface ToolErrorResponse {
28
- [x: string]: unknown;
29
28
  content: ToolContentBlockUnion[];
30
29
  structuredContent: {
31
30
  error: string;
@@ -34,7 +33,6 @@ export interface ToolErrorResponse {
34
33
  isError: true;
35
34
  }
36
35
  export interface ToolResponseBase {
37
- [x: string]: unknown;
38
36
  content: ToolContentBlockUnion[];
39
37
  structuredContent?: Record<string, unknown>;
40
38
  isError?: boolean;
@@ -86,6 +84,10 @@ export interface ToolHandlerExtra {
86
84
  }
87
85
  export declare const FETCH_URL_TOOL_NAME = "fetch-url";
88
86
  export declare const FETCH_URL_TOOL_DESCRIPTION = "Fetches a webpage and converts it to clean Markdown format";
87
+ interface ProgressReporter {
88
+ report: (progress: number, message: string) => Promise<void>;
89
+ }
90
+ export declare function createProgressReporter(extra?: ToolHandlerExtra): ProgressReporter;
89
91
  interface InlineContentResult {
90
92
  content?: string;
91
93
  contentSize: number;
package/dist/tools.js CHANGED
@@ -5,10 +5,11 @@ import { config } from './config.js';
5
5
  import { FetchError, getErrorMessage, isSystemError } from './errors.js';
6
6
  import { fetchNormalizedUrl, normalizeUrl, transformToRawUrl, } from './fetch.js';
7
7
  import { getRequestId, logDebug, logError, logWarn, runWithRequestContext, } from './observability.js';
8
- import { transformHtmlToMarkdown, } from './transform.js';
8
+ import { transformHtmlToMarkdown } from './transform.js';
9
9
  import { isObject } from './type-guards.js';
10
10
  const TRUNCATION_MARKER = '...[truncated]';
11
11
  const FETCH_PROGRESS_TOTAL = 4;
12
+ const PROGRESS_NOTIFICATION_TIMEOUT_MS = 5000;
12
13
  const fetchUrlInputSchema = z.strictObject({
13
14
  url: z
14
15
  .url({ protocol: /^https?$/i })
@@ -46,7 +47,7 @@ const fetchUrlOutputSchema = z.strictObject({
46
47
  });
47
48
  export const FETCH_URL_TOOL_NAME = 'fetch-url';
48
49
  export const FETCH_URL_TOOL_DESCRIPTION = 'Fetches a webpage and converts it to clean Markdown format';
49
- function createProgressReporter(extra) {
50
+ export function createProgressReporter(extra) {
50
51
  const token = extra?._meta?.progressToken ?? null;
51
52
  const sendNotification = extra?.sendNotification;
52
53
  if (token === null || !sendNotification) {
@@ -55,19 +56,33 @@ function createProgressReporter(extra) {
55
56
  return {
56
57
  report: async (progress, message) => {
57
58
  try {
58
- await sendNotification({
59
- method: 'notifications/progress',
60
- params: {
61
- progressToken: token,
62
- progress,
63
- total: FETCH_PROGRESS_TOTAL,
64
- message,
65
- },
66
- });
59
+ await Promise.race([
60
+ sendNotification({
61
+ method: 'notifications/progress',
62
+ params: {
63
+ progressToken: token,
64
+ progress,
65
+ total: FETCH_PROGRESS_TOTAL,
66
+ message,
67
+ },
68
+ }),
69
+ new Promise((_, reject) => {
70
+ setTimeout(() => {
71
+ reject(new Error('Progress notification timeout'));
72
+ }, PROGRESS_NOTIFICATION_TIMEOUT_MS);
73
+ }),
74
+ ]);
67
75
  }
68
76
  catch (error) {
69
- logWarn('Failed to send progress notification', {
77
+ const isTimeout = error instanceof Error &&
78
+ error.message === 'Progress notification timeout';
79
+ const logMessage = isTimeout
80
+ ? 'Progress notification timed out'
81
+ : 'Failed to send progress notification';
82
+ logWarn(logMessage, {
70
83
  error: getErrorMessage(error),
84
+ progress,
85
+ message,
71
86
  });
72
87
  }
73
88
  },
@@ -77,4 +77,42 @@ export interface TransformStageContext {
77
77
  readonly stage: string;
78
78
  readonly startTime: number;
79
79
  readonly url: string;
80
+ readonly budgetMs?: number;
81
+ readonly totalBudgetMs?: number;
80
82
  }
83
+ /**
84
+ * Worker message types for transform workers.
85
+ */
86
+ export interface TransformWorkerTransformMessage {
87
+ type: 'transform';
88
+ id: string;
89
+ html: string;
90
+ url: string;
91
+ includeMetadata: boolean;
92
+ }
93
+ export interface TransformWorkerCancelMessage {
94
+ type: 'cancel';
95
+ id: string;
96
+ }
97
+ export interface TransformWorkerResultMessage {
98
+ type: 'result';
99
+ id: string;
100
+ result: {
101
+ markdown: string;
102
+ title?: string;
103
+ truncated: boolean;
104
+ };
105
+ }
106
+ export interface TransformWorkerErrorMessage {
107
+ type: 'error';
108
+ id: string;
109
+ error: {
110
+ name: string;
111
+ message: string;
112
+ url: string;
113
+ statusCode?: number;
114
+ details?: Record<string, unknown>;
115
+ };
116
+ }
117
+ export type TransformWorkerIncomingMessage = TransformWorkerTransformMessage | TransformWorkerCancelMessage;
118
+ export type TransformWorkerOutgoingMessage = TransformWorkerResultMessage | TransformWorkerErrorMessage;
@@ -1,12 +1,12 @@
1
1
  import type { ExtractedArticle, ExtractedMetadata, ExtractionResult, MarkdownTransformResult, MetadataBlock, TransformOptions, TransformStageContext } from './transform-types.js';
2
- export { detectLanguageFromCode, resolveLanguageFromAttributes, } from './language-detection.js';
3
- export { cleanupMarkdownArtifacts, promoteOrphanHeadings, } from './markdown-cleanup.js';
4
- export { removeNoiseFromHtml } from './dom-noise-removal.js';
5
- export type { MetadataBlock, ExtractedArticle, ExtractedMetadata, ExtractionResult, MarkdownTransformResult, TransformOptions, TransformStageEvent, TransformStageContext, } from './transform-types.js';
6
- export declare function startTransformStage(url: string, stage: string): TransformStageContext | null;
2
+ export interface StageBudget {
3
+ totalBudgetMs: number;
4
+ elapsedMs: number;
5
+ }
6
+ export declare function startTransformStage(url: string, stage: string, budget?: StageBudget): TransformStageContext | null;
7
7
  export declare function endTransformStage(context: TransformStageContext | null, options?: {
8
8
  truncated?: boolean;
9
- }): void;
9
+ }): number;
10
10
  export declare function extractContent(html: string, url: string, options?: {
11
11
  extractArticle?: boolean;
12
12
  signal?: AbortSignal;
@@ -22,4 +22,10 @@ export declare function determineContentExtractionSource(article: ExtractedArtic
22
22
  export declare function createContentMetadataBlock(url: string, article: ExtractedArticle | null, extractedMeta: ExtractedMetadata, shouldExtractFromArticle: boolean, includeMetadata: boolean): MetadataBlock | undefined;
23
23
  export declare function transformHtmlToMarkdownInProcess(html: string, url: string, options: TransformOptions): MarkdownTransformResult;
24
24
  export declare function shutdownTransformWorkerPool(): Promise<void>;
25
+ export interface TransformPoolStats {
26
+ queueDepth: number;
27
+ activeWorkers: number;
28
+ capacity: number;
29
+ }
30
+ export declare function getTransformPoolStats(): TransformPoolStats | null;
25
31
  export declare function transformHtmlToMarkdown(html: string, url: string, options: TransformOptions): Promise<MarkdownTransformResult>;