@doclo/core 0.2.2 → 0.2.4
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 +16 -15
- package/dist/index.d.ts +2 -2
- package/dist/index.js +11 -12
- package/dist/index.js.map +1 -1
- package/dist/internal/validation-utils.d.ts +1 -1
- package/dist/internal/validation-utils.js +1 -1
- package/dist/internal/validation-utils.js.map +1 -1
- package/dist/pdf-utils.d.ts +12 -7
- package/dist/pdf-utils.js +10 -11
- package/dist/pdf-utils.js.map +1 -1
- package/dist/{validation-BQO54qAY.d.ts → validation-C_RN-Xqr.d.ts} +35 -8
- package/dist/validation.d.ts +1 -1
- package/dist/validation.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/validation-utils.ts","../src/security/url-validator.ts","../src/security/resource-limits.ts","../src/runtime/base64.ts","../src/mime-detection.ts","../src/internal/file-utils.ts","../src/pdf-utils.ts","../src/provider-config.ts","../src/provider-identity.ts","../src/provider-query.ts","../src/retry.ts"],"sourcesContent":["/**\n * Browser-safe validation utilities\n *\n * This module contains all validation code with ZERO Node.js dependencies.\n * It can be safely bundled for browser environments.\n */\n\n// Edge Runtime compatible - no AJV dependency\n\n// Re-export all types and constants from the validation section of index.ts\n// This file has NO fs imports and is completely browser-safe\n\n/** Page-centric IR */\nexport type BBox = { x: number; y: number; w: number; h: number };\nexport type IRLine = {\n text: string;\n bbox?: BBox;\n startChar?: number; // Character offset in full document text\n endChar?: number; // Character offset in full document text\n lineId?: string; // Unique line identifier (e.g., \"p1_l5\" for page 1, line 5)\n};\nexport type IRPage = {\n pageNumber?: number; // Explicit 1-indexed page number (for chunked documents)\n width: number;\n height: number;\n lines: IRLine[];\n markdown?: string; // Rich markdown preserving layout (tables, headers, lists)\n html?: string; // Rich HTML preserving layout (tables, headers, lists)\n extras?: Record<string, unknown>\n};\n\n/** Standard extras fields for DocumentIR */\nexport type DocumentIRExtras = {\n /** Total number of pages in the original document (for PDFs, DOCX, etc.) */\n pageCount?: number;\n /** Cost in USD for processing this document */\n costUSD?: number;\n /** Provider-specific raw response */\n raw?: unknown;\n /** For chunked documents: which chunk this is (0-indexed) */\n chunkIndex?: number;\n /** For chunked documents: total number of chunks */\n totalChunks?: number;\n /** For chunked documents: page range [startPage, endPage] (1-indexed, inclusive) */\n pageRange?: [number, number];\n /** For Unsiloed: total semantic chunks (not traditional pages) */\n totalSemanticChunks?: number;\n /** Allow arbitrary additional fields */\n [key: string]: unknown;\n};\n\nexport type DocumentIR = {\n pages: IRPage[];\n extras?: DocumentIRExtras;\n};\n\n/** Provider identity for 3-layer hierarchy (provider/model/method) */\nimport type { ProviderIdentity } from '../provider-identity.js';\n\n/** Provider capability contracts */\nexport type OCRProvider = {\n /** Full 3-layer identity (provider/model/method) */\n identity?: ProviderIdentity;\n /** Canonical name in \"provider:model\" format */\n name: string;\n parseToIR: (input: { url?: string; base64?: string }) => Promise<DocumentIR>;\n};\n\n/** Multimodal input for VLM providers */\nexport type MultimodalInput = {\n text?: string;\n images?: Array<{ url?: string; base64?: string; mimeType: string }>;\n pdfs?: Array<{ url?: string; base64?: string; fileId?: string }>;\n};\n\n/** Reasoning configuration (normalized across providers) */\nexport type ReasoningConfig = {\n /** Reasoning effort level: low (20% budget), medium (50%), high (80%) */\n effort?: 'low' | 'medium' | 'high';\n /** Exclude reasoning tokens from response (only use for accuracy, not visible) */\n exclude?: boolean;\n /** Enable reasoning with default (medium) effort */\n enabled?: boolean;\n};\n\n/** Base LLM provider (text-only) */\nexport type LLMProvider = {\n /** Full 3-layer identity (provider/model/method) */\n identity?: ProviderIdentity;\n /** Canonical name in \"provider:model\" format */\n name: string;\n completeJson: (input: { prompt: string; schema: object; max_tokens?: number; reasoning?: ReasoningConfig }) =>\n Promise<{ json: unknown; rawText?: string; costUSD?: number; inputTokens?: number; outputTokens?: number; cacheCreationInputTokens?: number; cacheReadInputTokens?: number }>;\n};\n\n/** Vision-capable LLM provider */\nexport type VLMProvider = {\n /** Full 3-layer identity (provider/model/method) */\n identity?: ProviderIdentity;\n /** Canonical name in \"provider:model\" format */\n name: string;\n completeJson: (input: { prompt: string | MultimodalInput; schema: object; max_tokens?: number; reasoning?: ReasoningConfig }) =>\n Promise<{ json: unknown; rawText?: string; costUSD?: number; inputTokens?: number; outputTokens?: number; cacheCreationInputTokens?: number; cacheReadInputTokens?: number }>;\n capabilities: {\n supportsImages: true;\n supportsPDFs: boolean;\n maxPDFPages?: number;\n };\n};\n\n/** Legacy alias for backward compatibility */\nexport type LLMJsonProvider = VLMProvider;\n\n// ============================================================================\n// Processing Options - Normalized types for provider-agnostic configuration\n// ============================================================================\n\n/**\n * Processing quality/speed tradeoff modes\n * Providers map their specific modes to these normalized values\n */\nexport type ProcessingMode = 'fast' | 'balanced' | 'high_accuracy';\n\n/**\n * Page range specification for partial document processing\n * Allows processing a subset of pages for cost savings\n */\nexport type PageRangeOptions = {\n /** Process only the first N pages */\n maxPages?: number;\n /** Specific page range (0-indexed), e.g., \"0,2-4,10\" */\n pageRange?: string;\n};\n\n/**\n * Language hints for OCR processing\n */\nexport type LanguageOptions = {\n /** ISO language codes for OCR, e.g., ['en', 'de', 'fr'] */\n langs?: string[];\n};\n\n/**\n * Document segmentation result for splitting \"stapled\" PDFs\n * Returns page boundaries for each detected document type\n */\nexport type SegmentationResult = {\n segments: Array<{\n /** Document type name (e.g., 'invoice', 'contract') */\n name: string;\n /** Page indices (0-indexed) belonging to this segment */\n pages: number[];\n /** Confidence level of segmentation */\n confidence: 'high' | 'medium' | 'low';\n }>;\n metadata: {\n /** Total pages in the original document */\n totalPages: number;\n /** How segmentation was performed */\n segmentationMethod: 'auto' | 'schema' | 'manual';\n };\n};\n\n/**\n * Extracted image from a document\n * Represents figures, charts, or embedded images\n */\nexport type ExtractedImage = {\n /** Block ID or reference (provider-specific) */\n id: string;\n /** Page number where image appears (0-indexed) */\n pageNumber: number;\n /** Base64-encoded image data */\n base64: string;\n /** MIME type of the image */\n mimeType: string;\n /** Location on page (normalized 0-1 coordinates) */\n bbox?: NormalizedBBox;\n /** Caption text if detected */\n caption?: string;\n};\n\n/**\n * Extended OCR provider options (beyond basic parseToIR)\n * These options are normalized across different OCR providers\n */\nexport type OCRProviderOptions = PageRangeOptions & LanguageOptions & {\n /** Processing quality/speed tradeoff */\n mode?: ProcessingMode;\n /** Force OCR even on text-based PDFs */\n forceOCR?: boolean;\n /** Extract embedded images from document */\n extractImages?: boolean;\n /** Add page delimiters to output */\n paginate?: boolean;\n /** Remove and redo existing OCR */\n stripExistingOCR?: boolean;\n};\n\n/**\n * Output format options for LLM-based text fields\n * Controls how text content is formatted in the response\n */\nexport type OutputFormat = 'markdown' | 'html' | 'json' | 'text';\n\n/**\n * Table format options for tabular data in responses\n */\nexport type TableFormat = 'markdown' | 'html' | 'csv';\n\n/**\n * Chunking strategy options for document segmentation\n */\nexport type ChunkingStrategy = 'page' | 'section' | 'paragraph' | 'semantic';\n\n/**\n * LLM-derived feature options\n * These features are implemented via prompting rather than native API support\n */\nexport type LLMDerivedOptions = {\n /** Format for text output in string fields */\n outputFormat?: OutputFormat;\n /** Format for tables within text fields */\n tableFormat?: TableFormat;\n /** Add page break markers (---) between pages */\n pageMarkers?: boolean;\n /** Include per-field confidence scores (attached to result, not in JSON) */\n includeConfidence?: boolean;\n /** Include source citations with bounding boxes (attached to result, not in JSON) */\n includeSources?: boolean;\n /** Include block type classification for each extracted element */\n includeBlockTypes?: boolean;\n /** Extract document headers (repeated content at top of pages) */\n extractHeaders?: boolean;\n /** Extract document footers (repeated content at bottom of pages) */\n extractFooters?: boolean;\n /** Document chunking strategy */\n chunkingStrategy?: ChunkingStrategy;\n /** Maximum chunk size in characters (when using chunking) */\n maxChunkSize?: number;\n /** Language hints for the document (e.g., ['English', 'German']) */\n languageHints?: string[];\n};\n\n/**\n * Extended VLM provider options for document extraction\n * These options are normalized across different VLM providers\n */\nexport type VLMProviderOptions = PageRangeOptions & LanguageOptions & LLMDerivedOptions & {\n /** Processing quality/speed tradeoff */\n mode?: ProcessingMode;\n /** Force OCR even on text-based PDFs */\n forceOCR?: boolean;\n /** Additional prompt/instructions for extraction */\n prompt?: string;\n /** Schema for auto-segmentation of multi-document PDFs */\n segmentationSchema?: object;\n};\n\n/**\n * Provider citation from source document\n * Maps extracted fields to their source locations\n */\nexport type ProviderCitation = {\n /** JSON path to extracted field (e.g., \"invoice.total\") */\n fieldPath: string;\n /** Source block IDs from the provider */\n blockIds: string[];\n /** Confidence score (0-1) */\n confidence?: number;\n};\n\n/** Consensus configuration for any node */\nexport type ConsensusConfig = {\n runs: number; // Number of times to run\n strategy?: 'majority' | 'unanimous'; // Default: majority\n onTie?: 'random' | 'fail' | 'retry'; // Default: random\n parallel?: boolean; // Run consensus in parallel (default: true)\n includeMetadata?: boolean; // Include detailed consensus metadata (default: false)\n level?: 'object' | 'field'; // Voting level: object (default) or per-field\n retryOnFailure?: boolean; // Retry failed/empty runs (default: false)\n maxRetries?: number; // Max retries per run (default: 1)\n};\n\n/** Individual consensus run result */\nexport type ConsensusRunResult<T = any> = {\n runIndex: number;\n value: T | null;\n success: boolean;\n error?: string;\n startTime: number;\n endTime: number;\n duration: number;\n attempts?: number; // Number of attempts (1 = no retry, >1 = retried)\n};\n\n/** Field-level voting details */\nexport type FieldVotingDetails = {\n fieldPath: string;\n values: Array<{\n /** The actual value for this voting option - can be any JSON-serializable type */\n value: unknown;\n count: number;\n percentage: number;\n runIndices: number[];\n }>;\n /** The winning value from consensus - can be any JSON-serializable type */\n winner: unknown;\n isTie: boolean;\n agreementScore: number; // 0.0 to 1.0\n};\n\n/** Consensus execution metadata */\nexport type ConsensusMetadata<T = unknown> = {\n totalRuns: number;\n successfulRuns: number;\n failedRuns: number;\n strategy: 'majority' | 'unanimous';\n selectedResult: T;\n selectedRunIndex: number;\n confidence: 'high' | 'medium' | 'low';\n overallAgreement: number; // 0.0 to 1.0\n fieldAgreement: Record<string, number>; // Field path -> agreement score\n votingDetails: FieldVotingDetails[];\n runs: ConsensusRunResult<T>[];\n executionTime: number;\n wasRetry: boolean;\n tieBreakerUsed?: 'random' | 'retry' | 'fail' | null;\n // New fields for enhanced consensus features\n votingLevel?: 'object' | 'field';\n isSyntheticResult?: boolean; // true if field-level voting composed a new object\n totalRetries?: number; // Total retry attempts across all runs\n emptyResultsFiltered?: number; // Number of empty results filtered out\n};\n\n/** Output with consensus metadata wrapper */\nexport type OutputWithConsensus<T = unknown> = {\n data: T;\n consensus: ConsensusMetadata<T>;\n};\n\n/** Conditional type helper for consensus metadata */\nexport type MaybeWithConsensusMetadata<T, Config> = Config extends { includeMetadata: true }\n ? OutputWithConsensus<T>\n : T;\n\n/** Flow input/output types */\nexport type FlowInput = {\n url?: string;\n base64?: string;\n pages?: number[]; // For post-split runs\n bounds?: BBox; // For post-split runs\n};\n\n/**\n * All MIME types supported by at least one provider.\n * This is the union of all provider capabilities.\n */\nexport type SupportedMimeType =\n // PDF\n | 'application/pdf'\n // Images - common\n | 'image/jpeg'\n | 'image/png'\n | 'image/gif'\n | 'image/webp'\n // Images - additional\n | 'image/tiff'\n | 'image/bmp'\n | 'image/heic'\n | 'image/heif'\n | 'image/vnd.adobe.photoshop' // PSD\n // Microsoft Office\n | 'application/msword' // DOC\n | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' // DOCX\n | 'application/vnd.ms-excel' // XLS\n | 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' // XLSX\n | 'application/vnd.ms-powerpoint' // PPT\n | 'application/vnd.openxmlformats-officedocument.presentationml.presentation' // PPTX\n // OpenDocument formats (Datalab)\n | 'application/vnd.oasis.opendocument.text' // ODT\n | 'application/vnd.oasis.opendocument.spreadsheet' // ODS\n | 'application/vnd.oasis.opendocument.presentation' // ODP\n // Text formats\n | 'text/plain' // TXT\n | 'text/csv' // CSV\n | 'text/html' // HTML\n | 'application/rtf' // RTF\n // Other\n | 'application/epub+zip'; // EPUB\n\n/**\n * Flow-level input validation configuration\n *\n * Allows specifying accepted MIME types for early validation\n * before flow execution begins.\n */\nexport type FlowInputValidation = {\n /**\n * List of accepted MIME types.\n * If specified, input must match one of these types or validation fails.\n * If empty/undefined, all supported types are accepted.\n */\n acceptedFormats?: SupportedMimeType[];\n /**\n * Whether to throw on validation failure.\n * @default true\n */\n throwOnInvalid?: boolean;\n};\n\nexport type FlowResult<T = any> = {\n output: T;\n metrics: StepMetric[];\n aggregated: AggregatedMetrics;\n artifacts: Record<string, any>;\n error?: Error;\n};\n\nexport type SplitDocument = {\n type: string; // 'invoice', 'bunker', 'other'\n schema?: object; // Matched schema (optional - only present when schemas provided)\n pages: number[]; // Page numbers\n bounds?: BBox; // Bounding box\n input: FlowInput; // Original input for re-processing\n};\n\n/** Citation and source tracking types */\n\n/** Citation source type indicating data provenance */\nexport type CitationSourceType = 'ocr' | 'vlm' | 'llm' | 'inferred';\n\n/** Normalized bounding box (0-1 coordinates relative to page dimensions) */\nexport type NormalizedBBox = {\n x: number; // Left edge (0-1)\n y: number; // Top edge (0-1)\n w: number; // Width (0-1)\n h: number; // Height (0-1)\n};\n\n/** Line-level citation reference with spatial information */\nexport type LineCitation = {\n pageNumber: number; // 1-indexed page number\n lineIndex: number; // 0-indexed line position on page\n bbox?: NormalizedBBox; // Normalized bounding box (0-1 coordinates)\n text: string; // Text snippet for verification\n confidence?: number; // 0-1 confidence score\n sourceType: CitationSourceType;\n startChar?: number; // Character offset in full document\n endChar?: number; // Character offset in full document\n};\n\n/** Field-level citation mapping extracted values to sources */\nexport type FieldCitation = {\n fieldPath: string; // JSON path to field (e.g., \"invoice.lineItems[0].amount\")\n /** Extracted value - can be any JSON-serializable type */\n value: unknown;\n citations: LineCitation[]; // Source lines supporting this value\n reasoning?: string; // LLM explanation for inferred values\n confidence?: number; // Overall confidence (0-1)\n};\n\n/** Citation configuration for nodes */\nexport type CitationConfig = {\n enabled: boolean; // Enable citation tracking (default: false)\n includeTextSnippets?: boolean; // Include text snippets in citations (default: true)\n includeBoundingBoxes?: boolean; // Include bboxes when available (default: true)\n includeConfidence?: boolean; // Include confidence scores (default: true)\n minConfidence?: number; // Minimum confidence threshold (0-1, default: 0.0)\n detectInferred?: boolean; // Use LLM to detect inferred values (default: false)\n};\n\n/** Extended output with citations */\nexport type OutputWithCitations<T> = {\n data: T; // Extracted data\n citations: FieldCitation[]; // Field-level citations\n metadata: {\n totalPages?: number; // Total pages processed\n sourceType: CitationSourceType; // Primary source type\n hasInferredValues?: boolean; // Whether any values were inferred\n processingTime?: number; // Processing time in ms\n };\n};\n\n/** Node configuration types */\nexport type ParseNodeConfig = {\n provider: OCRProvider | VLMProvider;\n consensus?: ConsensusConfig;\n chunked?: {\n maxPagesPerChunk: number;\n overlap?: number; // Default: 0\n parallel?: boolean; // Default: true - process chunks in parallel for speed\n };\n format?: 'text' | 'markdown' | 'html'; // Output format: text (default, line-level citations), markdown/html (page-level citations, preserves structure)\n describeFigures?: boolean; // When true, VLM providers describe charts/figures/diagrams in text. Default: false\n includeImages?: boolean; // When true, providers extract images (figures/tables/charts) from documents. Supported by Surya/Marker. Default: false\n additionalPrompt?: string; // Custom OCR guidance or instructions\n citations?: CitationConfig; // Citation tracking config\n\n // NEW: Prompt asset support\n promptRef?: string; // Reference to prompt asset (e.g., \"default-parse@1.0.0\")\n /**\n * Optional custom variables for prompt rendering (e.g., language, strictMode, tenantId).\n *\n * Auto-injected variables (no need to pass manually):\n * - format: From config.format\n * - schema: Constructed schema (if applicable)\n * - describeFigures: From config.describeFigures\n * - citationsEnabled: From config.citations?.enabled\n *\n * Use promptVariables only for runtime context (localization, multi-tenancy, behavioral flags).\n */\n promptVariables?: Record<string, any>;\n\n /**\n * Additional instructions to append to the default prompt.\n * This provides a simple way to customize the prompt without creating a custom prompt asset.\n * The instructions will be added after the main prompt content.\n *\n * @example\n * ```typescript\n * parse({\n * provider: vlmProvider,\n * format: 'markdown',\n * additionalInstructions: \"Pay special attention to preserving table structures and footnotes.\"\n * })\n * ```\n */\n additionalInstructions?: string;\n\n /**\n * When using promptRef, automatically inject format instruction if {{format}} placeholder is not present.\n * This ensures the UI format selection always takes effect.\n * Default: true\n *\n * @example\n * ```typescript\n * parse({\n * provider: vlmProvider,\n * format: 'markdown',\n * promptRef: 'my-custom-prompt',\n * autoInjectFormat: false // Disable auto-injection\n * })\n * ```\n */\n autoInjectFormat?: boolean;\n\n /**\n * Enable extended reasoning/thinking for VLM providers that support it.\n * Only applies when using a VLM provider (not OCR).\n *\n * @example\n * ```typescript\n * parse({\n * provider: vlmProvider,\n * format: 'markdown',\n * reasoning: { enabled: true, effort: 'medium' }\n * })\n * ```\n */\n reasoning?: {\n effort?: 'low' | 'medium' | 'high';\n exclude?: boolean;\n enabled?: boolean;\n };\n\n /**\n * Maximum tokens for the LLM response.\n * If not specified, the provider's default will be used (typically 4096).\n */\n maxTokens?: number;\n};\n\nexport type SplitNodeConfig = {\n provider: VLMProvider;\n\n /**\n * Simple category definitions (recommended).\n * Each category can be a string or an object with name and optional description.\n *\n * @example\n * ```typescript\n * split({\n * provider: vlmProvider,\n * categories: [\n * 'invoice',\n * { name: 'cover_letter', description: 'Cover letter or transmittal pages' },\n * { name: 'contract', description: 'Legal agreements with terms and signatures' }\n * ]\n * })\n * ```\n */\n categories?: (string | { name: string; description?: string })[];\n\n /**\n * @deprecated Use `categories` instead. Full schema definitions for backwards compatibility.\n * Schema names are used as category names, but schemas are no longer attached to output.\n */\n schemas?: Record<string, object>; // { invoice: Schema, bunker: Schema }\n\n includeOther?: boolean; // Default: true\n consensus?: ConsensusConfig;\n schemaRef?: string; // Reference to schema asset (e.g., \"document-split@2.0.0\")\n\n /**\n * Enable extended reasoning/thinking for providers that support it.\n *\n * @example\n * ```typescript\n * split({\n * provider: vlmProvider,\n * categories: ['invoice', 'receipt', 'contract'],\n * reasoning: { enabled: true, effort: 'high' }\n * })\n * ```\n */\n reasoning?: {\n effort?: 'low' | 'medium' | 'high';\n exclude?: boolean;\n enabled?: boolean;\n };\n\n /**\n * Maximum tokens for the LLM response.\n * If not specified, the provider's default will be used (typically 4096).\n */\n maxTokens?: number;\n};\n\nexport type CategorizeNodeConfig = {\n provider: LLMProvider | VLMProvider;\n categories: (string | { name: string; description?: string })[];\n consensus?: ConsensusConfig;\n additionalPrompt?: string; // Custom categorization instructions\n\n // NEW: Prompt asset support\n promptRef?: string; // Reference to prompt asset (e.g., \"default-categorize@1.0.0\")\n /**\n * Optional custom variables for prompt rendering (e.g., language, strictMode, tenantId).\n *\n * Auto-injected variables (no need to pass manually):\n * - categories: From config.categories\n * - documentText: Computed from DocumentIR input\n *\n * Use promptVariables only for runtime context (localization, multi-tenancy, behavioral flags).\n */\n promptVariables?: Record<string, any>;\n\n /**\n * Additional instructions to append to the default prompt.\n * This provides a simple way to customize the prompt without creating a custom prompt asset.\n * The instructions will be added after the main prompt content.\n *\n * @example\n * ```typescript\n * categorize({\n * provider: llmProvider,\n * categories: ['invoice', 'receipt', 'contract'],\n * additionalInstructions: \"Consider the document's header and footer when categorizing.\"\n * })\n * ```\n */\n additionalInstructions?: string;\n\n /**\n * Enable extended reasoning/thinking for providers that support it.\n *\n * @example\n * ```typescript\n * categorize({\n * provider: vlmProvider,\n * categories: ['invoice', 'receipt', 'contract'],\n * reasoning: { enabled: true, effort: 'low' }\n * })\n * ```\n */\n reasoning?: {\n effort?: 'low' | 'medium' | 'high';\n exclude?: boolean;\n enabled?: boolean;\n };\n\n /**\n * Maximum tokens for the LLM response.\n * If not specified, the provider's default will be used (typically 4096).\n */\n maxTokens?: number;\n};\n\n/**\n * Controls what inputs the extract node ingests.\n * - 'auto': Automatically detect input type and route appropriately (default)\n * - 'ir': Only DocumentIR from previous step (text-only extraction)\n * - 'ir+source': Both DocumentIR AND source document (multimodal with parsed text)\n * - 'source': Only raw source document (direct VLM extraction, no parsed text)\n *\n * Auto mode logic:\n * - If DocumentIR available AND source available AND VLM provider -> 'ir+source'\n * - If only DocumentIR available -> 'ir'\n * - If only FlowInput available AND VLM provider -> 'source'\n */\nexport type ExtractInputMode = 'auto' | 'ir' | 'ir+source' | 'source';\n\nexport type ExtractNodeConfig<T = any> = {\n provider: LLMProvider | VLMProvider;\n schema: object | EnhancedExtractionSchema<T> | { ref: string }; // Accept plain, enhanced, or reference\n consensus?: ConsensusConfig;\n reasoning?: {\n effort?: 'low' | 'medium' | 'high';\n exclude?: boolean;\n enabled?: boolean;\n };\n additionalPrompt?: string; // Custom extraction instructions (appended after schema)\n citations?: CitationConfig; // Citation tracking config\n\n // NEW: Prompt asset support\n promptRef?: string; // Reference to prompt asset (e.g., \"default-extraction@1.0.0\")\n /**\n * Optional custom variables for prompt rendering (e.g., language, strictMode, tenantId).\n *\n * Auto-injected variables (no need to pass manually):\n * - schema: From config.schema\n * - documentText: Computed from DocumentIR or FlowInput\n * - schemaTitle: From schema.title or default \"the provided schema\"\n * - schemaDescription: From schema.description or empty string\n * - structuredFormat: Generated formatting instructions (for markdown/html)\n *\n * Use promptVariables only for runtime context (localization, multi-tenancy, behavioral flags).\n */\n promptVariables?: Record<string, any>;\n\n /**\n * Additional instructions to append to the default prompt.\n * This provides a simple way to customize the prompt without creating a custom prompt asset.\n * The instructions will be added after the main prompt content.\n *\n * @example\n * ```typescript\n * extract({\n * provider: llmProvider,\n * schema: mySchema,\n * additionalInstructions: \"Be strict with date formats. Use YYYY-MM-DD format only.\"\n * })\n * ```\n */\n additionalInstructions?: string;\n\n /**\n * Controls what inputs the extract node ingests.\n * - 'auto': Automatically detect input type and route appropriately (default)\n * - 'ir': Only DocumentIR from previous step (text-only extraction)\n * - 'ir+source': Both DocumentIR AND source document (multimodal with parsed text)\n * - 'source': Only raw source document (direct VLM extraction, no parsed text)\n * @default 'auto'\n */\n inputMode?: ExtractInputMode;\n\n /**\n * In split/forEach contexts, use the original unsplit document instead of the segment.\n * Only applies when inputMode includes source ('ir+source' or 'source').\n * @default false (uses split segment source)\n */\n useOriginalSource?: boolean;\n\n /**\n * When auto mode has both IR and source available with VLM provider:\n * - true: use 'ir+source' for maximum context (hybrid multimodal)\n * - false: use 'ir' for text-only extraction (lower cost)\n * Only applies when inputMode='auto'.\n * @default true\n */\n preferVisual?: boolean;\n\n /**\n * Maximum tokens for the LLM response.\n * If not specified, the provider's default will be used (typically 4096).\n */\n maxTokens?: number;\n};\n\n/** Chunk output structure */\nexport type ChunkMetadata = {\n // Core content\n content: string;\n id: string; // Unique chunk identifier\n\n // Position metadata\n index: number; // Chunk position in sequence\n startChar: number;\n endChar: number;\n\n // Document context\n pageNumbers: number[]; // Pages this chunk spans\n section?: string; // Section/chapter title\n headers?: string[]; // Hierarchy of headers above this chunk\n\n // Chunking metadata\n strategy: string; // Which strategy created this chunk\n tokenCount?: number; // For LLM context planning\n wordCount: number;\n charCount: number;\n};\n\nexport type ChunkOutput = {\n chunks: ChunkMetadata[];\n totalChunks: number;\n averageChunkSize: number;\n sourceMetadata?: {\n providerType?: string; // 'ocr' | 'vlm' - original provider type\n };\n sourceDocument?: DocumentIR; // Original DocumentIR for citation mapping\n};\n\nexport type ChunkNodeConfig = {\n strategy: 'recursive' | 'section' | 'page' | 'fixed';\n maxSize?: number; // Max characters per chunk (recursive, section)\n minSize?: number; // Min characters per chunk (default: 100)\n overlap?: number; // Character overlap between chunks (default: 0)\n separators?: string[]; // Hierarchical separators (recursive)\n pagesPerChunk?: number; // Pages per chunk (page strategy)\n combineShortPages?: boolean; // Combine short pages (page strategy)\n minPageContent?: number; // Min content length to keep page (page strategy)\n size?: number; // Fixed size for fixed strategy\n unit?: 'tokens' | 'characters'; // Unit for fixed strategy\n};\n\nexport type CombineNodeConfig = {\n strategy: 'merge' | 'concatenate' | 'first' | 'last';\n};\n\nexport type OutputNodeConfig = {\n source?: string | string[];\n transform?: 'first' | 'last' | 'merge' | 'pick' | 'custom';\n fields?: string[];\n name?: string;\n /**\n * Custom transform function for 'custom' transform mode.\n * @param inputs - The input value(s) from the source step(s)\n * @param artifacts - All artifacts from the flow execution\n * @returns The transformed output value\n */\n customTransform?: (inputs: unknown | unknown[], artifacts: Record<string, unknown>) => unknown;\n};\n\n/** Enhanced extraction schema with examples and guidance */\nexport type EnhancedExtractionSchema<T = unknown> = {\n // Core schema (JSON Schema or Zod schema)\n schema: object;\n\n // Optional extraction enhancements\n examples?: Array<{\n description: string; // Description of this example\n input: string; // Sample input text\n output: T; // Expected output matching schema\n }>;\n\n extractionRules?: string; // Extraction guidelines (e.g., \"Focus on tables in appendix\")\n contextPrompt?: string; // Document context (e.g., \"This is a legal document\")\n hints?: string[]; // Additional hints for the extractor\n};\n\n/** Node & runner */\nexport type StepMetric = {\n step: string;\n configStepId?: string; // Flow-level step ID for config lookups (schemaRef, promptRef)\n startMs: number; // Absolute timestamp when step started (Date.now())\n provider?: string;\n model?: string;\n ms: number; // Total duration; for wrappers with rollup=true, includes child work\n costUSD?: number;\n inputTokens?: number;\n outputTokens?: number;\n cacheCreationInputTokens?: number;\n cacheReadInputTokens?: number;\n attemptNumber?: number; // Retry attempt number (1 = first attempt, 2+ = retries)\n metadata?: {\n kind?: 'leaf' | 'wrapper' | 'prep'; // 'leaf' = actual LLM call, 'wrapper' = composite overhead, 'prep' = preparation step\n rollup?: boolean; // True if ms includes child work (for wrappers with children)\n overheadMs?: number; // Pure overhead time excluding child work (for wrappers with children)\n /** Additional metadata fields */\n [key: string]: string | number | boolean | undefined;\n };\n};\n\n/** Aggregated metrics for multi-step flows */\nexport interface AggregatedMetrics {\n totalDurationMs: number;\n totalCostUSD: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n totalCacheCreationTokens: number;\n totalCacheReadTokens: number;\n stepCount: number;\n byProvider: Record<string, {\n costUSD: number;\n inputTokens: number;\n outputTokens: number;\n callCount: number;\n }>;\n}\n\n/**\n * Aggregate metrics from multiple steps\n * @param metrics - Array of step metrics\n * @returns Aggregated totals and per-provider breakdowns\n */\nexport function aggregateMetrics(metrics: StepMetric[]): AggregatedMetrics {\n const byProvider: Record<string, {\n costUSD: number;\n inputTokens: number;\n outputTokens: number;\n callCount: number;\n }> = {};\n\n const result = metrics.reduce((acc, m) => {\n acc.totalDurationMs += m.ms;\n acc.totalCostUSD += m.costUSD || 0;\n acc.totalInputTokens += m.inputTokens || 0;\n acc.totalOutputTokens += m.outputTokens || 0;\n acc.totalCacheCreationTokens += m.cacheCreationInputTokens || 0;\n acc.totalCacheReadTokens += m.cacheReadInputTokens || 0;\n\n // Group by provider\n if (m.provider) {\n if (!byProvider[m.provider]) {\n byProvider[m.provider] = { costUSD: 0, inputTokens: 0, outputTokens: 0, callCount: 0 };\n }\n byProvider[m.provider].costUSD += m.costUSD || 0;\n byProvider[m.provider].inputTokens += m.inputTokens || 0;\n byProvider[m.provider].outputTokens += m.outputTokens || 0;\n byProvider[m.provider].callCount += 1;\n }\n\n return acc;\n }, {\n totalDurationMs: 0,\n totalCostUSD: 0,\n totalInputTokens: 0,\n totalOutputTokens: 0,\n totalCacheCreationTokens: 0,\n totalCacheReadTokens: 0,\n stepCount: metrics.length,\n byProvider\n });\n\n return result;\n}\n\n/**\n * Execution context passed to conditional functions and trigger nodes\n * Provides access to artifacts and metrics from all previous steps\n */\nexport interface FlowContext {\n /** Outputs from all completed steps, indexed by step ID */\n artifacts: Record<string, any>;\n /** Performance metrics from all completed steps */\n metrics: StepMetric[];\n /** Call stack for tracking nested flow execution (for circular dependency detection) */\n callStack?: string[];\n /** Maximum nesting depth for flow triggers (default: 10) */\n maxDepth?: number;\n}\n\n/**\n * W3C Trace Context for distributed tracing.\n * Compatible with observability module's TraceContext.\n */\nexport interface TraceContextLite {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n traceFlags: number; // W3C trace flags (0x01 = sampled), required for compatibility\n traceState?: string;\n}\n\n/**\n * Observability context passed to node executions.\n * Uses 'any' for config and traceContext to avoid circular imports and\n * maintain compatibility with the full observability types.\n */\nexport type NodeObservabilityContext = {\n /** Observability configuration - full type in observability module */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n config?: any;\n flowId?: string;\n executionId?: string;\n stepId?: string;\n stepIndex?: number;\n /** W3C Trace Context - compatible with TraceContext from observability module */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n traceContext?: any;\n metadata?: Record<string, unknown>;\n};\n\nexport type NodeCtx = {\n stepId?: string; // Flow-level step ID for metrics tracking\n artifacts: Record<string, unknown>;\n emit: (key: string, value: unknown) => void;\n metrics: { push: (m: StepMetric) => void };\n /** Observability context for hooks (optional) */\n observability?: NodeObservabilityContext;\n};\n\n/** Node type metadata for runtime validation */\nexport type NodeTypeInfo = {\n /** Input types this node accepts (e.g., ['FlowInput', 'DocumentIR']) */\n inputTypes: string[];\n /**\n * Output type this node produces - can be string or function for config-dependent types.\n * When a function, it receives the node's specific config and returns the output type string.\n * Uses 'any' parameter to allow nodes to use their specific config types.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n outputType: string | ((config: any) => string);\n /** Provider types this node requires (if any) */\n requiresProvider?: ('OCR' | 'VLM' | 'LLM')[];\n /** Whether this node can accept array input */\n acceptsArray?: boolean;\n /**\n * Whether this node always outputs an array (or function for config-dependent).\n * Uses 'any' parameter to allow nodes to use their specific config types.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n outputsArray?: boolean | ((config: any) => boolean);\n /** Human-readable description of what this node does */\n description?: string;\n};\n\nexport type NodeDef<I, O> = {\n key: string;\n run: (input: I, ctx: NodeCtx) => Promise<O>;\n /** Optional type metadata for validation */\n __meta?: NodeTypeInfo;\n};\n\nexport const node = <I, O>(key: string, run: NodeDef<I, O>[\"run\"]): NodeDef<I, O> => ({ key, run });\n\nexport async function runPipeline(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n steps: NodeDef<any, any>[],\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n input: any,\n observabilityContext?: NodeObservabilityContext,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n flowArtifacts?: Record<string, any>\n) {\n // Merge flow artifacts with local (flow artifacts as read-only base for source access)\n const artifacts: Record<string, unknown> = flowArtifacts ? { ...flowArtifacts } : {};\n const metrics: StepMetric[] = [];\n const ctx: NodeCtx = {\n stepId: observabilityContext?.stepId,\n artifacts,\n emit: (k, v) => { artifacts[k] = v; },\n metrics: { push: (m) => metrics.push(m) },\n observability: observabilityContext\n };\n let acc = input;\n for (const s of steps) {\n acc = await s.run(acc, ctx);\n ctx.emit(s.key, acc);\n }\n return { output: acc, artifacts, metrics };\n}\n\n/**\n * Flow execution error with step context\n *\n * Thrown when a flow step fails during execution. Includes:\n * - Which step failed (ID, index, type)\n * - Which steps completed successfully\n * - Partial artifacts from completed steps (for debugging)\n * - The original error that caused the failure\n *\n * This makes debugging flow failures much easier by showing exactly where the error occurred\n * and what data was produced before the failure.\n *\n * @example\n * ```typescript\n * try {\n * await flow.run(input);\n * } catch (error) {\n * if (error instanceof FlowExecutionError) {\n * console.error(`Failed at step ${error.failedStepIndex}: ${error.failedStepType}`);\n * console.error(`Step ID: ${error.failedStep}`);\n * console.error(`Completed: ${error.completedSteps.join(', ')}`);\n * console.error(`Original error: ${error.originalError.message}`);\n *\n * // Access partial results from completed steps\n * if (error.partialArtifacts?.qualify) {\n * console.log('Quality assessment completed:', error.partialArtifacts.qualify);\n * }\n * }\n * }\n * ```\n */\n\n/**\n * Extracts a human-readable error message from potentially JSON error responses.\n *\n * Handles common API error formats:\n * - { \"detail\": \"...\" } (Surya-style)\n * - { \"error\": { \"message\": \"...\" } } (OpenAI, Anthropic)\n * - { \"error\": \"...\" } (Simple format)\n * - { \"message\": \"...\" } (Direct format)\n * - Plain text (returned as-is)\n *\n * @param errorText - The error text which may contain JSON\n * @returns A human-readable error message\n */\nexport function extractErrorMessage(errorText: string): string {\n // If it's short or doesn't look like JSON, return as-is\n if (errorText.length < 10 || !errorText.trim().startsWith('{')) {\n return errorText;\n }\n\n try {\n const parsed = JSON.parse(errorText);\n\n // Surya-style: { \"detail\": \"...\" }\n if (parsed.detail) {\n return parsed.detail;\n }\n\n // OpenAI/Anthropic style: { error: { message: \"...\" } }\n if (parsed.error?.message) {\n return parsed.error.message;\n }\n\n // Simple style: { error: \"...\" }\n if (typeof parsed.error === 'string') {\n return parsed.error;\n }\n\n // Direct style: { message: \"...\" }\n if (parsed.message) {\n return parsed.message;\n }\n\n // Google style: { error: { status: \"...\", message: \"...\" } }\n if (parsed.error?.status && parsed.error?.message) {\n return `${parsed.error.status}: ${parsed.error.message}`;\n }\n\n // Fallback: return original but truncated if very long\n return errorText.length > 200\n ? errorText.substring(0, 200) + '...'\n : errorText;\n } catch {\n // Not valid JSON, return as-is (truncated if needed)\n return errorText.length > 500\n ? errorText.substring(0, 500) + '...'\n : errorText;\n }\n}\n\n/**\n * Represents a step location in a flow hierarchy.\n * Used to track the execution path through nested flows.\n */\nexport interface FlowStepLocation {\n /** Step ID */\n stepId: string;\n /** Step index within this flow (0-based) */\n stepIndex: number;\n /** Step type (e.g., 'parse', 'conditional', 'forEach') */\n stepType: string;\n /** Branch name if within a conditional (e.g., \"Invoice\", \"Receipt\") */\n branch?: string;\n /** Item index if within a forEach iteration */\n itemIndex?: number;\n}\n\nexport class FlowExecutionError extends Error {\n constructor(\n message: string,\n /** The ID of the step that failed (e.g., 'parse_node123') */\n public readonly failedStep: string,\n /** The index of the failed step in the flow (0-based) */\n public readonly failedStepIndex: number,\n /** The type of the failed step (e.g., 'parse', 'extract', 'step', 'conditional', 'forEach') */\n public readonly failedStepType: string,\n /** Array of step IDs that completed successfully before the failure */\n public readonly completedSteps: string[],\n /** The original error that caused the failure */\n public readonly originalError: Error,\n /** Partial artifacts from steps that completed before the failure */\n public readonly partialArtifacts?: Record<string, any>,\n /** Execution path through nested flows (for hierarchical context) */\n public readonly flowPath?: FlowStepLocation[],\n /** All completed steps aggregated across flow boundaries */\n public readonly allCompletedSteps?: string[]\n ) {\n super(message);\n this.name = 'FlowExecutionError';\n\n // Maintain proper stack trace for V8 engines\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, FlowExecutionError);\n }\n }\n\n /**\n * Returns a formatted string showing the execution path.\n * Example: \"parse → conditional:Invoice → extract\"\n */\n getFormattedPath(): string {\n if (!this.flowPath || this.flowPath.length === 0) {\n return this.failedStep;\n }\n\n return this.flowPath.map(loc => {\n let label = loc.stepId;\n if (loc.branch) {\n label += `:${loc.branch}`;\n }\n if (loc.itemIndex !== undefined) {\n label += `[${loc.itemIndex}]`;\n }\n return label;\n }).join(' → ');\n }\n\n /**\n * Returns the root cause error (innermost originalError).\n * Useful when errors are nested multiple levels deep.\n */\n getRootCause(): Error {\n let cause: Error = this.originalError;\n while (cause instanceof FlowExecutionError && cause.originalError) {\n cause = cause.originalError;\n }\n return cause;\n }\n}\n\n/**\n * Flow validation error for invalid node connections\n *\n * Thrown when building a flow with incompatible node connections.\n * Provides helpful error messages and suggestions for fixing the issue.\n *\n * @example\n * ```typescript\n * try {\n * const flow = createFlow()\n * .step('parse', parse({ provider: ocrProvider }))\n * .step('combine', combine()) // Invalid: combine needs array input\n * .build();\n * } catch (error) {\n * if (error instanceof FlowValidationError) {\n * console.error(error.message);\n * console.error('Reason:', error.reason);\n * console.log('Suggestions:', error.suggestions?.join('\\n'));\n * }\n * }\n * ```\n */\nexport class FlowValidationError extends Error {\n constructor(\n message: string,\n public readonly reason?: string,\n public readonly suggestions?: string[],\n public readonly sourceNode?: string,\n public readonly targetNode?: string,\n public readonly sourceOutputType?: string,\n public readonly targetInputTypes?: string[]\n ) {\n super(message);\n this.name = 'FlowValidationError';\n\n // Maintain proper stack trace for V8 engines\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, FlowValidationError);\n }\n }\n}\n\n/** Node type names for validation */\nexport type NodeTypeName = 'parse' | 'split' | 'categorize' | 'extract' | 'chunk' | 'combine' | 'trigger' | 'output';\n\n/** Compatibility rule for node connections */\nexport type CompatibilityRule = {\n valid: boolean;\n requiresForEach?: boolean;\n /** Indicates this connection cannot be fully validated at build-time and requires runtime type checking */\n requiresRuntimeValidation?: boolean;\n reason?: string;\n note?: string;\n};\n\n/**\n * Node Compatibility Matrix\n *\n * Defines which nodes can connect to which other nodes.\n * This is the single source of truth for node connection validation.\n *\n * Rules based on input/output type compatibility:\n * - parse: FlowInput → DocumentIR (or DocumentIR[] if chunked)\n * - split: FlowInput → SplitDocument[] (requires forEach)\n * - categorize: DocumentIR|FlowInput → {input, category}\n * - extract: DocumentIR|FlowInput|ChunkOutput → T (typed JSON)\n * - chunk: DocumentIR|DocumentIR[] → ChunkOutput\n * - combine: T[] → T|T[] (merges forEach results)\n * - trigger: any → TOutput (depends on child flow)\n *\n * Special behaviors:\n * - forEach auto-unwraps SplitDocument.input → FlowInput\n * - Conditional auto-unwraps {input, category} → input\n * - parse with chunked:true outputs DocumentIR[] instead of DocumentIR\n */\nexport const NODE_COMPATIBILITY_MATRIX: Record<NodeTypeName, Record<NodeTypeName, CompatibilityRule>> = {\n parse: {\n parse: {\n valid: false,\n reason: 'Cannot chain parse nodes. Parse is typically the starting node.'\n },\n split: {\n valid: false,\n reason: 'Split requires FlowInput, but parse outputs DocumentIR. Use split directly on input instead.',\n note: 'If you need to re-split after parsing, use trigger to invoke a child flow with FlowInput.'\n },\n categorize: {\n valid: true,\n note: 'categorize accepts DocumentIR and wraps it with {input, category}'\n },\n extract: {\n valid: true,\n note: 'extract accepts DocumentIR and produces typed JSON'\n },\n chunk: {\n valid: true,\n note: 'chunk accepts DocumentIR and produces ChunkOutput for RAG'\n },\n combine: {\n valid: false,\n reason: 'Parse outputs DocumentIR (single document), not an array. Combine requires array input from forEach.',\n note: 'Use parse with chunked:true to output DocumentIR[], then use combine.'\n },\n trigger: {\n valid: true,\n note: 'trigger accepts any input type'\n },\n output: {\n valid: true,\n note: 'output node can follow any node to select or transform results'\n }\n },\n split: {\n parse: {\n valid: true,\n requiresForEach: true,\n reason: 'Split outputs SplitDocument[] which requires forEach. forEach auto-unwraps SplitDocument.input → FlowInput for parse.',\n note: 'Enable forEach on split node before connecting to parse.'\n },\n split: {\n valid: false,\n reason: 'Cannot nest split operations. Split nodes cannot appear in forEach itemFlow.'\n },\n categorize: {\n valid: true,\n requiresForEach: true,\n reason: 'Split outputs SplitDocument[] which requires forEach. forEach auto-unwraps SplitDocument.input for categorize.'\n },\n extract: {\n valid: true,\n requiresForEach: true,\n reason: 'Split outputs SplitDocument[] which requires forEach. forEach auto-unwraps SplitDocument.input for extract.'\n },\n chunk: {\n valid: false,\n reason: 'SplitDocument output is incompatible with Chunk input. Chunk expects DocumentIR or DocumentIR[].',\n note: 'Use parse in forEach after split to convert SplitDocument → DocumentIR, then chunk.'\n },\n combine: {\n valid: false,\n reason: 'Combine should appear AFTER forEach completes, not as a forEach itemFlow step.',\n note: 'Place combine after the forEach block to merge results.'\n },\n trigger: {\n valid: true,\n requiresForEach: true,\n reason: 'Split outputs SplitDocument[] which requires forEach for processing.',\n note: 'forEach auto-unwraps SplitDocument.input for child flow.'\n },\n output: {\n valid: true,\n note: 'output node can follow any node to select or transform results'\n }\n },\n categorize: {\n parse: {\n valid: true,\n note: 'categorize outputs {input, category}. Conditional can unwrap this or use directly.'\n },\n split: {\n valid: false,\n reason: 'Split requires FlowInput, but categorize outputs {input, category}.',\n note: 'Use conditional to unwrap and pass input field to split.'\n },\n categorize: {\n valid: true,\n note: 'Can chain categorize nodes for multi-level classification.'\n },\n extract: {\n valid: true,\n note: 'extract can process the categorized document.'\n },\n chunk: {\n valid: false,\n reason: 'Categorize wraps input as {input, category}. Chunk needs unwrapped DocumentIR.',\n note: 'Use conditional to unwrap input field before chunk.'\n },\n combine: {\n valid: false,\n reason: 'Categorize outputs single result {input, category}, not an array. Combine requires array input.'\n },\n trigger: {\n valid: true,\n note: 'trigger accepts any input type, including {input, category}'\n },\n output: {\n valid: true,\n note: 'output node can follow any node to select or transform results'\n }\n },\n extract: {\n parse: {\n valid: false,\n reason: 'Extract outputs typed JSON (terminal node). Cannot pipe JSON to parse.',\n note: 'Extract should be one of the last steps in a flow. Use combine if extracting in parallel.'\n },\n split: {\n valid: false,\n reason: 'Extract outputs typed JSON (terminal node). Cannot pipe JSON to split.'\n },\n categorize: {\n valid: false,\n reason: 'Extract outputs typed JSON (terminal node). Cannot pipe JSON to categorize.'\n },\n extract: {\n valid: false,\n reason: 'Extract outputs typed JSON (terminal node). Cannot chain extractions on JSON output.',\n note: 'If you need multi-step extraction, extract from DocumentIR/ChunkOutput in parallel, then combine.'\n },\n chunk: {\n valid: false,\n reason: 'Extract outputs typed JSON, not DocumentIR. Chunk expects DocumentIR input.'\n },\n combine: {\n valid: true,\n note: 'Use combine to merge parallel extraction results from forEach.'\n },\n trigger: {\n valid: true,\n note: 'trigger accepts any input type, including extracted JSON'\n },\n output: {\n valid: true,\n note: 'output node can follow any node to select or transform results'\n }\n },\n chunk: {\n parse: {\n valid: false,\n reason: 'Chunk outputs ChunkOutput (specialized type), not FlowInput. Parse expects FlowInput as input.'\n },\n split: {\n valid: false,\n reason: 'Chunk outputs ChunkOutput, incompatible with Split input (FlowInput).'\n },\n categorize: {\n valid: false,\n reason: 'Chunk outputs ChunkOutput, incompatible with Categorize input (DocumentIR|FlowInput).',\n note: 'Categorize before chunking, not after.'\n },\n extract: {\n valid: true,\n note: 'extract has special handling for ChunkOutput - extracts data from chunks.'\n },\n chunk: {\n valid: false,\n reason: 'Cannot chain chunk operations. Chunk only once per document.',\n note: 'Different chunking strategies should be applied to the original DocumentIR, not to chunks.'\n },\n combine: {\n valid: false,\n reason: 'Chunk outputs ChunkOutput (specialized type), not an array type. Combine expects T[].',\n note: 'Use chunk on individual documents in forEach, then extract, then combine extractions.'\n },\n trigger: {\n valid: true,\n note: 'trigger accepts any input type, including ChunkOutput'\n },\n output: {\n valid: true,\n note: 'output node can follow any node to select or transform results'\n }\n },\n combine: {\n parse: {\n valid: true,\n note: 'After combining, result can be re-parsed if needed.'\n },\n split: {\n valid: false,\n reason: 'Combine output depends on strategy. Split requires FlowInput.',\n note: 'Most combine strategies output merged objects/arrays, not FlowInput.'\n },\n categorize: {\n valid: true,\n note: 'Can categorize combined results.'\n },\n extract: {\n valid: true,\n note: 'Can extract from combined results.'\n },\n chunk: {\n valid: true,\n note: 'Can chunk combined DocumentIR. Only valid if combine output is DocumentIR or DocumentIR[].'\n },\n combine: {\n valid: false,\n reason: 'Cannot chain combine nodes. Combine once per forEach operation.'\n },\n trigger: {\n valid: true,\n note: 'trigger accepts any input type'\n },\n output: {\n valid: true,\n note: 'output node can follow any node to select or transform results'\n }\n },\n trigger: {\n parse: {\n valid: true,\n requiresRuntimeValidation: true,\n note: 'Valid only if child flow returns FlowInput. Type safety cannot be guaranteed at build-time.'\n },\n split: {\n valid: true,\n requiresRuntimeValidation: true,\n note: 'Valid only if child flow returns FlowInput. Type safety cannot be guaranteed at build-time.'\n },\n categorize: {\n valid: true,\n requiresRuntimeValidation: true,\n note: 'Valid only if child flow returns DocumentIR or FlowInput. Type safety cannot be guaranteed at build-time.'\n },\n extract: {\n valid: true,\n requiresRuntimeValidation: true,\n note: 'Valid only if child flow returns DocumentIR, FlowInput, or ChunkOutput. Type safety cannot be guaranteed at build-time.'\n },\n chunk: {\n valid: true,\n requiresRuntimeValidation: true,\n note: 'Valid only if child flow returns DocumentIR or DocumentIR[]. Type safety cannot be guaranteed at build-time.'\n },\n combine: {\n valid: true,\n requiresRuntimeValidation: true,\n note: 'Valid only if child flow returns an array (T[]). Type safety cannot be guaranteed at build-time.'\n },\n trigger: {\n valid: true,\n requiresRuntimeValidation: true,\n note: 'Can nest trigger nodes (with circular dependency detection and max depth limits). Output type depends on nested child flow.'\n },\n output: {\n valid: true,\n note: 'output node can follow any node to select or transform results'\n }\n },\n output: {\n parse: {\n valid: false,\n reason: 'Output is a terminal node that selects/transforms results. Cannot chain to other nodes.'\n },\n split: {\n valid: false,\n reason: 'Output is a terminal node that selects/transforms results. Cannot chain to other nodes.'\n },\n categorize: {\n valid: false,\n reason: 'Output is a terminal node that selects/transforms results. Cannot chain to other nodes.'\n },\n extract: {\n valid: false,\n reason: 'Output is a terminal node that selects/transforms results. Cannot chain to other nodes.'\n },\n chunk: {\n valid: false,\n reason: 'Output is a terminal node that selects/transforms results. Cannot chain to other nodes.'\n },\n combine: {\n valid: false,\n reason: 'Output is a terminal node that selects/transforms results. Cannot chain to other nodes.'\n },\n trigger: {\n valid: false,\n reason: 'Output is a terminal node that selects/transforms results. Cannot chain to other nodes.'\n },\n output: {\n valid: true,\n note: 'Multiple output nodes are allowed to create multiple named outputs from a flow.'\n }\n }\n};\n\n/**\n * Get node type name from a NodeDef\n * @param node - Node definition\n * @returns Node type name (e.g., 'parse', 'extract')\n */\nexport function getNodeTypeName(node: NodeDef<any, any>): NodeTypeName | null {\n if (!node || !node.key) return null;\n const key = node.key;\n\n // Check if it's a known node type\n const knownTypes: NodeTypeName[] = ['parse', 'split', 'categorize', 'extract', 'chunk', 'combine', 'trigger', 'output'];\n return knownTypes.includes(key as NodeTypeName) ? (key as NodeTypeName) : null;\n}\n\n/**\n * Get type information from a node\n * @param node - Node definition\n * @returns NodeTypeInfo if available\n */\nexport function getNodeTypeInfo(node: NodeDef<any, any>): NodeTypeInfo | null {\n return node.__meta || null;\n}\n\n/**\n * Get compatible target nodes for a given source node\n * @param sourceType - Source node type name\n * @param includeForEach - Include connections that require forEach\n * @returns Array of compatible target node types\n */\nexport function getCompatibleTargets(sourceType: NodeTypeName, includeForEach: boolean = false): NodeTypeName[] {\n const rules = NODE_COMPATIBILITY_MATRIX[sourceType];\n if (!rules) return [];\n\n return Object.entries(rules)\n .filter(([_, rule]) => {\n if (!rule.valid) return false;\n if (rule.requiresForEach && !includeForEach) return false;\n return true;\n })\n .map(([targetType, _]) => targetType as NodeTypeName);\n}\n\n/**\n * Get suggested connections when a connection is invalid\n * @param sourceType - Source node type name\n * @returns Array of suggestion strings\n */\nexport function getSuggestedConnections(sourceType: NodeTypeName): string[] {\n const compatibleTargets = getCompatibleTargets(sourceType, false);\n const forEachTargets = getCompatibleTargets(sourceType, true).filter(\n t => !compatibleTargets.includes(t)\n );\n\n if (compatibleTargets.length === 0 && forEachTargets.length === 0) {\n return [`${sourceType} has no standard outgoing connections (terminal node).`];\n }\n\n const suggestions: string[] = [];\n\n if (compatibleTargets.length > 0) {\n suggestions.push(`${sourceType} can connect to:`);\n compatibleTargets.forEach(target => {\n const rule = NODE_COMPATIBILITY_MATRIX[sourceType][target];\n suggestions.push(` • ${target}${rule.note ? ` - ${rule.note}` : ''}`);\n });\n }\n\n if (forEachTargets.length > 0) {\n suggestions.push(`${sourceType} can connect to (with forEach enabled):`);\n forEachTargets.forEach(target => {\n const rule = NODE_COMPATIBILITY_MATRIX[sourceType][target];\n suggestions.push(` • ${target}${rule.note ? ` - ${rule.note}` : ''}`);\n });\n }\n\n return suggestions;\n}\n\n/**\n * Validation result for node connections\n */\nexport type ValidationResult = {\n valid: boolean;\n reason?: string;\n suggestions?: string[];\n requiresForEach?: boolean;\n /** Warning message for connections that are valid but require runtime type checking */\n warning?: string;\n};\n\n/**\n * Validate if two node types can be connected\n * @param sourceType - Source node type name\n * @param targetType - Target node type name\n * @param forEachEnabled - Whether forEach is enabled on the source node\n * @returns Validation result with reason and suggestions\n */\nexport function validateNodeConnection(\n sourceType: NodeTypeName,\n targetType: NodeTypeName,\n forEachEnabled: boolean = false\n): ValidationResult {\n const rule = NODE_COMPATIBILITY_MATRIX[sourceType]?.[targetType];\n\n if (!rule) {\n return {\n valid: false,\n reason: `Unknown node type combination: ${sourceType} → ${targetType}`,\n suggestions: ['Ensure both nodes are valid node types.']\n };\n }\n\n if (!rule.valid) {\n return {\n valid: false,\n reason: rule.reason,\n suggestions: getSuggestedConnections(sourceType)\n };\n }\n\n // Check forEach requirement\n if (rule.requiresForEach && !forEachEnabled) {\n return {\n valid: false,\n reason: `Cannot connect ${sourceType} to ${targetType} without forEach enabled.`,\n suggestions: [\n `Enable forEach on the ${sourceType} node:`,\n ` 1. Click the ${sourceType} node`,\n ` 2. Enable \"forEach Processing\" in the configuration`,\n ` 3. Try connecting again`,\n '',\n ...getSuggestedConnections(sourceType)\n ],\n requiresForEach: true\n };\n }\n\n // Check if runtime validation is required\n if (rule.requiresRuntimeValidation) {\n return {\n valid: true,\n warning: `⚠️ ${sourceType} → ${targetType}: ${rule.note || 'Type compatibility depends on runtime values and cannot be validated at build-time.'}`\n };\n }\n\n return {\n valid: true\n };\n}\n\n/**\n * Get valid starting nodes for forEach itemFlow based on parent node type\n *\n * When a node outputs an array and uses forEach, the itemFlow receives individual\n * array items. This function returns which node types can accept those items.\n *\n * @param parentType - The node type that outputs the array (e.g., 'split', 'parse')\n * @returns Array of node types that can start the forEach itemFlow\n *\n * @example\n * ```typescript\n * // split outputs SplitDocument[], itemFlow gets SplitDocument\n * getValidForEachStarters('split') // ['parse', 'extract', 'categorize', 'trigger']\n *\n * // parse(chunked:true) outputs DocumentIR[], itemFlow gets DocumentIR\n * getValidForEachStarters('parse') // ['categorize', 'extract', 'chunk']\n * ```\n */\nexport function getValidForEachStarters(parentType: NodeTypeName): NodeTypeName[] {\n const rules = NODE_COMPATIBILITY_MATRIX[parentType];\n if (!rules) return [];\n\n // Get all targets that require forEach (these are valid itemFlow starters)\n return Object.entries(rules)\n .filter(([_, rule]) => rule.valid && rule.requiresForEach)\n .map(([targetType, _]) => targetType as NodeTypeName);\n}\n\n/**\n * Validate if a node type can start a forEach itemFlow for a given parent\n *\n * @param parentType - The node type that outputs the array (e.g., 'split')\n * @param starterType - The node type to validate as itemFlow starter\n * @returns ValidationResult with detailed error messages and suggestions\n *\n * @example\n * ```typescript\n * // Valid: split → forEach → parse\n * canStartForEachItemFlow('split', 'parse') // { valid: true }\n *\n * // Invalid: split → forEach → chunk\n * canStartForEachItemFlow('split', 'chunk')\n * // {\n * // valid: false,\n * // reason: 'chunk cannot start forEach itemFlow after split...',\n * // suggestions: ['Valid starters: parse, extract, categorize, trigger']\n * // }\n * ```\n */\nexport function canStartForEachItemFlow(\n parentType: NodeTypeName,\n starterType: NodeTypeName\n): ValidationResult {\n const rule = NODE_COMPATIBILITY_MATRIX[parentType]?.[starterType];\n\n if (!rule) {\n return {\n valid: false,\n reason: `Unknown node type combination: ${parentType} → forEach → ${starterType}`,\n suggestions: ['Ensure both nodes are valid node types.']\n };\n }\n\n // Check if this connection requires forEach (meaning it's valid in itemFlow)\n if (rule.valid && rule.requiresForEach) {\n return {\n valid: true\n };\n }\n\n // If the rule is invalid, provide error\n if (!rule.valid) {\n const validStarters = getValidForEachStarters(parentType);\n return {\n valid: false,\n reason: `${starterType} cannot start forEach itemFlow after ${parentType}. ${rule.reason || 'Type incompatible with forEach unwrapped item.'}`,\n suggestions: validStarters.length > 0\n ? [`Valid itemFlow starters for ${parentType}: ${validStarters.join(', ')}`]\n : [`${parentType} has no valid forEach itemFlow starters.`]\n };\n }\n\n // If valid but doesn't require forEach, it's not a valid itemFlow starter\n const validStarters = getValidForEachStarters(parentType);\n return {\n valid: false,\n reason: `${starterType} cannot start forEach itemFlow after ${parentType}. This connection does not require forEach, meaning it expects the full array, not individual items.`,\n suggestions: validStarters.length > 0\n ? [`Valid itemFlow starters for ${parentType}: ${validStarters.join(', ')}`]\n : [`${parentType} has no valid forEach itemFlow starters.`]\n };\n}\n\n/**\n * JSON Schema node structure for validation.\n * Represents a node in a JSON Schema definition.\n */\nexport interface JSONSchemaNode {\n type?: string | string[];\n properties?: Record<string, JSONSchemaNode>;\n items?: JSONSchemaNode | JSONSchemaNode[];\n required?: string[];\n enum?: (string | number | boolean | null)[];\n nullable?: boolean;\n anyOf?: JSONSchemaNode[];\n oneOf?: JSONSchemaNode[];\n allOf?: JSONSchemaNode[];\n const?: unknown;\n additionalProperties?: boolean | JSONSchemaNode;\n minLength?: number;\n maxLength?: number;\n minimum?: number;\n maximum?: number;\n minItems?: number;\n maxItems?: number;\n pattern?: string;\n format?: string;\n description?: string;\n default?: unknown;\n $ref?: string;\n}\n\n/**\n * Lightweight JSON Schema validator for Edge Runtime compatibility\n *\n * Validates data against a JSON Schema without using AJV's code generation.\n * This is fully Edge Runtime compatible with zero dependencies.\n *\n * @param data - The data to validate\n * @param schema - JSON Schema object (plain object, not AJV JSONSchemaType)\n * @returns The validated data cast to type T\n * @throws Error if validation fails\n */\nexport function validateJson<T>(data: unknown, schema: JSONSchemaNode): T {\n const errors: string[] = [];\n const MAX_DEPTH = 50; // Prevent DoS via deeply nested objects\n\n function validate(value: unknown, schema: JSONSchemaNode, path: string = '', depth: number = 0): void {\n // Check recursion depth to prevent DoS attacks\n if (depth > MAX_DEPTH) {\n errors.push(`${path || 'root'}: maximum nesting depth (${MAX_DEPTH}) exceeded`);\n return;\n }\n\n // Handle nullable values\n if (schema.nullable && (value === null || value === undefined)) {\n return;\n }\n\n if (value === null || value === undefined) {\n if (schema.nullable !== true) {\n errors.push(`${path || 'root'}: value is null or undefined`);\n }\n return;\n }\n\n // Validate type\n const actualType = Array.isArray(value) ? 'array' : typeof value;\n const expectedType = schema.type;\n\n if (expectedType) {\n // Handle type validation\n if (expectedType === 'integer') {\n if (typeof value !== 'number' || !Number.isInteger(value)) {\n errors.push(`${path || 'root'}: expected integer, got ${actualType}`);\n return;\n }\n } else if (expectedType === 'number') {\n if (typeof value !== 'number') {\n errors.push(`${path || 'root'}: expected number, got ${actualType}`);\n return;\n }\n } else if (expectedType === 'string') {\n if (typeof value !== 'string') {\n errors.push(`${path || 'root'}: expected string, got ${actualType}`);\n return;\n }\n } else if (expectedType === 'boolean') {\n if (typeof value !== 'boolean') {\n errors.push(`${path || 'root'}: expected boolean, got ${actualType}`);\n return;\n }\n } else if (expectedType === 'object') {\n if (typeof value !== 'object' || Array.isArray(value)) {\n errors.push(`${path || 'root'}: expected object, got ${actualType}`);\n return;\n }\n\n // Validate required properties\n if (schema.required && Array.isArray(schema.required)) {\n for (const reqProp of schema.required) {\n if (!(reqProp in value)) {\n errors.push(`${path}.${reqProp}: required property missing`);\n }\n }\n }\n\n // Validate additionalProperties and check for prototype pollution\n const dangerousProps = ['__proto__', 'constructor', 'prototype'];\n\n if (schema.additionalProperties === false && schema.properties) {\n const allowedProps = Object.keys(schema.properties);\n const requiredProps = schema.required || [];\n const allAllowedProps = new Set([...allowedProps, ...requiredProps]);\n\n // Check all keys including potentially dangerous ones\n for (const key of [...Object.keys(value), ...Object.getOwnPropertyNames(value)]) {\n // Explicitly reject dangerous properties\n if (dangerousProps.includes(key)) {\n errors.push(`${path}.${key}: dangerous property not allowed`);\n continue;\n }\n\n if (!allAllowedProps.has(key)) {\n errors.push(`${path}.${key}: additional property not allowed`);\n }\n }\n } else {\n // Even without additionalProperties: false, reject dangerous properties\n for (const key of dangerousProps) {\n if (key in value && Object.prototype.hasOwnProperty.call(value, key)) {\n errors.push(`${path}.${key}: dangerous property not allowed`);\n }\n }\n }\n\n // Validate properties\n if (schema.properties) {\n const valueObj = value as Record<string, unknown>;\n for (const [propName, propSchema] of Object.entries(schema.properties)) {\n if (propName in valueObj) {\n validate(valueObj[propName], propSchema, path ? `${path}.${propName}` : propName, depth + 1);\n }\n }\n }\n } else if (expectedType === 'array') {\n if (!Array.isArray(value)) {\n errors.push(`${path || 'root'}: expected array, got ${actualType}`);\n return;\n }\n\n // Validate array items\n if (schema.items && !Array.isArray(schema.items)) {\n const itemSchema = schema.items;\n value.forEach((item, index) => {\n validate(item, itemSchema, `${path}[${index}]`, depth + 1);\n });\n }\n }\n }\n }\n\n validate(data, schema);\n\n if (errors.length > 0) {\n throw new Error(`Schema validation failed:\\n${errors.join('\\n')}`);\n }\n\n return data as T;\n}\n\n/**\n * Reserved variables that are auto-injected per node type.\n * These variables come from config or computed data and cannot be overridden by users.\n */\nexport const RESERVED_VARIABLES = {\n extract: ['schema', 'documentText', 'schemaTitle', 'schemaDescription', 'structuredFormat'],\n categorize: ['categories', 'documentText'],\n parse: ['format', 'schema', 'describeFigures', 'citationsEnabled']\n} as const;\n\n/**\n * Validates that user-provided promptVariables don't attempt to override reserved variables.\n * Emits console warnings if reserved variables are found in user variables and removes them.\n *\n * @param nodeType - The type of node (extract, categorize, parse)\n * @param userVariables - The user-provided promptVariables object\n * @param autoInjectedVariables - The auto-injected variables object\n * @returns A cleaned variables object with reserved variables protected\n */\nexport function protectReservedVariables(\n nodeType: 'extract' | 'categorize' | 'parse',\n userVariables: Record<string, any> | undefined,\n autoInjectedVariables: Record<string, any>\n): Record<string, any> {\n if (!userVariables || Object.keys(userVariables).length === 0) {\n return autoInjectedVariables;\n }\n\n const reserved = RESERVED_VARIABLES[nodeType];\n const warnings: string[] = [];\n\n // Check for reserved variable override attempts\n for (const key of reserved) {\n if (key in userVariables) {\n warnings.push(key);\n }\n }\n\n // Emit warnings if any reserved variables were attempted\n if (warnings.length > 0) {\n console.warn(\n `[doclo] Attempted to override reserved variables in ${nodeType} node: ${warnings.join(', ')}. ` +\n `These variables are auto-injected from config and cannot be overridden. ` +\n `They will be ignored.`\n );\n }\n\n // Merge: auto-injected first, then user variables (but reserved vars take precedence)\n return {\n ...autoInjectedVariables,\n ...userVariables,\n // Restore reserved variables to ensure they can't be overridden\n ...Object.fromEntries(\n reserved.map(key => [key, autoInjectedVariables[key]])\n )\n };\n}\n","/**\n * URL Validation and SSRF Protection\n *\n * ⚠️ SECURITY CRITICAL: SSRF (Server-Side Request Forgery) Prevention\n *\n * This module blocks URLs that could be used in Server-Side Request Forgery attacks:\n * - Internal IP addresses (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)\n * - Loopback addresses (127.0.0.0/8, ::1)\n * - Cloud metadata services (AWS, GCP, Aliyun, etc.)\n * - Link-local addresses (169.254.0.0/16)\n *\n * Attack example: An attacker tricks your server to make requests to:\n * - http://169.254.169.254 (AWS credentials endpoint)\n * - http://10.0.0.1:8080/admin (internal service)\n * - http://localhost/api/admin (localhost admin endpoint)\n *\n * Prevents Server-Side Request Forgery attacks by validating URLs against blocklist\n */\n\n/**\n * IP ranges that should be blocked (internal networks)\n * RFC 1918 private ranges + loopback + cloud metadata\n */\nconst BLOCKED_IP_RANGES = [\n // Loopback\n { start: '127.0.0.0', end: '127.255.255.255' },\n // Private Class A\n { start: '10.0.0.0', end: '10.255.255.255' },\n // Private Class B\n { start: '172.16.0.0', end: '172.31.255.255' },\n // Private Class C\n { start: '192.168.0.0', end: '192.168.255.255' },\n // Link Local\n { start: '169.254.0.0', end: '169.254.255.255' },\n];\n\nconst BLOCKED_METADATA_HOSTS = [\n '169.254.169.254', // AWS metadata service\n '169.254.169.253', // AWS metadata service (Windows)\n 'metadata.google.internal', // GCP metadata service\n 'metadata', // GCP alias\n '100.100.100.200', // Aliyun metadata service\n 'instance-data', // OpenStack alias\n];\n\n/**\n * IPv6 address patterns that should be blocked\n * Prevents SSRF attacks using IPv6 addresses\n */\nconst BLOCKED_IPV6_PATTERNS = [\n /^::1$/, // Loopback (::1)\n /^::$/, // Any address (::)\n /^::ffff:/i, // IPv4-mapped IPv6 (::ffff:0:0/96) - matches ::ffff:127.0.0.1\n /^::ffff:0:/i, // IPv4-mapped IPv6 alternative\n /^fe80:/i, // Link-local (fe80::/10)\n /^fec0:/i, // Site-local deprecated (fec0::/10)\n /^fc00:/i, // Unique local address (fc00::/7)\n /^fd00:/i, // Unique local address (fd00::/8)\n /^ff00:/i, // Multicast (ff00::/8)\n /^0:0:0:0:0:0:0:1$/i, // Loopback expanded form\n];\n\n/**\n * Convert IPv4 string to number for range comparison\n */\nfunction ipToNumber(ip: string): number {\n const parts = ip.split('.').map(Number);\n if (parts.length !== 4 || parts.some((p) => p < 0 || p > 255)) {\n return -1;\n }\n return (parts[0] << 24) + (parts[1] << 16) + (parts[2] << 8) + parts[3];\n}\n\n/**\n * Check if IP is in blocked range\n */\nfunction isIpInBlockedRange(ip: string): boolean {\n const ipNum = ipToNumber(ip);\n if (ipNum === -1) return false;\n\n return BLOCKED_IP_RANGES.some((range) => {\n const startNum = ipToNumber(range.start);\n const endNum = ipToNumber(range.end);\n return ipNum >= startNum && ipNum <= endNum;\n });\n}\n\n/**\n * Check if IPv6 address is blocked\n * Handles both bracket notation ([::1]) and standard notation (::1)\n */\nfunction isIPv6Blocked(hostname: string): boolean {\n // Remove brackets if present ([::1] -> ::1)\n const addr = hostname.replace(/^\\[|\\]$/g, '');\n\n return BLOCKED_IPV6_PATTERNS.some(pattern => pattern.test(addr));\n}\n\n/**\n * Validate a URL to prevent SSRF (Server-Side Request Forgery) attacks\n *\n * ⚠️ SECURITY CRITICAL: SSRF Prevention\n *\n * This function blocks URLs that could be used to exploit the server:\n * - Internal IP addresses (breaks firewall perimeter security)\n * - Cloud metadata services (leaks credentials, API keys)\n * - Localhost/loopback (access admin services, debug ports)\n *\n * By default, blocks internal network access automatically.\n *\n * @param urlString - The URL to validate\n * @param options - Validation options\n * - blockInternal (default: true) - Block internal IP ranges. ⚠️ Set to false only if you understand SSRF risks\n * - allowedProtocols (default: ['http:', 'https:']) - Restrict to specific protocols\n * @throws Error if URL is invalid, uses blocked protocol, or points to blocked IP/host\n * @returns The validated URL object\n * @security Always validate user-provided URLs. Do not set blockInternal to false without SSRF security review\n *\n * @example\n * ```typescript\n * // Validate user-provided URL\n * try {\n * const url = validateUrl(userInput);\n * const response = await fetch(url.toString());\n * } catch (error) {\n * // URL was malicious or pointed to internal resource\n * }\n * ```\n */\nexport function validateUrl(\n urlString: string,\n options: {\n blockInternal?: boolean;\n allowedProtocols?: string[];\n } = {}\n): URL {\n const {\n blockInternal = true,\n allowedProtocols = ['http:', 'https:'],\n } = options;\n\n let url: URL;\n\n try {\n url = new URL(urlString);\n } catch (error) {\n throw new Error(`Invalid URL: ${urlString}`);\n }\n\n // Check protocol\n if (!allowedProtocols.includes(url.protocol)) {\n throw new Error(\n `Blocked protocol: ${url.protocol}. Allowed: ${allowedProtocols.join(', ')}`\n );\n }\n\n // Check for internal access if enabled\n if (blockInternal) {\n const hostname = url.hostname;\n\n // Check blocked metadata hosts\n if (BLOCKED_METADATA_HOSTS.includes(hostname)) {\n throw new Error(`Blocked metadata service: ${hostname}`);\n }\n\n // Check IPv6 addresses (includes bracket notation)\n if (hostname.includes(':') || hostname.startsWith('[')) {\n if (isIPv6Blocked(hostname)) {\n throw new Error(`Blocked IPv6 address: ${hostname}`);\n }\n }\n\n // Check if IPv4 is in blocked range\n if (isIpInBlockedRange(hostname)) {\n throw new Error(`Blocked internal IP address: ${hostname}`);\n }\n\n // Block localhost keyword\n if (hostname === 'localhost') {\n throw new Error('Blocked localhost access');\n }\n }\n\n return url;\n}\n\n/**\n * Fetch a URL with SSRF protection and timeout\n * @param url - The URL to fetch\n * @param timeoutMs - Request timeout in milliseconds (default 30s)\n * @throws Error if URL is invalid, blocked, or request times out\n */\nexport async function secureFetch(\n url: string,\n timeoutMs: number = 30000\n): Promise<Response> {\n // Validate URL first\n const validatedUrl = validateUrl(url);\n\n // Create abort controller for timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(validatedUrl.toString(), {\n signal: controller.signal,\n });\n return response;\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\n/**\n * Extract hostname from URL string for validation\n * Useful for pre-validation checks before full URL parsing\n */\nexport function getHostnameFromUrl(urlString: string): string {\n try {\n const url = new URL(urlString);\n return url.hostname;\n } catch {\n return '';\n }\n}\n","/**\n * Resource Limits and DoS Protection\n *\n * ⚠️ SECURITY WARNING: Resource limits are critical for protecting against:\n * - Resource exhaustion attacks (large file downloads)\n * - Denial of Service (slow loris, timeout attacks)\n * - Memory exhaustion (deeply nested JSON, large arrays)\n *\n * Prevents resource exhaustion attacks through file size limits and timeouts\n */\n\n/**\n * Default resource limits\n *\n * ⚠️ SECURITY CRITICAL: These are conservative defaults designed to prevent DoS attacks.\n *\n * - MAX_FILE_SIZE (100MB): Prevents downloading very large files that could exhaust memory or disk\n * - REQUEST_TIMEOUT (30s): Prevents slow-loris attacks and hung connections\n * - MAX_JSON_DEPTH (100): Prevents billion laughs attack (deeply nested JSON)\n *\n * Do not increase these limits without understanding the security implications:\n * - Higher file size limit → Greater risk of resource exhaustion\n * - Lower timeout → May reject legitimate slow requests\n * - Lower JSON depth → May reject valid documents\n *\n * @security These limits can be overridden by SDK users, but doing so reduces security.\n */\nexport const DEFAULT_LIMITS = {\n // Maximum file size: 100MB\n MAX_FILE_SIZE: 100 * 1024 * 1024,\n // Request timeout: 30 seconds\n REQUEST_TIMEOUT: 30000,\n // Maximum JSON parse depth\n MAX_JSON_DEPTH: 100,\n};\n\n/**\n * Validate file size before processing\n *\n * ⚠️ SECURITY WARNING: File size validation prevents resource exhaustion.\n * - Without limits: attackers can force downloads of multi-gigabyte files\n * - Memory impact: files are loaded into memory for base64 encoding\n * - Disk impact: temporary storage of downloaded files\n *\n * @param size - The file size in bytes\n * @param maxSize - Maximum allowed size in bytes (default 100MB, can be customized)\n * @throws Error if file exceeds size limit\n * @security Do not disable this check without understanding resource implications\n *\n * @example\n * ```typescript\n * // Standard check with default limit (100MB)\n * validateFileSize(fileSize);\n *\n * // Custom limit for use case that requires larger files\n * validateFileSize(fileSize, 500 * 1024 * 1024); // 500MB\n * ```\n */\nexport function validateFileSize(\n size: number,\n maxSize: number = DEFAULT_LIMITS.MAX_FILE_SIZE\n): void {\n if (size > maxSize) {\n const maxMB = Math.round(maxSize / 1024 / 1024);\n const sizeMB = Math.round(size / 1024 / 1024);\n throw new Error(\n `File size ${sizeMB}MB exceeds maximum allowed size of ${maxMB}MB`\n );\n }\n}\n\n/**\n * Create a fetch controller with timeout\n * @param timeoutMs - Timeout in milliseconds (default 30s)\n * @returns AbortController with timeout configured\n */\nexport function createFetchController(\n timeoutMs: number = DEFAULT_LIMITS.REQUEST_TIMEOUT\n): AbortController {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n // Store timeout ID so caller can clean it up\n (controller as any).__timeoutId = timeoutId;\n\n return controller;\n}\n\n/**\n * Clean up fetch controller timeout\n * @param controller - The AbortController to clean up\n */\nexport function cleanupFetchController(controller: AbortController): void {\n const timeoutId = (controller as any).__timeoutId;\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n}\n\n/**\n * Execute a fetch with automatic timeout and cleanup\n *\n * ⚠️ SECURITY WARNING: Timeout protection prevents slow-loris and other timing attacks.\n * - Without timeout: requests can hang indefinitely, consuming server resources\n * - Slow-loris attack: attacker sends requests slowly to exhaust connection pool\n * - Zombie connections: closed clients but open server connections\n *\n * Default timeout (30s) balances security with legitimate use:\n * - Too short: may reject slow networks or legitimate large downloads\n * - Too long: keeps server resources tied up, enables DoS attacks\n *\n * @param url - The URL to fetch\n * @param options - Fetch options (method, headers, body, etc.)\n * @param timeoutMs - Timeout in milliseconds (default 30s, can be customized)\n * @returns The fetch response\n * @throws Error if request times out\n * @security Do not use very long timeouts without understanding DoS implications\n *\n * @example\n * ```typescript\n * // Default timeout (30 seconds)\n * const response = await fetchWithTimeout('https://example.com/file.pdf');\n *\n * // Custom timeout for slower connections\n * const response = await fetchWithTimeout(url, options, 60000); // 60 seconds\n * ```\n */\nexport async function fetchWithTimeout(\n url: string,\n options: RequestInit = {},\n timeoutMs: number = DEFAULT_LIMITS.REQUEST_TIMEOUT\n): Promise<Response> {\n const controller = createFetchController(timeoutMs);\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n cache: 'no-store', // Prevent Next.js cache revalidation which can cause AbortError (see: github.com/vercel/next.js/issues/54045)\n });\n return response;\n } finally {\n cleanupFetchController(controller);\n }\n}\n\n/**\n * Check buffer size before allocation\n * @param size - The buffer size in bytes\n * @param maxSize - Maximum allowed size (default 100MB)\n * @throws Error if buffer would exceed memory limit\n */\nexport function validateBufferSize(\n size: number,\n maxSize: number = DEFAULT_LIMITS.MAX_FILE_SIZE\n): void {\n validateFileSize(size, maxSize);\n}\n\n/**\n * Memory-safe JSON parse with depth limit\n *\n * ⚠️ SECURITY WARNING: Depth limits prevent billion laughs / XML bomb attacks in JSON format.\n * - Billion laughs attack: deeply nested JSON causes exponential memory expansion\n * - Stack overflow: deeply nested structures can exhaust call stack\n * - Resource exhaustion: parsing deeply nested JSON consumes CPU and memory\n *\n * Attack example (evil.json):\n * ```json\n * {\"a\":{\"b\":{\"c\":{\"d\":{\"e\":...}}}}} // 1000+ levels deep\n * ```\n *\n * Default limit (100 levels) prevents attacks while allowing legitimate documents.\n *\n * @param text - JSON string to parse\n * @param maxDepth - Maximum nesting depth in levels (default 100, can be customized)\n * @returns Parsed object\n * @throws Error if JSON exceeds depth limit, size limit, or is invalid\n * @security Do not disable depth checking without understanding XML bomb implications\n *\n * @example\n * ```typescript\n * // Standard parsing with default depth limit (100 levels)\n * const data = safeJsonParse(jsonString);\n *\n * // Custom depth limit for data that needs deeper nesting\n * const data = safeJsonParse(jsonString, 200); // 200 levels\n * ```\n */\nexport function safeJsonParse(\n text: string,\n maxDepth: number = DEFAULT_LIMITS.MAX_JSON_DEPTH\n): unknown {\n try {\n // Quick size check first\n if (text.length > DEFAULT_LIMITS.MAX_FILE_SIZE) {\n throw new Error('JSON string exceeds maximum size');\n }\n\n const obj = JSON.parse(text);\n\n // Check nesting depth\n function checkDepth(obj: unknown, depth: number = 0): void {\n if (depth > maxDepth) {\n throw new Error(`JSON nesting depth exceeds maximum of ${maxDepth}`);\n }\n\n if (typeof obj === 'object' && obj !== null) {\n for (const value of Object.values(obj)) {\n checkDepth(value, depth + 1);\n }\n }\n }\n\n checkDepth(obj);\n return obj;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Invalid JSON: ${String(error)}`);\n }\n}\n","/**\n * Universal Base64 Adapter\n *\n * Provides base64 encoding/decoding for both Node.js and Edge Runtime.\n * Replaces Node.js Buffer usage with Web APIs for Edge compatibility.\n *\n * @module @doclo/core/runtime/base64\n */\n\n/**\n * Convert ArrayBuffer to base64 string\n *\n * Uses different strategies depending on runtime:\n * - Edge Runtime / Browser: btoa() with binary string conversion\n * - Node.js: Buffer.toString('base64')\n *\n * @param buffer - ArrayBuffer to encode\n * @returns Base64 encoded string\n *\n * @example\n * ```typescript\n * const buffer = new Uint8Array([72, 101, 108, 108, 111]).buffer;\n * const base64 = arrayBufferToBase64(buffer); // \"SGVsbG8=\"\n * ```\n */\nexport function arrayBufferToBase64(buffer: ArrayBuffer): string {\n // Node.js: Use Buffer for best performance\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(buffer).toString('base64');\n }\n\n // Edge Runtime / Browser: Use btoa() with binary string\n const bytes = new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n\n/**\n * Convert base64 string to ArrayBuffer\n *\n * Uses different strategies depending on runtime:\n * - Edge Runtime / Browser: atob() with Uint8Array conversion\n * - Node.js: Buffer.from(base64, 'base64')\n *\n * @param base64 - Base64 encoded string (with or without data URI prefix)\n * @returns Decoded ArrayBuffer\n *\n * @example\n * ```typescript\n * const buffer = base64ToArrayBuffer(\"SGVsbG8=\");\n * const text = new TextDecoder().decode(buffer); // \"Hello\"\n *\n * // Also handles data URIs\n * const buffer2 = base64ToArrayBuffer(\"data:image/png;base64,iVBORw0KG...\");\n * ```\n */\nexport function base64ToArrayBuffer(base64: string): ArrayBuffer {\n // Remove data URI prefix if present\n const cleanBase64 = base64.replace(/^data:[^;]+;base64,/, '');\n\n // Node.js: Use Buffer for best performance\n if (typeof Buffer !== 'undefined') {\n const buffer = Buffer.from(cleanBase64, 'base64');\n // Convert Node.js Buffer to ArrayBuffer\n return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);\n }\n\n // Edge Runtime / Browser: Use atob()\n const binaryString = atob(cleanBase64);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes.buffer;\n}\n\n/**\n * Convert Uint8Array to base64 string\n *\n * Convenience wrapper around arrayBufferToBase64 for Uint8Array inputs.\n *\n * @param bytes - Uint8Array to encode\n * @returns Base64 encoded string\n */\nexport function uint8ArrayToBase64(bytes: Uint8Array): string {\n return arrayBufferToBase64(bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer);\n}\n\n/**\n * Convert base64 string to Uint8Array\n *\n * Convenience wrapper around base64ToArrayBuffer with Uint8Array result.\n *\n * @param base64 - Base64 encoded string\n * @returns Decoded Uint8Array\n */\nexport function base64ToUint8Array(base64: string): Uint8Array {\n return new Uint8Array(base64ToArrayBuffer(base64));\n}\n\n/**\n * Create a data URI from ArrayBuffer\n *\n * @param buffer - Data to encode\n * @param mimeType - MIME type (default: application/octet-stream)\n * @returns Data URI string\n *\n * @example\n * ```typescript\n * const buffer = new TextEncoder().encode(\"Hello, World!\");\n * const dataUri = createDataUri(buffer.buffer, 'text/plain');\n * // \"data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==\"\n * ```\n */\nexport function createDataUri(buffer: ArrayBuffer, mimeType = 'application/octet-stream'): string {\n const base64 = arrayBufferToBase64(buffer);\n return `data:${mimeType};base64,${base64}`;\n}\n\n/**\n * Check if a string is a valid data URI\n *\n * @param input - String to check\n * @returns True if valid data URI format\n */\nexport function isDataUri(input: string): boolean {\n return /^data:[^;,]+;base64,/.test(input);\n}\n\n/**\n * Extract MIME type from data URI\n *\n * @param dataUri - Data URI string\n * @returns MIME type or null if invalid\n *\n * @example\n * ```typescript\n * const mime = extractMimeType(\"data:image/png;base64,iVBOR...\");\n * console.log(mime); // \"image/png\"\n * ```\n */\nexport function extractMimeType(dataUri: string): string | null {\n const match = dataUri.match(/^data:([^;,]+);base64,/);\n return match ? match[1] : null;\n}\n","/**\n * MIME Type Detection Utility\n *\n * Detects MIME types from actual file data (magic bytes) to prevent mismatches\n * between declared MIME types and actual file content.\n *\n * Uses the `file-type` package for comprehensive format detection, with\n * manual fallback for basic types in synchronous contexts.\n */\n\nimport { fileTypeFromBuffer } from 'file-type';\n\n/**\n * Detects MIME type from base64-encoded data using the file-type package.\n * This is the preferred async method that supports 100+ file formats.\n *\n * @param base64Data - Base64 string (with or without data URI prefix)\n * @returns Detected MIME type (e.g., \"image/jpeg\", \"application/pdf\")\n * @throws Error if format is unsupported or data is invalid\n *\n * @example\n * ```typescript\n * const base64 = \"data:image/jpeg;base64,/9j/4AAQSkZJRg...\";\n * const mimeType = await detectMimeTypeFromBase64Async(base64);\n * console.log(mimeType); // \"image/jpeg\"\n * ```\n */\nexport async function detectMimeTypeFromBase64Async(base64Data: string): Promise<string> {\n // Strip data URI prefix if present\n const base64Only = base64Data.includes(',')\n ? base64Data.split(',')[1]\n : base64Data;\n\n // Decode to Uint8Array\n const binaryString = atob(base64Only);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n\n // Use file-type for detection\n const result = await fileTypeFromBuffer(bytes);\n if (result) {\n return result.mime;\n }\n\n throw new Error(\n `Unsupported file format. Magic bytes: ${Array.from(bytes.slice(0, 4))\n .map(b => b.toString(16).padStart(2, '0'))\n .join(' ')}`\n );\n}\n\n/**\n * Detects MIME type from base64-encoded data by examining magic bytes.\n * This is a synchronous fallback for basic formats.\n *\n * Supports:\n * - Images: JPEG, PNG, WebP, GIF, TIFF, BMP\n * - Documents: PDF, RTF\n * - Archives: ZIP (for DOCX, XLSX, PPTX, EPUB detection via extension)\n *\n * @param base64Data - Base64 string (with or without data URI prefix)\n * @returns Detected MIME type (e.g., \"image/jpeg\", \"application/pdf\")\n * @throws Error if format is unsupported or data is invalid\n *\n * @example\n * ```typescript\n * const base64 = \"data:image/jpeg;base64,/9j/4AAQSkZJRg...\";\n * const mimeType = detectMimeTypeFromBase64(base64);\n * console.log(mimeType); // \"image/jpeg\"\n * ```\n */\nexport function detectMimeTypeFromBase64(base64Data: string): string {\n // Strip data URI prefix if present\n const base64Only = base64Data.includes(',')\n ? base64Data.split(',')[1]\n : base64Data;\n\n // Decode first 16 bytes (enough for all magic byte checks)\n const binaryString = atob(base64Only.substring(0, 24));\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n\n return detectMimeTypeFromBytes(bytes);\n}\n\n/**\n * Detects MIME type from raw byte array.\n *\n * @param bytes - Uint8Array containing file data\n * @returns Detected MIME type\n * @throws Error if format is unsupported\n */\nexport function detectMimeTypeFromBytes(bytes: Uint8Array): string {\n if (bytes.length < 4) {\n throw new Error('Insufficient data to detect MIME type (need at least 4 bytes)');\n }\n\n // JPEG: FF D8 FF\n if (bytes[0] === 0xFF && bytes[1] === 0xD8 && bytes[2] === 0xFF) {\n return 'image/jpeg';\n }\n\n // PNG: 89 50 4E 47 0D 0A 1A 0A\n if (bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4E && bytes[3] === 0x47) {\n return 'image/png';\n }\n\n // GIF: 47 49 46 38 (GIF8)\n if (bytes[0] === 0x47 && bytes[1] === 0x49 && bytes[2] === 0x46 && bytes[3] === 0x38) {\n return 'image/gif';\n }\n\n // WebP: RIFF .... WEBP (check positions 0-3 for RIFF, 8-11 for WEBP)\n if (bytes.length >= 12 &&\n bytes[0] === 0x52 && bytes[1] === 0x49 && bytes[2] === 0x46 && bytes[3] === 0x46 &&\n bytes[8] === 0x57 && bytes[9] === 0x45 && bytes[10] === 0x42 && bytes[11] === 0x50) {\n return 'image/webp';\n }\n\n // PDF: %PDF (25 50 44 46)\n if (bytes[0] === 0x25 && bytes[1] === 0x50 && bytes[2] === 0x44 && bytes[3] === 0x46) {\n return 'application/pdf';\n }\n\n // TIFF: Little-endian (49 49 2A 00) or Big-endian (4D 4D 00 2A)\n if ((bytes[0] === 0x49 && bytes[1] === 0x49 && bytes[2] === 0x2A && bytes[3] === 0x00) ||\n (bytes[0] === 0x4D && bytes[1] === 0x4D && bytes[2] === 0x00 && bytes[3] === 0x2A)) {\n return 'image/tiff';\n }\n\n // BMP: 42 4D (BM)\n if (bytes[0] === 0x42 && bytes[1] === 0x4D) {\n return 'image/bmp';\n }\n\n // RTF: 7B 5C 72 74 66 ({\\rtf)\n if (bytes[0] === 0x7B && bytes[1] === 0x5C && bytes[2] === 0x72 && bytes[3] === 0x74 && bytes[4] === 0x66) {\n return 'application/rtf';\n }\n\n // ZIP-based formats: 50 4B 03 04 (PK..)\n // This includes DOCX, XLSX, PPTX, ODT, ODS, ODP, EPUB\n // We return a generic marker - actual type should be determined by file extension\n if (bytes[0] === 0x50 && bytes[1] === 0x4B && bytes[2] === 0x03 && bytes[3] === 0x04) {\n return 'application/zip';\n }\n\n // MS Office Compound Document (DOC, XLS, PPT): D0 CF 11 E0 A1 B1 1A E1\n if (bytes.length >= 8 &&\n bytes[0] === 0xD0 && bytes[1] === 0xCF && bytes[2] === 0x11 && bytes[3] === 0xE0 &&\n bytes[4] === 0xA1 && bytes[5] === 0xB1 && bytes[6] === 0x1A && bytes[7] === 0xE1) {\n // Could be DOC, XLS, or PPT - return generic Office type\n return 'application/x-cfb'; // Compound File Binary\n }\n\n // Unknown format\n throw new Error(\n `Unsupported file format. Magic bytes: ${Array.from(bytes.slice(0, 4))\n .map(b => b.toString(16).padStart(2, '0'))\n .join(' ')}`\n );\n}\n\n/**\n * Validates that declared MIME type matches actual file data.\n *\n * @param base64Data - Base64 string (with or without data URI prefix)\n * @param declaredMimeType - MIME type that was declared/expected\n * @returns Object with validation result and actual MIME type\n *\n * @example\n * ```typescript\n * const result = validateMimeType(base64Data, \"image/jpeg\");\n * if (!result.isValid) {\n * console.warn(`MIME mismatch: declared ${result.declaredMimeType}, actual ${result.actualMimeType}`);\n * }\n * ```\n */\nexport function validateMimeType(\n base64Data: string,\n declaredMimeType: string\n): { isValid: boolean; actualMimeType: string; declaredMimeType: string } {\n const actualMimeType = detectMimeTypeFromBase64(base64Data);\n return {\n isValid: actualMimeType === declaredMimeType,\n actualMimeType,\n declaredMimeType\n };\n}\n\n/**\n * Async version of validateMimeType using file-type for comprehensive detection.\n */\nexport async function validateMimeTypeAsync(\n base64Data: string,\n declaredMimeType: string\n): Promise<{ isValid: boolean; actualMimeType: string; declaredMimeType: string }> {\n const actualMimeType = await detectMimeTypeFromBase64Async(base64Data);\n return {\n isValid: actualMimeType === declaredMimeType,\n actualMimeType,\n declaredMimeType\n };\n}\n\n/**\n * Extracts base64 data from a data URI or returns the data as-is if already base64.\n *\n * @param data - Data URI or base64 string\n * @returns Pure base64 string without prefix\n *\n * @example\n * ```typescript\n * extractBase64(\"data:image/jpeg;base64,/9j/4AAQ...\") // \"/9j/4AAQ...\"\n * extractBase64(\"/9j/4AAQ...\") // \"/9j/4AAQ...\"\n * ```\n */\nexport function extractBase64(data: string): string {\n if (data.startsWith('data:')) {\n const commaIndex = data.indexOf(',');\n if (commaIndex === -1) {\n throw new Error('Invalid data URI: missing comma separator');\n }\n return data.substring(commaIndex + 1);\n }\n return data;\n}\n","/**\n * File utilities for universal runtime (Edge Runtime + Node.js compatible)\n *\n * These utilities work in both Edge Runtime and Node.js environments.\n * File system operations have been removed for Edge Runtime compatibility.\n */\n\nimport { validateUrl } from '../security/url-validator';\nimport { fetchWithTimeout, validateFileSize, DEFAULT_LIMITS } from '../security/resource-limits';\nimport { arrayBufferToBase64, createDataUri } from '../runtime/base64.js';\nimport { detectMimeTypeFromBase64 } from '../mime-detection.js';\n\n/**\n * Supported document MIME types that can be detected.\n * This includes all formats supported by at least one provider:\n * - Datalab: PDF, images, Office, OpenDocument, HTML, EPUB\n * - Reducto: PDF, images (incl. HEIC, BMP, PSD), Office, RTF, TXT, CSV\n * - Unsiloed: PDF, images, Office (DOCX, XLSX, PPTX)\n */\nexport type DocumentMimeType =\n // PDF\n | 'application/pdf'\n // Images - common\n | 'image/jpeg'\n | 'image/png'\n | 'image/gif'\n | 'image/webp'\n // Images - additional\n | 'image/tiff'\n | 'image/bmp'\n | 'image/heic'\n | 'image/heif'\n | 'image/vnd.adobe.photoshop' // PSD\n // Microsoft Office\n | 'application/msword' // DOC\n | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' // DOCX\n | 'application/vnd.ms-excel' // XLS\n | 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' // XLSX\n | 'application/vnd.ms-powerpoint' // PPT\n | 'application/vnd.openxmlformats-officedocument.presentationml.presentation' // PPTX\n // OpenDocument formats (Datalab)\n | 'application/vnd.oasis.opendocument.text' // ODT\n | 'application/vnd.oasis.opendocument.spreadsheet' // ODS\n | 'application/vnd.oasis.opendocument.presentation' // ODP\n // Text formats\n | 'text/plain' // TXT\n | 'text/csv' // CSV\n | 'text/html' // HTML\n | 'application/rtf' // RTF\n // Other\n | 'application/epub+zip' // EPUB\n | 'unknown';\n\n/**\n * Detect input type: HTTP URL or data URI\n *\n * Note: File paths are not supported in Edge Runtime.\n * Use ArrayBuffer or data URIs instead.\n */\nfunction detectInputType(input: string): 'data-uri' | 'url' {\n if (input.startsWith('data:')) return 'data-uri';\n if (input.startsWith('http://') || input.startsWith('https://')) return 'url';\n throw new Error(\n 'Edge Runtime does not support file paths. ' +\n 'Use HTTP URLs, data URIs, or pass ArrayBuffer/base64 data directly.\\n' +\n 'Example: await resolveDocument(\"https://example.com/doc.pdf\") or ' +\n 'resolveDocument(\"data:application/pdf;base64,...\")'\n );\n}\n\n/**\n * Extract MIME type from various sources\n */\nfunction detectMimeType(input: string, contentType?: string): string {\n // Try data URI first\n if (input.startsWith('data:')) {\n const match = input.match(/^data:([^;,]+)/);\n if (match) return match[1];\n }\n\n // Try Content-Type header (from HTTP response)\n if (contentType) {\n const match = contentType.match(/^([^;]+)/);\n if (match) return match[1].trim();\n }\n\n // Try file extension (works for paths and URLs)\n const lower = input.toLowerCase();\n // PDF\n if (lower.endsWith('.pdf') || lower.includes('.pdf?')) return 'application/pdf';\n // Images - common\n if (lower.endsWith('.png') || lower.includes('.png?')) return 'image/png';\n if (lower.endsWith('.webp') || lower.includes('.webp?')) return 'image/webp';\n if (lower.endsWith('.jpg') || lower.includes('.jpg?')) return 'image/jpeg';\n if (lower.endsWith('.jpeg') || lower.includes('.jpeg?')) return 'image/jpeg';\n if (lower.endsWith('.gif') || lower.includes('.gif?')) return 'image/gif';\n // Images - additional\n if (lower.endsWith('.tiff') || lower.includes('.tiff?')) return 'image/tiff';\n if (lower.endsWith('.tif') || lower.includes('.tif?')) return 'image/tiff';\n if (lower.endsWith('.bmp') || lower.includes('.bmp?')) return 'image/bmp';\n if (lower.endsWith('.heic') || lower.includes('.heic?')) return 'image/heic';\n if (lower.endsWith('.heif') || lower.includes('.heif?')) return 'image/heif';\n if (lower.endsWith('.psd') || lower.includes('.psd?')) return 'image/vnd.adobe.photoshop';\n // Microsoft Office\n if (lower.endsWith('.doc') || lower.includes('.doc?')) return 'application/msword';\n if (lower.endsWith('.docx') || lower.includes('.docx?')) return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';\n if (lower.endsWith('.xls') || lower.includes('.xls?')) return 'application/vnd.ms-excel';\n if (lower.endsWith('.xlsx') || lower.includes('.xlsx?')) return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';\n if (lower.endsWith('.ppt') || lower.includes('.ppt?')) return 'application/vnd.ms-powerpoint';\n if (lower.endsWith('.pptx') || lower.includes('.pptx?')) return 'application/vnd.openxmlformats-officedocument.presentationml.presentation';\n // OpenDocument formats\n if (lower.endsWith('.odt') || lower.includes('.odt?')) return 'application/vnd.oasis.opendocument.text';\n if (lower.endsWith('.ods') || lower.includes('.ods?')) return 'application/vnd.oasis.opendocument.spreadsheet';\n if (lower.endsWith('.odp') || lower.includes('.odp?')) return 'application/vnd.oasis.opendocument.presentation';\n // Text formats\n if (lower.endsWith('.txt') || lower.includes('.txt?')) return 'text/plain';\n if (lower.endsWith('.csv') || lower.includes('.csv?')) return 'text/csv';\n if (lower.endsWith('.html') || lower.includes('.html?')) return 'text/html';\n if (lower.endsWith('.htm') || lower.includes('.htm?')) return 'text/html';\n if (lower.endsWith('.rtf') || lower.includes('.rtf?')) return 'application/rtf';\n // Other\n if (lower.endsWith('.epub') || lower.includes('.epub?')) return 'application/epub+zip';\n\n // Default to octet-stream\n return 'application/octet-stream';\n}\n\n/**\n * Security limits configuration for file operations\n * @internal\n */\nexport interface FileLimitsConfig {\n /** Maximum file size in bytes (default: 100MB) - ⚠️ WARNING: Increasing this exposes to resource exhaustion attacks */\n maxFileSize?: number;\n /** Request timeout in milliseconds (default: 30s) - ⚠️ WARNING: Decreasing this may cause legitimate requests to fail */\n requestTimeout?: number;\n}\n\n/**\n * Detect document MIME type from various input formats\n *\n * Detection order (first match wins):\n * 1. Data URL prefix (data:application/pdf;base64,...)\n * 2. URL/path file extension (.pdf, .jpg, etc.)\n * 3. Magic bytes in base64 data (fallback for raw base64)\n *\n * @param input - Document input (data URL, URL, file path, or raw base64)\n * @returns Detected MIME type or 'unknown' if detection fails\n *\n * @example\n * ```typescript\n * detectDocumentType('data:application/pdf;base64,...') // 'application/pdf'\n * detectDocumentType('https://example.com/doc.pdf') // 'application/pdf'\n * detectDocumentType('JVBERi0xLjQK...') // 'application/pdf' (magic bytes)\n * detectDocumentType('/9j/4AAQSkZJRg...') // 'image/jpeg' (magic bytes)\n * ```\n */\n/**\n * All known MIME types that can be detected (for data URI prefix matching)\n */\nconst KNOWN_MIME_TYPES: DocumentMimeType[] = [\n // PDF\n 'application/pdf',\n // Images - common\n 'image/jpeg',\n 'image/png',\n 'image/gif',\n 'image/webp',\n // Images - additional\n 'image/tiff',\n 'image/bmp',\n 'image/heic',\n 'image/heif',\n 'image/vnd.adobe.photoshop',\n // Microsoft Office\n 'application/msword',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n 'application/vnd.ms-excel',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'application/vnd.ms-powerpoint',\n 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n // OpenDocument formats\n 'application/vnd.oasis.opendocument.text',\n 'application/vnd.oasis.opendocument.spreadsheet',\n 'application/vnd.oasis.opendocument.presentation',\n // Text formats\n 'text/plain',\n 'text/csv',\n 'text/html',\n 'application/rtf',\n // Other\n 'application/epub+zip',\n];\n\n/**\n * File extension to MIME type mapping\n */\nconst EXTENSION_TO_MIME: Record<string, DocumentMimeType> = {\n // PDF\n '.pdf': 'application/pdf',\n // Images - common\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.png': 'image/png',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n // Images - additional\n '.tiff': 'image/tiff',\n '.tif': 'image/tiff',\n '.bmp': 'image/bmp',\n '.heic': 'image/heic',\n '.heif': 'image/heif',\n '.psd': 'image/vnd.adobe.photoshop',\n // Microsoft Office\n '.doc': 'application/msword',\n '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n '.xls': 'application/vnd.ms-excel',\n '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n '.ppt': 'application/vnd.ms-powerpoint',\n '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n // OpenDocument formats\n '.odt': 'application/vnd.oasis.opendocument.text',\n '.ods': 'application/vnd.oasis.opendocument.spreadsheet',\n '.odp': 'application/vnd.oasis.opendocument.presentation',\n // Text formats\n '.txt': 'text/plain',\n '.csv': 'text/csv',\n '.html': 'text/html',\n '.htm': 'text/html',\n '.rtf': 'application/rtf',\n // Other\n '.epub': 'application/epub+zip',\n};\n\n/**\n * Get file extension from path or URL (handles query strings)\n */\nfunction getExtensionFromPath(path: string): string | null {\n // Remove query string if present\n const pathWithoutQuery = path.split('?')[0];\n const lastDot = pathWithoutQuery.lastIndexOf('.');\n if (lastDot === -1) return null;\n return pathWithoutQuery.slice(lastDot).toLowerCase();\n}\n\nexport function detectDocumentType(input: string | undefined): DocumentMimeType {\n if (!input) return 'unknown';\n\n // 1. Check data URL MIME type prefix\n if (input.startsWith('data:')) {\n const match = input.match(/^data:([^;,]+)/);\n if (match) {\n const mimeType = match[1] as DocumentMimeType;\n if (KNOWN_MIME_TYPES.includes(mimeType)) {\n return mimeType;\n }\n }\n // Unknown data URL type, try magic bytes below\n }\n\n // 2. Check URL/path file extension (only if not a data URL)\n if (!input.startsWith('data:')) {\n let ext: string | null = null;\n\n try {\n const url = new URL(input);\n ext = getExtensionFromPath(url.pathname);\n } catch {\n // Not a valid URL, try as file path\n ext = getExtensionFromPath(input);\n }\n\n if (ext && ext in EXTENSION_TO_MIME) {\n return EXTENSION_TO_MIME[ext];\n }\n }\n\n // 3. Magic byte detection (fallback for raw base64 or unknown data URLs)\n try {\n const mimeType = detectMimeTypeFromBase64(input);\n // Check if it's a known type\n if (KNOWN_MIME_TYPES.includes(mimeType as DocumentMimeType)) {\n return mimeType as DocumentMimeType;\n }\n } catch {\n // Magic byte detection failed\n }\n\n return 'unknown';\n}\n\n/**\n * Check if input represents a PDF document\n *\n * Handles various input formats:\n * - Data URLs with MIME type\n * - File paths with .pdf extension\n * - HTTP/HTTPS URLs (with or without query parameters)\n * - Raw base64 strings (detected via magic bytes)\n *\n * @param input - Document input (data URL, file path, URL, or raw base64)\n * @returns true if input appears to be a PDF\n *\n * @example\n * ```typescript\n * isPDFDocument('data:application/pdf;base64,...') // true\n * isPDFDocument('./document.pdf') // true\n * isPDFDocument('https://example.com/doc.pdf?token=123') // true\n * isPDFDocument('JVBERi0xLjQK...') // true (raw base64 PDF)\n * isPDFDocument('data:image/jpeg;base64,...') // false\n * ```\n */\nexport function isPDFDocument(input: string | undefined): boolean {\n return detectDocumentType(input) === 'application/pdf';\n}\n\n/**\n * Resolve document from any source (URL or data URI) to base64 data URL\n *\n * Supports two input types:\n * - HTTP/HTTPS URLs: 'https://example.com/document.pdf'\n * - Data URIs: 'data:application/pdf;base64,JVBERi0x...'\n *\n * Note: File paths are NOT supported in Edge Runtime.\n * Use HTTP URLs, data URIs, or pass ArrayBuffer/base64 directly.\n *\n * @param input - Document source (URL or data URI)\n * @param limits - Optional security limits for file size and request timeout (uses secure defaults if not specified)\n * @returns Promise resolving to base64 data URL\n *\n * @example\n * ```typescript\n * // Remote URL\n * const dataUrl = await resolveDocument('https://example.com/doc.pdf');\n *\n * // Remote URL with custom timeout\n * const dataUrl = await resolveDocument('https://example.com/doc.pdf', { requestTimeout: 60000 });\n *\n * // Data URI (pass-through)\n * const dataUrl = await resolveDocument('data:application/pdf;base64,JVBERi0x...');\n *\n * // For ArrayBuffer, use bufferToDataUri() instead\n * const dataUrl = bufferToDataUri(arrayBuffer, 'application/pdf');\n * ```\n */\nexport async function resolveDocument(input: string, limits?: FileLimitsConfig): Promise<string> {\n const inputType = detectInputType(input);\n\n switch (inputType) {\n case 'data-uri':\n // Already in data URI format - validate and return\n if (!input.match(/^data:[^;,]+;base64,/)) {\n throw new Error('Invalid data URI format. Expected: data:<mimetype>;base64,<data>');\n }\n return input;\n\n case 'url':\n // Fetch from HTTP/HTTPS with security validations\n try {\n // Validate URL for SSRF attacks\n validateUrl(input);\n\n // Use custom timeout or default\n const timeout = limits?.requestTimeout ?? DEFAULT_LIMITS.REQUEST_TIMEOUT;\n\n // Fetch with timeout protection\n const response = await fetchWithTimeout(input, {}, timeout);\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n // Check content-length header if available\n const contentLength = response.headers.get('content-length');\n if (contentLength) {\n const maxSize = limits?.maxFileSize ?? DEFAULT_LIMITS.MAX_FILE_SIZE;\n validateFileSize(parseInt(contentLength, 10), maxSize);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n\n // Validate actual downloaded size\n const maxSize = limits?.maxFileSize ?? DEFAULT_LIMITS.MAX_FILE_SIZE;\n validateFileSize(arrayBuffer.byteLength, maxSize);\n\n const base64 = arrayBufferToBase64(arrayBuffer);\n const mimeType = detectMimeType(input, response.headers.get('content-type') || undefined);\n return `data:${mimeType};base64,${base64}`;\n } catch (error) {\n throw new Error(`Failed to fetch URL ${input}: ${(error as Error).message}`);\n }\n }\n}\n\n/**\n * Convert ArrayBuffer or Uint8Array to base64 data URI\n *\n * Edge Runtime compatible - no file system access required.\n *\n * @param buffer - File buffer (ArrayBuffer or Uint8Array)\n * @param mimeType - MIME type (e.g., 'application/pdf', 'image/jpeg')\n * @returns Base64 data URI string\n *\n * @example\n * ```typescript\n * // From ArrayBuffer\n * const buffer = await response.arrayBuffer();\n * const dataUri = bufferToDataUri(buffer, 'application/pdf');\n *\n * // From Uint8Array\n * const bytes = new Uint8Array([72, 101, 108, 108, 111]);\n * const dataUri = bufferToDataUri(bytes, 'text/plain');\n * ```\n */\nexport function bufferToDataUri(buffer: ArrayBuffer | Uint8Array, mimeType: string): string {\n if (buffer instanceof Uint8Array) {\n // Convert Uint8Array to ArrayBuffer\n const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength) as ArrayBuffer;\n return createDataUri(arrayBuffer, mimeType);\n }\n return createDataUri(buffer, mimeType);\n}\n\n/**\n * @deprecated Use bufferToDataUri() instead. This function will be removed in v0.2.0.\n */\nexport function bufferToBase64(buffer: ArrayBuffer | Uint8Array, mimeType: string): string {\n return bufferToDataUri(buffer, mimeType);\n}\n\n/**\n * Accepted MIME types for flow input validation.\n * Excludes 'unknown' - only known provider-supported formats.\n */\nexport type AcceptedMimeType = Exclude<DocumentMimeType, 'unknown'>;\n\n/**\n * Error thrown when flow input doesn't match accepted formats\n */\nexport class FlowInputValidationError extends Error {\n /**\n * @param message - Human-readable error message\n * @param detectedType - The actual MIME type detected from the input\n * @param acceptedTypes - List of MIME types that would have been accepted\n */\n constructor(\n message: string,\n public readonly detectedType: string,\n public readonly acceptedTypes: string[]\n ) {\n super(message);\n this.name = 'FlowInputValidationError';\n // Maintain proper prototype chain for instanceof checks\n Object.setPrototypeOf(this, FlowInputValidationError.prototype);\n }\n}\n\n/**\n * Validate flow input against accepted MIME type formats\n *\n * @param input - Flow input string (base64, data URL, or URL)\n * @param acceptedFormats - List of accepted MIME types\n * @returns The detected MIME type if valid\n * @throws FlowInputValidationError if format doesn't match accepted types\n *\n * @example\n * ```typescript\n * // Validate PDF only\n * const mimeType = validateFlowInputFormat(pdfBase64, ['application/pdf']);\n *\n * // Validate images only\n * const mimeType = validateFlowInputFormat(jpgBase64, ['image/jpeg', 'image/png']);\n *\n * // Will throw FlowInputValidationError if input is a PDF but only images accepted\n * ```\n */\nexport function validateFlowInputFormat(\n input: string | undefined,\n acceptedFormats: AcceptedMimeType[]\n): AcceptedMimeType {\n if (!input) {\n throw new FlowInputValidationError(\n 'Flow input is empty or undefined',\n 'undefined',\n acceptedFormats\n );\n }\n\n const detected = detectDocumentType(input);\n\n if (detected === 'unknown') {\n const acceptedList = acceptedFormats.length > 0\n ? `Expected one of: ${acceptedFormats.join(', ')}`\n : 'Unable to determine document format';\n throw new FlowInputValidationError(\n `Unable to detect document format. ${acceptedList}. Ensure the input is a valid document (PDF, JPEG, PNG, GIF, or WebP).`,\n 'unknown',\n acceptedFormats\n );\n }\n\n if (acceptedFormats.length > 0 && !acceptedFormats.includes(detected as AcceptedMimeType)) {\n throw new FlowInputValidationError(\n `Document format '${detected}' is not accepted. Expected one of: ${acceptedFormats.join(', ')}`,\n detected,\n acceptedFormats\n );\n }\n\n return detected as AcceptedMimeType;\n}\n","/**\n * PDF Utilities\n *\n * Edge Runtime compatible PDF manipulation utilities using pdf-lib.\n * These functions work in Node.js, Vercel Edge Functions, Cloudflare Workers, and browsers.\n */\n\nimport { PDFDocument } from 'pdf-lib';\nimport { base64ToArrayBuffer, uint8ArrayToBase64 } from './runtime/base64.js';\nimport type { DocumentIR } from './internal/validation-utils.js';\n\n/**\n * Get the total number of pages in a PDF document\n *\n * @param dataUrl - PDF data URI in format: data:application/pdf;base64,{base64data}\n * @returns Total page count\n * @throws {Error} If the input is not a valid PDF data URL\n *\n * @example\n * ```typescript\n * const pageCount = await getPDFPageCount('data:application/pdf;base64,JVBERi0...');\n * console.log(`PDF has ${pageCount} pages`);\n * ```\n */\nexport async function getPDFPageCount(dataUrl: string): Promise<number> {\n const base64Match = dataUrl.match(/^data:application\\/pdf;base64,(.+)$/);\n if (!base64Match) {\n throw new Error('Invalid PDF data URL format. Expected: data:application/pdf;base64,{base64data}');\n }\n\n const base64Data = base64Match[1];\n const pdfBytes = base64ToArrayBuffer(base64Data);\n const pdfDoc = await PDFDocument.load(pdfBytes);\n return pdfDoc.getPageCount();\n}\n\n/**\n * Split a PDF into multiple smaller PDFs based on page ranges\n *\n * @param dataUrl - PDF data URI in format: data:application/pdf;base64,{base64data}\n * @param pageRanges - Array of [startPage, endPage] tuples (1-indexed, inclusive)\n * @returns Array of PDF data URLs, one for each page range\n * @throws {Error} If the input is not a valid PDF data URL or page ranges are invalid\n *\n * @example\n * ```typescript\n * // Split a 10-page PDF into three chunks\n * const chunks = await splitPDFIntoChunks(pdfDataUrl, [\n * [1, 3], // Pages 1-3\n * [4, 7], // Pages 4-7\n * [8, 10] // Pages 8-10\n * ]);\n * console.log(`Created ${chunks.length} PDF chunks`);\n * ```\n */\nexport async function splitPDFIntoChunks(\n dataUrl: string,\n pageRanges: Array<[number, number]>\n): Promise<string[]> {\n // Extract base64 data from data URL\n const base64Match = dataUrl.match(/^data:application\\/pdf;base64,(.+)$/);\n if (!base64Match) {\n throw new Error('Invalid PDF data URL format. Expected: data:application/pdf;base64,{base64data}');\n }\n\n const base64Data = base64Match[1];\n const pdfBytes = base64ToArrayBuffer(base64Data);\n\n // Load the PDF\n const pdfDoc = await PDFDocument.load(pdfBytes);\n const totalPages = pdfDoc.getPageCount();\n\n const chunks: string[] = [];\n\n for (const [startPage, endPage] of pageRanges) {\n // Validate page range\n if (startPage < 1 || endPage > totalPages || startPage > endPage) {\n throw new Error(\n `Invalid page range [${startPage}, ${endPage}] for PDF with ${totalPages} pages. ` +\n `Page numbers must be 1-indexed and within bounds.`\n );\n }\n\n // Create new PDF with only these pages\n const chunkDoc = await PDFDocument.create();\n const pagesToCopy = Array.from(\n { length: endPage - startPage + 1 },\n (_, i) => startPage - 1 + i // Convert to 0-indexed\n );\n\n const copiedPages = await chunkDoc.copyPages(pdfDoc, pagesToCopy);\n copiedPages.forEach(page => chunkDoc.addPage(page));\n\n // Serialize to base64 using Edge Runtime compatible adapter\n const chunkBytes = await chunkDoc.save();\n const chunkBase64 = uint8ArrayToBase64(chunkBytes);\n chunks.push(`data:application/pdf;base64,${chunkBase64}`);\n }\n\n return chunks;\n}\n\n/**\n * Get the page count from a DocumentIR, with fallback logic\n *\n * This helper function checks multiple sources for page count:\n * 1. `extras.pageCount` (explicit page count from provider or PDF analysis)\n * 2. `pages.length` (fallback - number of pages in the IR)\n *\n * Note: For Unsiloed provider, `pages.length` represents semantic chunks,\n * not traditional pages. Use `extras.totalSemanticChunks` to distinguish.\n *\n * @param ir - DocumentIR to get page count from\n * @returns Page count (or chunk count for Unsiloed)\n *\n * @example\n * ```typescript\n * const ir = await parseNode.run(pdfUrl, { provider: ocrProvider });\n * const pageCount = getDocumentPageCount(ir);\n * console.log(`Document has ${pageCount} pages`);\n * ```\n */\nexport function getDocumentPageCount(ir: DocumentIR): number {\n // Prefer explicit pageCount from extras\n if (ir.extras?.pageCount !== undefined) {\n return ir.extras.pageCount;\n }\n\n // Fallback to pages array length\n return ir.pages.length;\n}\n\n/**\n * Get total page count across multiple DocumentIR objects (chunked results)\n *\n * For chunked parsing results, this sums up the page counts across all chunks.\n * It respects `extras.pageCount` if available, otherwise uses `pages.length`.\n *\n * @param irArray - Array of DocumentIR objects from chunked parsing\n * @returns Total page count across all chunks\n *\n * @example\n * ```typescript\n * const chunks = await parseNode.run(largePdfUrl, {\n * provider: ocrProvider,\n * chunked: { maxPagesPerChunk: 10 }\n * });\n * const totalPages = getTotalPageCount(chunks);\n * console.log(`Total pages across ${chunks.length} chunks: ${totalPages}`);\n * ```\n */\nexport function getTotalPageCount(irArray: DocumentIR[]): number {\n return irArray.reduce((sum, ir) => sum + getDocumentPageCount(ir), 0);\n}\n\n/**\n * Get comprehensive page-related metadata from a DocumentIR\n *\n * Returns detailed information about page counts, chunk information,\n * and whether the result is chunked or a complete document.\n *\n * @param ir - DocumentIR to analyze\n * @returns Metadata object with page count details\n *\n * @example\n * ```typescript\n * const metadata = getPageCountMetadata(ir);\n * console.log(`Document has ${metadata.pageCount} pages`);\n * if (metadata.isChunked) {\n * console.log(`This is chunk ${metadata.chunkIndex + 1} of ${metadata.totalChunks}`);\n * console.log(`Contains pages ${metadata.pageRange[0]} to ${metadata.pageRange[1]}`);\n * }\n * ```\n */\nexport function getPageCountMetadata(ir: DocumentIR): {\n /** Total page count (or chunk count for Unsiloed) */\n pageCount: number;\n /** Number of pages in the IR (may differ from pageCount for chunked docs) */\n pagesInIR: number;\n /** Whether this is a chunked result */\n isChunked: boolean;\n /** For chunked results: which chunk this is (0-indexed) */\n chunkIndex?: number;\n /** For chunked results: total number of chunks */\n totalChunks?: number;\n /** For chunked results: page range [start, end] (1-indexed, inclusive) */\n pageRange?: [number, number];\n /** For Unsiloed: total semantic chunks */\n totalSemanticChunks?: number;\n /** Whether this is from Unsiloed (semantic chunking, not traditional pages) */\n isSemanticChunking: boolean;\n} {\n const pagesInIR = ir.pages.length;\n const pageCount = ir.extras?.pageCount ?? pagesInIR;\n const isSemanticChunking = ir.extras?.totalSemanticChunks !== undefined;\n const isChunked = ir.extras?.chunkIndex !== undefined && ir.extras?.totalChunks !== undefined;\n\n return {\n pageCount,\n pagesInIR,\n isChunked,\n chunkIndex: ir.extras?.chunkIndex as number | undefined,\n totalChunks: ir.extras?.totalChunks as number | undefined,\n pageRange: ir.extras?.pageRange as [number, number] | undefined,\n totalSemanticChunks: ir.extras?.totalSemanticChunks as number | undefined,\n isSemanticChunking\n };\n}\n","/**\n * Provider Configuration\n *\n * Serializable provider configurations for doclo-sdk.\n * These configs can be stored in databases and reconstructed at runtime.\n */\n\n/**\n * Type for dynamically imported module with potential default export.\n * Used for ESM/CJS interop when dynamically loading provider packages.\n */\ninterface DynamicModuleExports {\n [exportName: string]: unknown;\n default?: Record<string, unknown>;\n}\n\n/**\n * Base provider configuration\n */\nexport type BaseProviderConfig = {\n id: string; // Unique identifier for this provider instance\n name?: string; // Human-readable name\n};\n\n/**\n * VLM (Vision Language Model) provider configuration\n */\nexport type VLMProviderConfig = BaseProviderConfig & {\n type: 'vlm';\n provider: 'openai' | 'anthropic' | 'google' | 'xai' | 'generic-or';\n model: string;\n via?: 'openrouter' | 'native';\n baseUrl?: string;\n // API key stored separately (not serialized)\n};\n\n/**\n * OCR provider configuration\n */\nexport type OCRProviderConfig = BaseProviderConfig & (\n | {\n type: 'ocr';\n provider: 'surya';\n endpoint?: string;\n // API key stored separately\n }\n | {\n type: 'ocr';\n provider: 'marker';\n force_ocr?: boolean;\n use_llm?: boolean;\n // API key stored separately\n }\n);\n\n/**\n * All provider configurations\n */\nexport type ProviderConfig = VLMProviderConfig | OCRProviderConfig;\n\n/**\n * Provider secrets (API keys, credentials)\n * Stored separately from provider configs for security\n */\nexport type ProviderSecrets = Record<string, {\n apiKey?: string;\n /** Additional secret values (e.g., endpoint URLs, tokens) */\n [key: string]: string | undefined;\n}>;\n\n/**\n * Base provider interface - common methods shared by all providers\n */\nexport interface ProviderInstance {\n /** Optional provider name for identification */\n name?: string;\n /** Capabilities of this provider instance */\n capabilities?: Record<string, unknown>;\n}\n\n/**\n * Provider registry - maps provider IDs to instantiated providers\n * Uses a generic constraint to allow type narrowing when the provider type is known\n */\nexport type ProviderRegistry<T extends ProviderInstance = ProviderInstance> = Record<string, T>;\n\n/**\n * Helper to create VLM provider config\n */\nexport function defineVLMProvider(config: Omit<VLMProviderConfig, 'type'>): VLMProviderConfig {\n return {\n type: 'vlm',\n ...config\n };\n}\n\n/**\n * Helper to create Surya OCR provider config\n */\nexport function defineSuryaProvider(config: Omit<Extract<OCRProviderConfig, { provider: 'surya' }>, 'type'>): OCRProviderConfig {\n return {\n type: 'ocr',\n ...config\n };\n}\n\n/**\n * Helper to create Marker OCR provider config\n */\nexport function defineMarkerProvider(config: Omit<Extract<OCRProviderConfig, { provider: 'marker' }>, 'type'>): OCRProviderConfig {\n return {\n type: 'ocr',\n ...config\n };\n}\n\n/**\n * Build a provider instance from config and secrets\n *\n * @param config - Provider configuration\n * @param secrets - Provider secrets (API keys)\n * @returns Provider instance\n *\n * @example\n * ```typescript\n * const config: VLMProviderConfig = {\n * type: 'vlm',\n * id: 'gemini-flash',\n * provider: 'google',\n * model: 'google/gemini-2.5-flash-preview-09-2025',\n * via: 'openrouter'\n * };\n *\n * const secrets: ProviderSecrets = {\n * 'gemini-flash': {\n * apiKey: process.env.OPENROUTER_API_KEY\n * }\n * };\n *\n * const provider = await buildProviderFromConfig(config, secrets);\n * ```\n */\nexport async function buildProviderFromConfig(\n config: ProviderConfig,\n secrets: ProviderSecrets\n): Promise<ProviderInstance> {\n const secret = secrets[config.id];\n\n if (!secret || !secret.apiKey) {\n throw new Error(`API key not found for provider \"${config.id}\"`);\n }\n\n if (config.type === 'vlm') {\n // Dynamic import to avoid build-time dependencies\n try {\n // @ts-ignore - Dynamic import, package may not be installed\n const module: DynamicModuleExports = await import(/* webpackIgnore: true */ '@doclo/providers-llm');\n const createVLMProvider = (module.createVLMProvider || module.default?.createVLMProvider) as\n | ((opts: {\n provider: string;\n model: string;\n apiKey: string;\n via?: 'openrouter';\n baseUrl?: string;\n }) => ProviderInstance)\n | undefined;\n\n if (!createVLMProvider) {\n throw new Error('@doclo/providers-llm does not export createVLMProvider');\n }\n\n return createVLMProvider({\n provider: config.provider,\n model: config.model,\n apiKey: secret.apiKey,\n via: config.via === 'openrouter' ? 'openrouter' : undefined,\n baseUrl: config.baseUrl\n });\n } catch (error) {\n throw new Error(\n `Failed to create VLM provider: ${(error as Error).message}. ` +\n `Make sure @doclo/providers-llm is installed.`\n );\n }\n } else if (config.type === 'ocr') {\n // Dynamic import to avoid build-time dependencies\n try {\n // @ts-ignore - Dynamic import, package may not be installed\n const module: DynamicModuleExports = await import(/* webpackIgnore: true */ '@doclo/providers-datalab');\n\n if (config.provider === 'surya') {\n const suryaProvider = (module.suryaProvider || module.default?.suryaProvider) as\n | ((opts: { endpoint?: string; apiKey: string }) => ProviderInstance)\n | undefined;\n if (!suryaProvider) {\n throw new Error('@doclo/providers-datalab does not export suryaProvider');\n }\n\n return suryaProvider({\n endpoint: config.endpoint,\n apiKey: secret.apiKey\n });\n } else if (config.provider === 'marker') {\n const markerProvider = (module.markerProvider || module.default?.markerProvider) as\n | ((opts: { apiKey: string; force_ocr?: boolean; use_llm?: boolean }) => ProviderInstance)\n | undefined;\n if (!markerProvider) {\n throw new Error('@doclo/providers-datalab does not export markerProvider');\n }\n\n return markerProvider({\n apiKey: secret.apiKey,\n force_ocr: config.force_ocr,\n use_llm: config.use_llm\n });\n } else {\n // This branch is unreachable due to discriminated union, but TypeScript doesn't know that\n const exhaustiveCheck: never = config;\n throw new Error(`Unknown OCR provider: ${(exhaustiveCheck as OCRProviderConfig).provider}`);\n }\n } catch (error) {\n throw new Error(\n `Failed to create OCR provider: ${(error as Error).message}. ` +\n `Make sure @doclo/providers-datalab is installed.`\n );\n }\n } else {\n // This branch is unreachable due to discriminated union, but TypeScript doesn't know that\n const exhaustiveCheck: never = config;\n throw new Error(`Unknown provider type: ${(exhaustiveCheck as ProviderConfig).type}`);\n }\n}\n\n/**\n * Build multiple providers from configs\n *\n * @param configs - Array of provider configurations\n * @param secrets - Provider secrets\n * @returns Provider registry (map of IDs to instances)\n *\n * @example\n * ```typescript\n * const configs: ProviderConfig[] = [\n * { type: 'vlm', id: 'gemini', provider: 'google', model: '...', via: 'openrouter' },\n * { type: 'ocr', id: 'surya', provider: 'surya' }\n * ];\n *\n * const secrets: ProviderSecrets = {\n * 'gemini': { apiKey: process.env.OPENROUTER_API_KEY },\n * 'surya': { apiKey: process.env.SURYA_API_KEY }\n * };\n *\n * const providers = await buildProvidersFromConfigs(configs, secrets);\n * // providers = { gemini: VLMProvider, surya: OCRProvider }\n * ```\n */\nexport async function buildProvidersFromConfigs(\n configs: ProviderConfig[],\n secrets: ProviderSecrets\n): Promise<ProviderRegistry> {\n const registry: ProviderRegistry = {};\n\n for (const config of configs) {\n try {\n registry[config.id] = await buildProviderFromConfig(config, secrets);\n } catch (error) {\n throw new Error(\n `Failed to build provider \"${config.id}\": ${(error as Error).message}`\n );\n }\n }\n\n return registry;\n}\n","/**\n * Provider Identity Types\n *\n * Implements the 3-layer hierarchy for provider identification:\n * 1. Provider (Company/Vendor) - e.g., datalab, openai, anthropic\n * 2. Model - e.g., surya, marker-ocr, claude-sonnet-4.5\n * 3. Method - e.g., native, openrouter, self-hosted\n */\n\n/**\n * Provider vendors (companies)\n * These represent the company or organization providing the service\n */\nexport type ProviderVendor =\n | 'datalab' // Datalab (surya, marker-ocr, marker-vlm)\n | 'reducto' // Reducto (unified document processing)\n | 'unsiloed' // Unsiloed (unified document processing)\n | 'extend' // Extend.ai (document parsing, extraction, classification, splitting)\n | 'openai' // OpenAI (gpt-4.1, o3, o4-mini)\n | 'anthropic' // Anthropic (claude-*)\n | 'google' // Google (gemini-*)\n | 'xai' // xAI (grok-*)\n | 'mistral'; // Mistral AI (ocr-3, pixtral, large, medium, small)\n\n/**\n * Access methods for providers\n * - native: Direct API call to provider's official endpoint\n * - openrouter: Via OpenRouter aggregator (LLM only)\n * - self-hosted: Self-hosted instance (e.g., pip install surya-ocr)\n */\nexport type AccessMethod = 'native' | 'openrouter' | 'self-hosted';\n\n/**\n * Complete provider identity combining all three layers\n */\nexport interface ProviderIdentity {\n /** The company/vendor (e.g., 'datalab') */\n readonly provider: ProviderVendor;\n\n /** The specific model/version (e.g., 'surya', 'marker-vlm') */\n readonly model: string;\n\n /** How the provider is accessed (e.g., 'native', 'self-hosted') */\n readonly method: AccessMethod;\n}\n\n/**\n * Convert provider identity to canonical string format\n * Format: \"provider:model\" (e.g., \"datalab:surya\")\n *\n * @example\n * ```typescript\n * toProviderString({ provider: 'datalab', model: 'surya', method: 'native' })\n * // => \"datalab:surya\"\n * ```\n */\nexport function toProviderString(identity: ProviderIdentity): string {\n return `${identity.provider}:${identity.model}`;\n}\n\n/**\n * Parse canonical provider string back to partial identity\n * Note: method cannot be determined from string alone\n *\n * @example\n * ```typescript\n * parseProviderString(\"datalab:surya\")\n * // => { provider: 'datalab', model: 'surya' }\n * ```\n */\nexport function parseProviderString(str: string): { provider: string; model: string } {\n const colonIndex = str.indexOf(':');\n if (colonIndex === -1) {\n // Legacy format: just model name (e.g., \"surya\")\n return { provider: str, model: str };\n }\n return {\n provider: str.slice(0, colonIndex),\n model: str.slice(colonIndex + 1)\n };\n}\n\n/**\n * Check if an endpoint appears to be self-hosted\n * Used to determine the access method for OCR providers\n */\nexport function isLocalEndpoint(endpoint?: string): boolean {\n if (!endpoint) return false;\n return (\n endpoint.includes('localhost') ||\n endpoint.includes('127.0.0.1') ||\n endpoint.includes('0.0.0.0') ||\n endpoint.startsWith('http://192.168.') ||\n endpoint.startsWith('http://10.')\n );\n}\n\n/**\n * Create a provider identity with inferred method\n *\n * @param provider - The vendor/company\n * @param model - The model name\n * @param opts - Options including endpoint for method inference\n */\nexport function createIdentity(\n provider: ProviderVendor,\n model: string,\n opts?: { endpoint?: string; via?: 'openrouter' | 'native' }\n): ProviderIdentity {\n let method: AccessMethod = 'native';\n\n if (opts?.via === 'openrouter') {\n method = 'openrouter';\n } else if (isLocalEndpoint(opts?.endpoint)) {\n method = 'self-hosted';\n }\n\n return { provider, model, method };\n}\n","/**\n * Unified Provider Query Interface\n *\n * Provides a unified way to query and filter provider metadata across\n * all provider packages (@doclo/providers-llm, @doclo/providers-datalab).\n *\n * @example\n * ```typescript\n * import { queryProviders, registerProviderMetadata } from '@doclo/core';\n *\n * // Register metadata from provider packages (done automatically if packages are imported)\n * import { PROVIDER_METADATA as LLM_METADATA } from '@doclo/providers-llm';\n * import { PROVIDER_METADATA as DATALAB_METADATA } from '@doclo/providers-datalab';\n *\n * registerProviderMetadata('llm', LLM_METADATA);\n * registerProviderMetadata('datalab', DATALAB_METADATA);\n *\n * // Query providers\n * const pdfProviders = queryProviders({ supports: { pdfs: true } });\n * const cheapProviders = queryProviders({ maxCostPerPage: 0.01 });\n * const largeFileProviders = queryProviders({ minFileSize: 100 }); // 100 MB+\n * ```\n */\n\n// ============================================================================\n// Input Requirements Types\n// ============================================================================\n\n/**\n * Input type requirements for providers/models.\n * More normalized than a boolean - allows for future extensibility.\n *\n * - 'raw-document': Needs FlowInput with base64/url (OCR/VLM providers like marker-vlm)\n * - 'parsed-text': Needs DocumentIR text output from parse step (text-only processors)\n * - 'any': Can work with either (most vision LLMs like GPT-4o, Claude with vision)\n */\nexport type ProviderInputType = 'raw-document' | 'parsed-text' | 'any';\n\n/**\n * Input requirements specification for a provider or model.\n * Determines what form of document input is expected.\n */\nexport type InputRequirements = {\n /**\n * What type of input this provider accepts.\n * - 'raw-document': Needs PDF/image bytes directly (marker-vlm, reducto-extract)\n * - 'parsed-text': Needs DocumentIR text (text-only processors)\n * - 'any': Can work with either (vision LLMs like GPT-4o, Claude)\n */\n inputType: ProviderInputType;\n\n /**\n * Accepted input methods when inputType is 'raw-document'.\n * Inherited from inputFormats.inputMethods if not specified.\n */\n acceptedMethods?: readonly ('url' | 'base64' | 'fileId')[];\n};\n\n// ============================================================================\n// Normalized Capabilities and Features\n// ============================================================================\n\n/**\n * Output format support flags\n */\nexport type OutputFormatSupport = {\n text: boolean;\n markdown: boolean;\n html: boolean;\n json: boolean;\n};\n\n/**\n * Feature status values for normalized features.\n * - `true`: Natively supported by the API\n * - `false`: Not supported\n * - `'deprecated'`: API deprecated this feature, may not work\n * - `'derived'`: SDK provides via transformation (e.g., maxPages from pageRange)\n */\nexport type FeatureStatus = true | false | 'deprecated' | 'derived';\n\n/**\n * Helper to check if a feature is enabled (true, deprecated, or derived)\n */\nexport function isFeatureEnabled(status: FeatureStatus): boolean {\n return status === true || status === 'deprecated' || status === 'derived';\n}\n\n/**\n * Page indexing convention used by provider\n */\nexport type PageIndexing = '0-indexed' | '1-indexed';\n\n/**\n * Normalized features across all providers.\n * Maps provider-specific option names to unified names.\n *\n * This enables UIs to query \"what features does this provider support?\"\n * and get a consistent answer across all providers.\n */\nexport type NormalizedFeatures = {\n // === Page Selection ===\n /** Limit to first N pages */\n maxPages: FeatureStatus;\n /** Specific page range selection */\n pageRange: FeatureStatus;\n\n // === Language ===\n /** OCR language hints (maps from 'langs') */\n languageHints: FeatureStatus;\n\n // === Processing Mode ===\n /** Quality/speed modes (fast/balanced/high_accuracy) */\n processingModes: FeatureStatus;\n /** Reducto agentic mode (higher accuracy, more cost) */\n agenticMode: FeatureStatus;\n\n // === Content Enhancement ===\n /** Custom prompts (maps from blockCorrectionPrompt, additionalPrompt, systemPrompt) */\n customPrompts: FeatureStatus;\n\n // === Output Features ===\n /** Extract embedded images (maps from extractImages, returnImages) */\n imageExtraction: FeatureStatus;\n /** Page delimiters (maps from paginate, addPageMarkers) */\n pageMarkers: FeatureStatus;\n /** Field-level citations with source references (page/char/block indices) */\n citations: FeatureStatus;\n /** Document chunking modes (RAG-optimized) */\n chunking: FeatureStatus;\n /** Auto-segmentation for multi-document PDFs */\n segmentation: FeatureStatus;\n\n // === OCR-Specific ===\n /** Re-run OCR on already-OCR'd documents */\n stripExistingOCR: FeatureStatus;\n /** Format lines in output */\n formatLines: FeatureStatus;\n /** Force OCR even if text layer exists */\n forceOCR: FeatureStatus;\n\n // === Table Handling ===\n /** Table format options (html/json/md/csv) */\n tableOutputFormats: FeatureStatus;\n /** Merge consecutive tables */\n tableMerging: FeatureStatus;\n\n // === Quality/Accuracy ===\n /** Block-level confidence scores */\n confidence: FeatureStatus;\n /** Bounding box coordinates for TEXT elements (pixel/normalized coords) */\n boundingBoxes: FeatureStatus;\n /** Bounding box coordinates for IMAGES/FIGURES only (not text) */\n imageBoundingBoxes: FeatureStatus;\n /** JSON schema validation for structured output */\n schemaValidation: FeatureStatus;\n /** Handwritten text recognition support */\n handwrittenText: FeatureStatus;\n /** Separate header/footer extraction from main content */\n headerFooterExtraction: FeatureStatus;\n\n // === NEW: Extended Features ===\n /** Optimize output for embeddings/RAG */\n embedOptimized: FeatureStatus;\n /** Handle encrypted/password-protected PDFs */\n passwordProtected: FeatureStatus;\n /** Filter block types (headers, footers, page numbers, etc.) */\n contentFiltering: FeatureStatus;\n /** OCR system/mode selection (standard/legacy, auto/full) */\n ocrMode: FeatureStatus;\n /** Async completion webhook callbacks */\n webhookCallback: FeatureStatus;\n /** Vision quality control (low/medium/high) - Gemini */\n mediaResolution: FeatureStatus;\n /** Track changes extraction from Word docs */\n changeTracking: FeatureStatus;\n /** Extract hyperlinks from documents */\n hyperlinkExtraction: FeatureStatus;\n /** Enhanced chart and graph interpretation (Datalab extras=chart_understanding) */\n chartUnderstanding: FeatureStatus;\n /** Control image caption generation (Datalab disable_image_captions) */\n imageCaptions: FeatureStatus;\n /** Extract signatures from documents (Reducto include: [\"signatures\"]) */\n signatureExtraction: FeatureStatus;\n /** Extract comments/annotations from documents (Reducto include: [\"comments\"]) */\n commentExtraction: FeatureStatus;\n /** Extract highlighted text from documents (Reducto include: [\"highlight\"]) */\n highlightExtraction: FeatureStatus;\n /** Summarize figures/charts with VLM (Reducto summarize_figures) */\n figureSummaries: FeatureStatus;\n\n // === Output Formats ===\n /** Supported output formats */\n outputFormats: OutputFormatSupport;\n};\n\n// ============================================================================\n// Normalized Provider Metadata\n// ============================================================================\n\nimport type { ProviderIdentity, ProviderVendor, AccessMethod } from './provider-identity.js';\n\n// Normalized provider metadata that works across all provider types\nexport type NormalizedProviderMetadata = {\n // Identity - legacy fields\n id: string;\n name: string;\n source: 'llm' | 'datalab' | 'unsiloed' | 'reducto' | string;\n type: 'LLM' | 'OCR' | 'VLM' | 'Split';\n\n // NEW: 3-layer identity (provider/model/method)\n identity?: {\n /** Provider vendor (company) */\n provider: ProviderVendor | string;\n /** Model identifier */\n model: string;\n /** Access method (native, openrouter, self-hosted) */\n method?: AccessMethod;\n };\n\n // Capabilities (high-level, queryable)\n capabilities: {\n // === Existing ===\n supportsImages: boolean;\n supportsPDFs: boolean;\n supportsDocuments: boolean; // Word, Excel, PowerPoint\n supportsReasoning: boolean;\n supportsStructuredOutput: boolean;\n\n // === NEW: Processing Features ===\n supportsPrompts: boolean; // Custom prompts/instructions\n supportsCitations: boolean; // Field-level citations\n supportsChunking: boolean; // Document chunking (RAG-optimized)\n supportsImageExtraction: boolean; // Extract embedded images\n supportsPageMarkers: boolean; // Add page delimiters to output\n supportsLanguageHints: boolean; // Language hints for OCR\n supportsProcessingModes: boolean; // Quality/speed modes\n supportsSegmentation: boolean; // Auto-segment multi-doc PDFs\n\n // === NEW: Output Formats ===\n outputFormats: OutputFormatSupport;\n };\n\n // Features - fine-grained capability flags for UI\n features: NormalizedFeatures;\n\n // Input requirements (what type of input the provider needs)\n inputRequirements: InputRequirements;\n\n // Node compatibility\n compatibleNodes: {\n parse: boolean;\n extract: boolean;\n categorize: boolean;\n qualify: boolean;\n split: boolean;\n };\n\n // Input formats\n inputFormats: {\n imageMimeTypes: readonly string[];\n documentMimeTypes: readonly string[];\n inputMethods: readonly ('url' | 'base64' | 'fileId')[];\n maxImageSize?: number; // MB\n maxPdfSize?: number; // MB\n maxFileSize?: number; // MB (general)\n maxPages?: number;\n };\n\n // Pricing (normalized)\n pricing: {\n model: 'per-token' | 'per-page';\n // Per-token pricing (LLM)\n inputPer1kTokens?: number;\n outputPer1kTokens?: number;\n // Per-page pricing (OCR/VLM)\n perPage?: number;\n currency: 'USD';\n notes?: string;\n };\n\n // Rate limits\n rateLimits?: {\n requestsPerMinute?: number;\n docsPerMinute?: number;\n };\n\n // Raw metadata for advanced use\n raw: unknown;\n};\n\n/**\n * Feature names that can be queried (excludes outputFormats which is nested)\n */\nexport type FeatureName = Exclude<keyof NormalizedFeatures, 'outputFormats'>;\n\n// Query filters\nexport type ProviderQueryFilter = {\n // Filter by source (legacy)\n source?: 'llm' | 'datalab' | 'unsiloed' | 'reducto' | string | string[];\n\n // Filter by type\n type?: 'LLM' | 'OCR' | 'VLM' | 'Split' | ('LLM' | 'OCR' | 'VLM' | 'Split')[];\n\n // NEW: Filter by 3-layer identity\n /** Filter by provider vendor (company) */\n provider?: ProviderVendor | ProviderVendor[] | string | string[];\n /** Filter by model ID (requires provider to be specified for best results) */\n model?: string | string[];\n /** Filter by access method */\n method?: AccessMethod | AccessMethod[];\n\n // Filter by capabilities\n supports?: {\n images?: boolean;\n pdfs?: boolean;\n documents?: boolean;\n reasoning?: boolean;\n structuredOutput?: boolean;\n // NEW: Extended capability filters\n prompts?: boolean;\n citations?: boolean;\n chunking?: boolean;\n imageExtraction?: boolean;\n pageMarkers?: boolean;\n languageHints?: boolean;\n processingModes?: boolean;\n segmentation?: boolean;\n };\n\n // NEW: Filter by specific features (all must be supported)\n hasFeatures?: FeatureName[];\n\n // NEW: Filter by output format support\n outputFormat?: 'text' | 'markdown' | 'html' | 'json';\n\n // Filter by input requirements\n inputRequirements?: {\n /**\n * Filter by input type requirement.\n * - 'raw-document': Only providers that need raw document input\n * - 'parsed-text': Only providers that need parsed text\n * - 'any': Only providers that accept any input type\n * - ['raw-document', 'any']: Providers that accept raw documents (raw-document OR any)\n */\n inputType?: ProviderInputType | ProviderInputType[];\n };\n\n // Filter by node compatibility\n compatibleWith?: ('parse' | 'extract' | 'categorize' | 'qualify' | 'split')[];\n\n // Filter by MIME type support\n mimeType?: string | string[];\n\n // Filter by file size limits (MB)\n minFileSize?: number; // Provider must support at least this size\n maxFileSize?: number; // Provider must have limit at most this size\n\n // Filter by pricing\n maxCostPerPage?: number; // For OCR/VLM\n maxCostPer1kTokens?: number; // For LLM (input)\n\n // Custom filter function\n filter?: (provider: NormalizedProviderMetadata) => boolean;\n};\n\n// Registry for provider metadata\nconst providerRegistry = new Map<string, Map<string, NormalizedProviderMetadata>>();\n\n/**\n * Register provider metadata from a provider package\n *\n * @param source - Source identifier (e.g., 'llm', 'datalab')\n * @param metadata - Raw metadata object from the provider package\n * @param normalizer - Function to normalize the metadata\n *\n * @example\n * ```typescript\n * import { PROVIDER_METADATA } from '@doclo/providers-llm';\n * registerProviderMetadata('llm', PROVIDER_METADATA, normalizeLLMMetadata);\n * ```\n */\nexport function registerProviderMetadata(\n source: string,\n metadata: Record<string, unknown>,\n normalizer?: (id: string, data: unknown, source: string) => NormalizedProviderMetadata\n): void {\n const normalized = new Map<string, NormalizedProviderMetadata>();\n\n for (const [id, data] of Object.entries(metadata)) {\n if (normalizer) {\n normalized.set(id, normalizer(id, data, source));\n } else {\n // Use default normalizer based on source\n normalized.set(id, defaultNormalizer(id, data, source));\n }\n }\n\n providerRegistry.set(source, normalized);\n}\n\n/**\n * Get all registered providers (normalized)\n */\nexport function getAllProviders(): NormalizedProviderMetadata[] {\n const all: NormalizedProviderMetadata[] = [];\n for (const providers of providerRegistry.values()) {\n all.push(...providers.values());\n }\n return all;\n}\n\n/**\n * Query providers with filters\n *\n * @param filter - Query filters\n * @returns Array of matching providers\n *\n * @example\n * ```typescript\n * // Get all providers that support PDFs\n * const pdfProviders = queryProviders({ supports: { pdfs: true } });\n *\n * // Get cheap OCR providers\n * const cheapOcr = queryProviders({\n * type: 'OCR',\n * maxCostPerPage: 0.02\n * });\n *\n * // Get providers that can handle large files\n * const largeFileProviders = queryProviders({ minFileSize: 100 });\n *\n * // Get providers compatible with extract() node\n * const extractProviders = queryProviders({\n * compatibleWith: ['extract']\n * });\n * ```\n */\nexport function queryProviders(filter: ProviderQueryFilter = {}): NormalizedProviderMetadata[] {\n let providers = getAllProviders();\n\n // Filter by source\n if (filter.source) {\n const sources = Array.isArray(filter.source) ? filter.source : [filter.source];\n providers = providers.filter(p => sources.includes(p.source));\n }\n\n // Filter by type\n if (filter.type) {\n const types = Array.isArray(filter.type) ? filter.type : [filter.type];\n providers = providers.filter(p => types.includes(p.type));\n }\n\n // NEW: Filter by 3-layer identity\n if (filter.provider) {\n const providerVendors = Array.isArray(filter.provider) ? filter.provider : [filter.provider];\n providers = providers.filter(p => p.identity?.provider && providerVendors.includes(p.identity.provider));\n }\n\n if (filter.model) {\n const models = Array.isArray(filter.model) ? filter.model : [filter.model];\n providers = providers.filter(p => p.identity?.model && models.includes(p.identity.model));\n }\n\n if (filter.method) {\n const methods = Array.isArray(filter.method) ? filter.method : [filter.method];\n providers = providers.filter(p => p.identity?.method && methods.includes(p.identity.method));\n }\n\n // Filter by capabilities\n if (filter.supports) {\n // Existing capability filters\n if (filter.supports.images !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsImages === filter.supports!.images);\n }\n if (filter.supports.pdfs !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsPDFs === filter.supports!.pdfs);\n }\n if (filter.supports.documents !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsDocuments === filter.supports!.documents);\n }\n if (filter.supports.reasoning !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsReasoning === filter.supports!.reasoning);\n }\n if (filter.supports.structuredOutput !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsStructuredOutput === filter.supports!.structuredOutput);\n }\n // NEW: Extended capability filters\n if (filter.supports.prompts !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsPrompts === filter.supports!.prompts);\n }\n if (filter.supports.citations !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsCitations === filter.supports!.citations);\n }\n if (filter.supports.chunking !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsChunking === filter.supports!.chunking);\n }\n if (filter.supports.imageExtraction !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsImageExtraction === filter.supports!.imageExtraction);\n }\n if (filter.supports.pageMarkers !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsPageMarkers === filter.supports!.pageMarkers);\n }\n if (filter.supports.languageHints !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsLanguageHints === filter.supports!.languageHints);\n }\n if (filter.supports.processingModes !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsProcessingModes === filter.supports!.processingModes);\n }\n if (filter.supports.segmentation !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsSegmentation === filter.supports!.segmentation);\n }\n }\n\n // NEW: Filter by specific features (all must be supported)\n // Uses isFeatureEnabled() to treat 'deprecated' and 'derived' as truthy\n if (filter.hasFeatures && filter.hasFeatures.length > 0) {\n providers = providers.filter(p =>\n filter.hasFeatures!.every(feature => isFeatureEnabled(p.features[feature]))\n );\n }\n\n // NEW: Filter by output format support\n if (filter.outputFormat) {\n providers = providers.filter(p =>\n p.capabilities.outputFormats[filter.outputFormat!] === true\n );\n }\n\n // Filter by input requirements\n if (filter.inputRequirements?.inputType !== undefined) {\n const inputTypes = Array.isArray(filter.inputRequirements.inputType)\n ? filter.inputRequirements.inputType\n : [filter.inputRequirements.inputType];\n providers = providers.filter(p => inputTypes.includes(p.inputRequirements.inputType));\n }\n\n // Filter by node compatibility\n if (filter.compatibleWith && filter.compatibleWith.length > 0) {\n providers = providers.filter(p =>\n filter.compatibleWith!.every(node => p.compatibleNodes[node])\n );\n }\n\n // Filter by MIME type support\n if (filter.mimeType) {\n const mimeTypes = Array.isArray(filter.mimeType) ? filter.mimeType : [filter.mimeType];\n providers = providers.filter(p => {\n const allMimes = [...p.inputFormats.imageMimeTypes, ...p.inputFormats.documentMimeTypes];\n return mimeTypes.every(mime => allMimes.includes(mime));\n });\n }\n\n // Filter by minimum file size support\n if (filter.minFileSize !== undefined) {\n providers = providers.filter(p => {\n const maxSize = p.inputFormats.maxFileSize ??\n Math.max(p.inputFormats.maxImageSize ?? 0, p.inputFormats.maxPdfSize ?? 0);\n return maxSize >= filter.minFileSize!;\n });\n }\n\n // Filter by maximum file size limit\n if (filter.maxFileSize !== undefined) {\n providers = providers.filter(p => {\n const maxSize = p.inputFormats.maxFileSize ??\n Math.max(p.inputFormats.maxImageSize ?? Infinity, p.inputFormats.maxPdfSize ?? Infinity);\n return maxSize <= filter.maxFileSize!;\n });\n }\n\n // Filter by cost\n if (filter.maxCostPerPage !== undefined) {\n providers = providers.filter(p =>\n p.pricing.perPage !== undefined && p.pricing.perPage <= filter.maxCostPerPage!\n );\n }\n\n if (filter.maxCostPer1kTokens !== undefined) {\n providers = providers.filter(p =>\n p.pricing.inputPer1kTokens !== undefined && p.pricing.inputPer1kTokens <= filter.maxCostPer1kTokens!\n );\n }\n\n // Custom filter\n if (filter.filter) {\n providers = providers.filter(filter.filter);\n }\n\n return providers;\n}\n\n/**\n * Get a single provider by ID\n */\nexport function getProviderById(id: string): NormalizedProviderMetadata | undefined {\n for (const providers of providerRegistry.values()) {\n if (providers.has(id)) {\n return providers.get(id);\n }\n }\n return undefined;\n}\n\n/**\n * Get providers by source\n */\nexport function getProvidersBySource(source: string): NormalizedProviderMetadata[] {\n const providers = providerRegistry.get(source);\n return providers ? [...providers.values()] : [];\n}\n\n/**\n * Clear all registered providers (useful for testing)\n */\nexport function clearProviderRegistry(): void {\n providerRegistry.clear();\n}\n\n// Default normalizer that handles LLM, Datalab, Reducto, and Unsiloed metadata formats\nfunction defaultNormalizer(id: string, data: unknown, source: string): NormalizedProviderMetadata {\n const d = data as Record<string, any>;\n\n if (source === 'llm') {\n return normalizeLLMProvider(id, d);\n } else if (source === 'datalab') {\n return normalizeDatalabProvider(id, d);\n } else if (source === 'reducto') {\n return normalizeReductoProvider(id, d);\n } else if (source === 'unsiloed') {\n return normalizeUnsiloedProvider(id, d);\n } else if (source === 'mistral') {\n return normalizeMistralProvider(id, d);\n }\n\n // Generic fallback\n const defaultOutputFormats: OutputFormatSupport = { text: true, markdown: false, html: false, json: false };\n const defaultFeatures: NormalizedFeatures = {\n maxPages: false,\n pageRange: false,\n languageHints: false,\n processingModes: false,\n agenticMode: false,\n customPrompts: false,\n imageExtraction: false,\n pageMarkers: false,\n citations: false,\n chunking: false,\n segmentation: false,\n stripExistingOCR: false,\n formatLines: false,\n forceOCR: false,\n tableOutputFormats: false,\n tableMerging: false,\n confidence: false,\n boundingBoxes: false,\n imageBoundingBoxes: false,\n schemaValidation: false,\n handwrittenText: false,\n headerFooterExtraction: false,\n // Extended features\n embedOptimized: false,\n passwordProtected: false,\n contentFiltering: false,\n ocrMode: false,\n webhookCallback: false,\n mediaResolution: false,\n changeTracking: false,\n hyperlinkExtraction: false,\n chartUnderstanding: false,\n imageCaptions: false,\n signatureExtraction: false,\n commentExtraction: false,\n highlightExtraction: false,\n figureSummaries: false,\n outputFormats: defaultOutputFormats,\n };\n\n return {\n id,\n name: d.name ?? id,\n source,\n type: d.type ?? 'LLM',\n capabilities: {\n supportsImages: d.capabilities?.supportsImages ?? false,\n supportsPDFs: d.capabilities?.supportsPDFs ?? false,\n supportsDocuments: d.capabilities?.supportsDocuments ?? false,\n supportsReasoning: d.capabilities?.supportsReasoning ?? false,\n supportsStructuredOutput: d.capabilities?.supportsStructuredOutput ?? false,\n supportsPrompts: false,\n supportsCitations: false,\n supportsChunking: false,\n supportsImageExtraction: false,\n supportsPageMarkers: false,\n supportsLanguageHints: false,\n supportsProcessingModes: false,\n supportsSegmentation: false,\n outputFormats: defaultOutputFormats,\n },\n features: defaultFeatures,\n inputRequirements: {\n inputType: d.inputRequirements?.inputType ?? 'any',\n acceptedMethods: d.inputRequirements?.acceptedMethods ?? d.inputFormats?.inputMethods ?? ['base64'],\n },\n compatibleNodes: {\n parse: d.compatibleNodes?.parse ?? false,\n extract: d.compatibleNodes?.extract ?? false,\n categorize: d.compatibleNodes?.categorize ?? false,\n qualify: d.compatibleNodes?.qualify ?? false,\n split: d.compatibleNodes?.split ?? false,\n },\n inputFormats: {\n imageMimeTypes: [],\n documentMimeTypes: [],\n inputMethods: ['base64'],\n },\n pricing: {\n model: 'per-token',\n currency: 'USD',\n },\n raw: data,\n };\n}\n\nfunction normalizeLLMProvider(id: string, d: Record<string, any>): NormalizedProviderMetadata {\n // LLM providers can output any format via prompting\n const outputFormats: OutputFormatSupport = {\n text: true,\n markdown: true,\n html: true,\n json: d.capabilities?.supportsStructuredOutput ?? true,\n };\n\n // Extract vendor from id or default to id\n const vendor = d.vendor ?? id;\n\n // LLM features - all LLMs support prompts and flexible output\n // LLMs don't have native pageRange support - they receive full document\n // maxPages can be 'derived' if SDK pre-processes pages before sending\n const features: NormalizedFeatures = {\n maxPages: 'derived' as FeatureStatus, // SDK can limit via pre-processing\n pageRange: false, // No native API support - LLMs receive full text\n languageHints: false, // Not applicable to LLMs\n processingModes: false, // Not applicable to LLMs\n agenticMode: false, // Not applicable to LLMs\n customPrompts: true, // All LLMs support prompts\n imageExtraction: false, // LLMs don't extract images\n pageMarkers: false, // LLMs don't add page markers\n citations: vendor === 'anthropic' ? true : false, // Anthropic has Citations API\n chunking: false, // LLMs don't do chunking\n segmentation: false, // LLMs don't do segmentation\n stripExistingOCR: false,\n formatLines: false,\n forceOCR: false,\n tableOutputFormats: false,\n tableMerging: false,\n confidence: false, // LLMs don't provide confidence scores\n boundingBoxes: false, // LLMs don't provide bounding boxes\n imageBoundingBoxes: false, // LLMs don't provide image bounding boxes (Gemini 2.0+ can via specific prompting, but not a simple toggle)\n schemaValidation: d.capabilities?.supportsStructuredOutput ?? false, // Some LLMs support schema validation\n handwrittenText: false, // Not specific to LLMs\n headerFooterExtraction: false, // LLMs don't extract header/footer separately\n // Extended features\n embedOptimized: false,\n passwordProtected: false,\n contentFiltering: false,\n ocrMode: false,\n webhookCallback: false,\n mediaResolution: vendor === 'google' ? true : false, // Google Gemini has mediaResolution\n changeTracking: false,\n hyperlinkExtraction: false,\n chartUnderstanding: false,\n imageCaptions: false,\n signatureExtraction: false,\n commentExtraction: false,\n highlightExtraction: false,\n figureSummaries: false,\n outputFormats,\n };\n\n return {\n id,\n name: d.name ?? id,\n source: 'llm',\n type: 'LLM',\n // NEW: 3-layer identity\n identity: {\n provider: vendor,\n model: d.defaultModel ?? id,\n method: 'native' as const,\n },\n capabilities: {\n supportsImages: d.capabilities?.supportsImages ?? false,\n supportsPDFs: d.capabilities?.supportsPDFs ?? false,\n supportsDocuments: false, // LLM providers don't support Office docs directly\n supportsReasoning: d.capabilities?.supportsReasoning ?? false,\n supportsStructuredOutput: d.capabilities?.supportsStructuredOutput ?? false,\n // NEW capabilities\n supportsPrompts: true,\n supportsCitations: vendor === 'anthropic', // Anthropic has Citations API\n supportsChunking: false,\n supportsImageExtraction: false,\n supportsPageMarkers: false,\n supportsLanguageHints: false,\n supportsProcessingModes: false,\n supportsSegmentation: false,\n outputFormats,\n },\n features,\n // LLM providers with vision can work with either raw documents or parsed text\n inputRequirements: {\n inputType: d.inputRequirements?.inputType ?? 'any',\n acceptedMethods: d.inputRequirements?.acceptedMethods ?? d.inputFormats?.images?.methods ?? ['base64', 'url'],\n },\n compatibleNodes: {\n parse: d.compatibleNodes?.parse ?? false,\n extract: d.compatibleNodes?.extract ?? false,\n categorize: d.compatibleNodes?.categorize ?? false,\n qualify: d.compatibleNodes?.qualify ?? false,\n split: d.compatibleNodes?.split ?? false,\n },\n inputFormats: {\n imageMimeTypes: d.inputFormats?.images?.mimeTypes ?? [],\n documentMimeTypes: ['application/pdf'], // PDFs only for LLM\n inputMethods: d.inputFormats?.images?.methods ?? ['base64'],\n maxImageSize: d.inputFormats?.images?.maxSize,\n maxPdfSize: d.inputFormats?.pdfs?.maxSize,\n maxPages: d.inputFormats?.pdfs?.maxPages,\n },\n pricing: {\n model: 'per-token',\n inputPer1kTokens: d.pricing?.inputPer1k,\n outputPer1kTokens: d.pricing?.outputPer1k,\n currency: 'USD',\n notes: d.pricing?.notes,\n },\n rateLimits: {\n requestsPerMinute: d.limits?.requestsPerMinute,\n },\n raw: d,\n };\n}\n\nfunction normalizeDatalabProvider(id: string, d: Record<string, any>): NormalizedProviderMetadata {\n const opts = d.supportedOptions ?? {};\n const isVLM = d.type === 'VLM';\n const isMarkerOCR = id === 'marker-ocr' || id.includes('marker-ocr');\n const isMarkerVLM = id === 'marker-vlm' || id.includes('marker-vlm');\n\n // Extract model from the provider/model format or use id\n const model = d.model ?? id;\n\n // Output formats based on provider type and outputFormat.features\n const outputFormats: OutputFormatSupport = {\n text: true,\n markdown: d.outputFormat?.features?.markdown ?? false,\n html: false,\n json: d.outputFormat?.features?.structuredJSON ?? isVLM,\n };\n\n // Map Datalab supportedOptions to normalized features\n // Mark deprecated features based on Datalab API docs\n const features: NormalizedFeatures = {\n maxPages: opts.maxPages ?? false,\n pageRange: opts.pageRange ?? false,\n languageHints: opts.langs ? 'deprecated' as FeatureStatus : false, // API ignores, handled automatically\n processingModes: opts.mode ?? false,\n agenticMode: false, // Datalab doesn't have agentic mode\n customPrompts: opts.blockCorrectionPrompt ? 'deprecated' as FeatureStatus : false, // Not currently supported\n imageExtraction: opts.extractImages ?? false,\n pageMarkers: opts.paginate ?? false, // maps from 'paginate'\n citations: isMarkerVLM ? true : false, // Marker VLM has citations\n chunking: false, // Datalab doesn't have chunking\n segmentation: opts.segmentation ?? false,\n stripExistingOCR: opts.stripExistingOCR ? 'deprecated' as FeatureStatus : false, // Managed automatically\n formatLines: opts.formatLines ? 'deprecated' as FeatureStatus : false, // Handled automatically\n forceOCR: 'deprecated' as FeatureStatus, // DEPRECATED: force_ocr param has no effect per API docs\n tableOutputFormats: false,\n tableMerging: false,\n confidence: false, // Datalab doesn't provide confidence scores\n boundingBoxes: d.outputFormat?.features?.boundingBoxes ?? true, // Datalab Surya provides text bboxes\n imageBoundingBoxes: isMarkerOCR || isMarkerVLM ? true : false, // Marker extracts images with bboxes\n schemaValidation: isVLM, // VLM providers support schema validation\n handwrittenText: true, // Datalab handles handwritten text\n headerFooterExtraction: false, // Datalab has issues with header/footer extraction\n // Extended features\n embedOptimized: false,\n passwordProtected: false,\n contentFiltering: false,\n ocrMode: false,\n webhookCallback: true, // Datalab supports webhook callbacks\n mediaResolution: false,\n changeTracking: true, // Datalab marker_extras supports track_changes\n hyperlinkExtraction: isMarkerOCR || isMarkerVLM, // Datalab extras=extract_links\n chartUnderstanding: isMarkerOCR || isMarkerVLM, // Datalab extras=chart_understanding\n imageCaptions: isMarkerOCR || isMarkerVLM, // Datalab disable_image_captions param\n signatureExtraction: false,\n commentExtraction: false,\n highlightExtraction: false,\n figureSummaries: false,\n outputFormats,\n };\n\n return {\n id,\n name: d.name ?? id,\n source: 'datalab',\n type: d.type ?? 'OCR',\n // NEW: 3-layer identity\n identity: {\n provider: 'datalab',\n model,\n method: 'native' as const, // Default to native, can be overridden when self-hosted\n },\n capabilities: {\n supportsImages: d.capabilities?.supportsImages ?? true,\n supportsPDFs: d.capabilities?.supportsPDFs ?? true,\n supportsDocuments: d.capabilities?.supportsDocuments ?? true,\n supportsReasoning: false, // Datalab doesn't do reasoning\n supportsStructuredOutput: isVLM,\n // NEW capabilities from supportedOptions\n supportsPrompts: opts.blockCorrectionPrompt ?? false,\n supportsCitations: opts.citations ?? false,\n supportsChunking: false,\n supportsImageExtraction: opts.extractImages ?? false,\n supportsPageMarkers: opts.paginate ?? false,\n supportsLanguageHints: opts.langs ?? false,\n supportsProcessingModes: opts.mode ?? false,\n supportsSegmentation: opts.segmentation ?? false,\n outputFormats,\n },\n features,\n // Datalab providers always need raw document input\n inputRequirements: {\n inputType: d.inputRequirements?.inputType ?? 'raw-document',\n acceptedMethods: d.inputRequirements?.acceptedMethods ?? d.inputFormats?.inputMethods ?? ['base64', 'url'],\n },\n compatibleNodes: {\n parse: d.compatibleNodes?.parse ?? false,\n extract: d.compatibleNodes?.extract ?? false,\n categorize: d.compatibleNodes?.categorize ?? false,\n qualify: d.compatibleNodes?.qualify ?? false,\n split: d.compatibleNodes?.split ?? false,\n },\n inputFormats: {\n imageMimeTypes: (d.inputFormats?.mimeTypes ?? []).filter((m: string) => m.startsWith('image/')),\n documentMimeTypes: (d.inputFormats?.mimeTypes ?? []).filter((m: string) => !m.startsWith('image/')),\n inputMethods: d.inputFormats?.inputMethods ?? ['base64'],\n maxFileSize: d.inputFormats?.maxFileSize,\n maxPages: d.inputFormats?.maxPages,\n },\n pricing: {\n model: 'per-page',\n perPage: d.pricing?.perPage,\n currency: 'USD',\n notes: d.pricing?.notes,\n },\n rateLimits: {\n docsPerMinute: d.apiConfig?.rateLimit?.docsPerMinute,\n },\n raw: d,\n };\n}\n\nfunction normalizeReductoProvider(id: string, d: Record<string, any>): NormalizedProviderMetadata {\n const opts = d.supportedOptions ?? {};\n const isVLM = d.type === 'VLM';\n const isExtract = d.compatibleNodes?.extract === true;\n const isParse = d.compatibleNodes?.parse === true;\n\n // Extract model from metadata or default to 'v1'\n const model = d.model ?? 'v1';\n\n // Output formats based on provider type\n const outputFormats: OutputFormatSupport = {\n text: d.outputFormat?.features?.textLines ?? true,\n markdown: d.outputFormat?.features?.markdown ?? d.compatibleNodes?.parse ?? false,\n html: opts.tableOutputFormat ?? false, // Reducto can output HTML tables\n json: d.outputFormat?.features?.structuredJSON ?? isExtract,\n };\n\n // Map Reducto supportedOptions to normalized features\n // Reducto doesn't have native maxPages, only pageRange - mark maxPages as derived\n const features: NormalizedFeatures = {\n maxPages: (opts.pageRange ?? false) ? 'derived' as FeatureStatus : false, // SDK derives from pageRange (1-indexed)\n pageRange: opts.pageRange ?? false,\n languageHints: false, // Reducto doesn't support language hints\n processingModes: false, // Reducto uses agentic instead\n agenticMode: opts.mode ?? false, // maps from 'mode' (agentic)\n customPrompts: opts.additionalPrompt ?? false, // maps from 'additionalPrompt'\n imageExtraction: opts.extractImages ?? false, // maps from 'returnImages'\n pageMarkers: true, // Reducto has addPageMarkers\n citations: opts.citations ?? false,\n chunking: opts.chunking ?? false,\n segmentation: opts.segmentation ?? false, // Via Split endpoint\n stripExistingOCR: false,\n formatLines: false,\n forceOCR: false,\n tableOutputFormats: opts.tableOutputFormat ?? false,\n tableMerging: d.compatibleNodes?.parse ?? false, // Parse has mergeTables\n confidence: opts.confidence ?? d.outputFormat?.features?.confidence ?? false, // Reducto Parse has confidence\n boundingBoxes: d.outputFormat?.features?.boundingBoxes ?? isParse, // Reducto Parse has text bounding boxes\n imageBoundingBoxes: isParse ? true : false, // Reducto Parse has figure bounding boxes\n schemaValidation: d.outputFormat?.features?.schemaValidation ?? isExtract, // Extract has schema validation\n handwrittenText: false, // Reducto doesn't specifically advertise handwriting\n headerFooterExtraction: true, // Reducto has Header/Footer block types\n // Extended features\n embedOptimized: isParse, // Reducto Parse supports retrieval.embedding_optimized: true\n passwordProtected: true, // Reducto handles encrypted PDFs\n contentFiltering: true, // Reducto can filter block types\n ocrMode: opts.ocrSystem ?? false, // Reducto has ocr_system selection\n webhookCallback: true, // Reducto supports webhook callbacks\n mediaResolution: false,\n changeTracking: true, // Reducto tracks changes in Word docs\n hyperlinkExtraction: true, // Reducto extracts hyperlinks via formatting.include\n chartUnderstanding: isParse, // Reducto enhance.agentic[].advanced_chart_agent for figures\n imageCaptions: false, // Not available in Reducto\n signatureExtraction: false, // NOT supported - formatting.include only accepts: change_tracking, highlight, comments, hyperlinks\n commentExtraction: isParse || isExtract, // Reducto formatting.include: [\"comments\"]\n highlightExtraction: isParse || isExtract, // Reducto formatting.include: [\"highlight\"]\n figureSummaries: isParse, // Reducto enhance.summarize_figures\n outputFormats,\n };\n\n return {\n id,\n name: d.name ?? id,\n source: 'reducto',\n type: d.type ?? 'OCR',\n // NEW: 3-layer identity\n identity: {\n provider: 'reducto',\n model,\n method: 'native' as const,\n },\n capabilities: {\n supportsImages: d.capabilities?.supportsImages ?? true,\n supportsPDFs: d.capabilities?.supportsPDFs ?? true,\n supportsDocuments: d.capabilities?.supportsDocuments ?? true,\n supportsReasoning: false, // Reducto doesn't do reasoning\n supportsStructuredOutput: isVLM || isExtract,\n // NEW capabilities from supportedOptions\n supportsPrompts: opts.additionalPrompt ?? false,\n supportsCitations: opts.citations ?? false,\n supportsChunking: opts.chunking ?? false,\n supportsImageExtraction: opts.extractImages ?? false,\n supportsPageMarkers: true,\n supportsLanguageHints: false,\n supportsProcessingModes: opts.mode ?? false, // agentic mode\n supportsSegmentation: opts.segmentation ?? false,\n outputFormats,\n },\n features,\n // Reducto providers always need raw document input\n inputRequirements: {\n inputType: d.inputRequirements?.inputType ?? 'raw-document',\n acceptedMethods: d.inputRequirements?.acceptedMethods ?? d.inputFormats?.inputMethods ?? ['base64', 'url'],\n },\n compatibleNodes: {\n parse: d.compatibleNodes?.parse ?? false,\n extract: d.compatibleNodes?.extract ?? false,\n categorize: d.compatibleNodes?.categorize ?? false,\n qualify: d.compatibleNodes?.qualify ?? false,\n split: d.compatibleNodes?.split ?? false,\n },\n inputFormats: {\n imageMimeTypes: (d.inputFormats?.mimeTypes ?? []).filter((m: string) => m.startsWith('image/')),\n documentMimeTypes: (d.inputFormats?.mimeTypes ?? []).filter((m: string) => !m.startsWith('image/')),\n inputMethods: d.inputFormats?.inputMethods ?? ['base64'],\n maxFileSize: d.inputFormats?.maxFileSize,\n maxPages: d.inputFormats?.maxPages,\n },\n pricing: {\n model: 'per-page',\n perPage: d.pricing?.standard ? d.pricing.standard * (d.pricing.usdPerCredit ?? 0.004) : d.pricing?.perPage,\n currency: 'USD',\n notes: d.pricing?.notes,\n },\n rateLimits: {\n docsPerMinute: d.apiConfig?.rateLimit?.docsPerMinute,\n },\n raw: d,\n };\n}\n\nfunction normalizeUnsiloedProvider(id: string, d: Record<string, any>): NormalizedProviderMetadata {\n const isVLM = d.type === 'VLM';\n const isExtract = d.compatibleNodes?.extract === true;\n const isParse = d.compatibleNodes?.parse === true;\n const isSplit = d.compatibleNodes?.split === true;\n const isCategorize = d.compatibleNodes?.categorize === true;\n\n // Extract model from metadata or default to 'v1'\n const model = d.model ?? 'v1';\n\n // Output formats based on provider type and outputFormat.features\n const outputFormats: OutputFormatSupport = {\n text: d.outputFormat?.features?.textLines ?? isParse,\n markdown: d.outputFormat?.features?.markdown ?? isParse,\n html: false, // Unsiloed doesn't output HTML\n json: d.outputFormat?.features?.structuredJSON ?? (isVLM || isExtract),\n };\n\n // Unsiloed features - inferred from outputFormat.features and capabilities\n // Note: Unsiloed doesn't have a formal supportedOptions like Datalab/Reducto\n const features: NormalizedFeatures = {\n maxPages: false, // Unsiloed doesn't have max pages option\n pageRange: false, // Unsiloed doesn't have page range option\n languageHints: false, // Unsiloed doesn't support language hints\n processingModes: false, // Unsiloed doesn't have fast/balanced/high_accuracy modes like Datalab\n agenticMode: false, // Unsiloed doesn't have agentic mode\n customPrompts: false, // Unsiloed doesn't support custom prompts\n imageExtraction: false, // Unsiloed doesn't extract images\n pageMarkers: false, // Unsiloed doesn't add page markers\n citations: d.outputFormat?.features?.citations ?? isExtract, // Extract has citations\n chunking: d.outputFormat?.features?.semanticChunking ?? isParse, // Parse has semantic chunking\n segmentation: isSplit, // Split provider does segmentation\n stripExistingOCR: false,\n formatLines: false,\n forceOCR: false,\n tableOutputFormats: false,\n tableMerging: false,\n confidence: d.outputFormat?.features?.confidence ?? false, // Unsiloed may provide confidence\n boundingBoxes: d.outputFormat?.features?.boundingBoxes ?? isParse, // Unsiloed Parse has bounding boxes\n imageBoundingBoxes: false, // Unsiloed doesn't return image-specific bboxes\n schemaValidation: isExtract, // Extract supports schema validation\n handwrittenText: d.capabilities?.specialFeatures?.includes('handwritten text') ?? false, // Parse supports handwriting\n headerFooterExtraction: false, // Unsiloed doesn't extract header/footer separately\n // Extended features\n embedOptimized: false,\n passwordProtected: false,\n contentFiltering: isParse, // Parse supports keep_segment_types: [\"table\", \"picture\", \"formula\", \"text\"]\n ocrMode: isParse, // Parse endpoint supports ocr_mode: 'auto_ocr' | 'full_ocr'\n webhookCallback: false, // Unsiloed is synchronous\n mediaResolution: false,\n changeTracking: false,\n hyperlinkExtraction: false,\n chartUnderstanding: false, // Not available in Unsiloed\n imageCaptions: false, // Not available in Unsiloed\n signatureExtraction: false, // Not available in Unsiloed\n commentExtraction: false, // Not available in Unsiloed\n highlightExtraction: false, // Not available in Unsiloed\n figureSummaries: false, // Not available in Unsiloed\n outputFormats,\n };\n\n return {\n id,\n name: d.name ?? id,\n source: 'unsiloed',\n type: d.type ?? 'OCR',\n // NEW: 3-layer identity\n identity: {\n provider: 'unsiloed',\n model,\n method: 'native' as const,\n },\n capabilities: {\n supportsImages: d.capabilities?.supportsImages ?? true,\n supportsPDFs: d.capabilities?.supportsPDFs ?? true,\n supportsDocuments: d.capabilities?.supportsDocuments ?? false,\n supportsReasoning: false, // Unsiloed doesn't do reasoning\n supportsStructuredOutput: isVLM || isExtract,\n // NEW capabilities\n supportsPrompts: false, // Unsiloed doesn't support custom prompts\n supportsCitations: d.outputFormat?.features?.citations ?? isExtract,\n supportsChunking: d.outputFormat?.features?.semanticChunking ?? isParse,\n supportsImageExtraction: false,\n supportsPageMarkers: false,\n supportsLanguageHints: false,\n supportsProcessingModes: false, // Unsiloed doesn't have fast/balanced/high_accuracy modes\n supportsSegmentation: isSplit || isCategorize,\n outputFormats,\n },\n features,\n // Unsiloed providers always need raw document input\n inputRequirements: {\n inputType: d.inputRequirements?.inputType ?? 'raw-document',\n acceptedMethods: d.inputRequirements?.acceptedMethods ?? d.inputFormats?.inputMethods ?? ['base64', 'url'],\n },\n compatibleNodes: {\n parse: d.compatibleNodes?.parse ?? false,\n extract: d.compatibleNodes?.extract ?? false,\n categorize: d.compatibleNodes?.categorize ?? false,\n qualify: d.compatibleNodes?.qualify ?? false,\n split: d.compatibleNodes?.split ?? false,\n },\n inputFormats: {\n imageMimeTypes: (d.inputFormats?.mimeTypes ?? []).filter((m: string) => m.startsWith('image/')),\n documentMimeTypes: (d.inputFormats?.mimeTypes ?? []).filter((m: string) => !m.startsWith('image/')),\n inputMethods: d.inputFormats?.inputMethods ?? ['base64'],\n maxFileSize: d.inputFormats?.maxFileSize,\n maxPages: d.inputFormats?.maxPages,\n },\n pricing: {\n model: 'per-page',\n perPage: d.pricing?.standardUSD ?? d.pricing?.perPage,\n currency: 'USD',\n notes: d.pricing?.notes,\n },\n rateLimits: {\n docsPerMinute: d.apiConfig?.rateLimit?.docsPerMinute,\n },\n raw: d,\n };\n}\n\n// Convenience functions\n\n/**\n * Get providers that support a specific MIME type\n */\nexport function getProvidersForMimeType(mimeType: string): NormalizedProviderMetadata[] {\n return queryProviders({ mimeType });\n}\n\n/**\n * Get the cheapest provider for a specific capability\n */\nexport function getCheapestProviderFor(\n capability: 'ocr' | 'extraction' | 'parse'\n): NormalizedProviderMetadata | undefined {\n let providers: NormalizedProviderMetadata[];\n\n switch (capability) {\n case 'ocr':\n case 'parse':\n providers = queryProviders({ compatibleWith: ['parse'] });\n break;\n case 'extraction':\n providers = queryProviders({ compatibleWith: ['extract'] });\n break;\n }\n\n // Sort by cost (per-page first, then per-token)\n return providers.sort((a, b) => {\n const costA = a.pricing.perPage ?? (a.pricing.inputPer1kTokens ?? Infinity);\n const costB = b.pricing.perPage ?? (b.pricing.inputPer1kTokens ?? Infinity);\n return costA - costB;\n })[0];\n}\n\n/**\n * Get providers with the largest file size support\n */\nexport function getProvidersForLargeFiles(minSizeMB: number = 100): NormalizedProviderMetadata[] {\n return queryProviders({ minFileSize: minSizeMB });\n}\n\n// ============================================================================\n// Model-Level Metadata Types\n// ============================================================================\n\n/**\n * Type alias for capabilities object (for model override typing)\n */\nexport type NormalizedCapabilities = NormalizedProviderMetadata['capabilities'];\n\n/**\n * Type alias for node compatibility object\n */\nexport type NodeCompatibility = NormalizedProviderMetadata['compatibleNodes'];\n\n/**\n * Type alias for pricing configuration\n */\nexport type NormalizedPricing = NormalizedProviderMetadata['pricing'];\n\n/**\n * Node type names for querying\n */\nexport type NodeTypeName = 'parse' | 'extract' | 'categorize' | 'qualify' | 'split';\n\n/**\n * Model-level limits that may differ from provider defaults\n */\nexport type ModelLimits = {\n maxContextTokens?: number;\n maxOutputTokens?: number;\n maxFileSize?: number; // MB\n maxPages?: number;\n};\n\n/**\n * Model-level metadata that can override provider defaults.\n * Unspecified fields inherit from the provider.\n */\nexport type ModelMetadata = {\n /** Model ID as used in API calls */\n id: string;\n\n /** Human-readable name (optional, defaults to id) */\n name?: string;\n\n /** OpenRouter model ID (e.g., 'openai/gpt-4.1') */\n openRouterId?: string;\n\n // =========================================\n // Capability Overrides (inherit if unset)\n // =========================================\n\n /** Override provider capabilities */\n capabilities?: Partial<NormalizedCapabilities>;\n\n /** Override provider input requirements */\n inputRequirements?: Partial<InputRequirements>;\n\n /** Override provider node compatibility */\n compatibleNodes?: Partial<NodeCompatibility>;\n\n // =========================================\n // Model-Specific Values\n // =========================================\n\n /** Model-specific pricing */\n pricing?: {\n inputPer1kTokens?: number;\n outputPer1kTokens?: number;\n perPage?: number;\n };\n\n /** Model-specific limits */\n limits?: ModelLimits;\n};\n\n/**\n * Provider metadata extended with model array\n */\nexport type ProviderMetadataWithModels = NormalizedProviderMetadata & {\n /** Per-model metadata with override capabilities */\n models?: ModelMetadata[];\n};\n\n/**\n * Fully resolved model metadata (all inheritance applied)\n */\nexport type ResolvedModelMetadata = {\n modelId: string;\n modelName: string;\n openRouterId?: string;\n providerId: string;\n providerName: string;\n providerSource: string;\n capabilities: NormalizedCapabilities;\n features: NormalizedFeatures;\n inputRequirements: InputRequirements;\n compatibleNodes: NodeCompatibility;\n pricing: NormalizedPricing;\n limits?: ModelLimits;\n};\n\n/**\n * Filter options for model queries\n */\nexport type ModelQueryFilter = {\n /** Filter by provider ID */\n providerId?: string | string[];\n\n /** Filter by provider source */\n source?: string | string[];\n\n /** Filter by capabilities */\n supports?: {\n images?: boolean;\n pdfs?: boolean;\n documents?: boolean;\n reasoning?: boolean;\n structuredOutput?: boolean;\n // Extended capability filters (same as ProviderQueryFilter)\n prompts?: boolean;\n citations?: boolean;\n chunking?: boolean;\n imageExtraction?: boolean;\n pageMarkers?: boolean;\n languageHints?: boolean;\n processingModes?: boolean;\n segmentation?: boolean;\n };\n\n /** Filter by specific features (all must be supported) */\n hasFeatures?: FeatureName[];\n\n /** Filter by output format support */\n outputFormat?: 'text' | 'markdown' | 'html' | 'json';\n\n /** Filter by input requirements */\n inputRequirements?: {\n inputType?: ProviderInputType | ProviderInputType[];\n };\n\n /** Filter by node compatibility */\n compatibleWith?: NodeTypeName[];\n\n /** Filter by context window (minimum) */\n minContextTokens?: number;\n\n /** Custom filter function */\n filter?: (model: ResolvedModelMetadata) => boolean;\n};\n\n// Registry for provider metadata with models\nconst modelRegistry = new Map<string, ProviderMetadataWithModels>();\n\n/**\n * Register provider metadata with model information\n *\n * @param providerId - Provider identifier\n * @param metadata - Provider metadata with models array\n */\nexport function registerProviderWithModels(\n providerId: string,\n metadata: ProviderMetadataWithModels\n): void {\n modelRegistry.set(providerId, metadata);\n}\n\n/**\n * Resolve model metadata by applying inheritance from provider.\n * Returns fully resolved metadata for a specific model.\n *\n * @param providerId - Provider ID (e.g., 'openai', 'anthropic')\n * @param modelId - Model ID (e.g., 'gpt-4.1', 'claude-sonnet-4.5'). If not provided, returns provider defaults.\n * @returns Resolved model metadata or undefined if not found\n *\n * @example\n * ```typescript\n * const gpt4 = resolveModelMetadata('openai', 'gpt-4.1');\n * console.log(gpt4?.capabilities.supportsReasoning); // false\n *\n * const o3 = resolveModelMetadata('openai', 'o3');\n * console.log(o3?.capabilities.supportsReasoning); // true\n * ```\n */\nexport function resolveModelMetadata(\n providerId: string,\n modelId?: string\n): ResolvedModelMetadata | undefined {\n // Try model registry first (has detailed model info)\n const providerWithModels = modelRegistry.get(providerId);\n if (providerWithModels) {\n return resolveFromProviderWithModels(providerWithModels, modelId);\n }\n\n // Fall back to basic provider registry\n const provider = getProviderById(providerId);\n if (!provider) return undefined;\n\n // Return provider-level metadata (no model-specific overrides)\n return {\n modelId: modelId ?? providerId,\n modelName: modelId ?? provider.name,\n providerId: provider.id,\n providerName: provider.name,\n providerSource: provider.source,\n capabilities: { ...provider.capabilities },\n features: { ...provider.features },\n inputRequirements: { ...provider.inputRequirements },\n compatibleNodes: { ...provider.compatibleNodes },\n pricing: { ...provider.pricing },\n };\n}\n\n/**\n * Resolve model from provider with models array (internal helper)\n */\nfunction resolveFromProviderWithModels(\n provider: ProviderMetadataWithModels,\n modelId?: string\n): ResolvedModelMetadata {\n // Find model in models array\n const model = modelId\n ? provider.models?.find(m => m.id === modelId)\n : undefined;\n\n // Build resolved metadata with inheritance\n return {\n modelId: model?.id ?? modelId ?? provider.id,\n modelName: model?.name ?? model?.id ?? modelId ?? provider.name,\n openRouterId: model?.openRouterId,\n providerId: provider.id,\n providerName: provider.name,\n providerSource: provider.source,\n\n // Merge capabilities (model overrides provider)\n capabilities: {\n supportsImages: model?.capabilities?.supportsImages ?? provider.capabilities.supportsImages,\n supportsPDFs: model?.capabilities?.supportsPDFs ?? provider.capabilities.supportsPDFs,\n supportsDocuments: model?.capabilities?.supportsDocuments ?? provider.capabilities.supportsDocuments,\n supportsReasoning: model?.capabilities?.supportsReasoning ?? provider.capabilities.supportsReasoning,\n supportsStructuredOutput: model?.capabilities?.supportsStructuredOutput ?? provider.capabilities.supportsStructuredOutput,\n // NEW capabilities\n supportsPrompts: model?.capabilities?.supportsPrompts ?? provider.capabilities.supportsPrompts,\n supportsCitations: model?.capabilities?.supportsCitations ?? provider.capabilities.supportsCitations,\n supportsChunking: model?.capabilities?.supportsChunking ?? provider.capabilities.supportsChunking,\n supportsImageExtraction: model?.capabilities?.supportsImageExtraction ?? provider.capabilities.supportsImageExtraction,\n supportsPageMarkers: model?.capabilities?.supportsPageMarkers ?? provider.capabilities.supportsPageMarkers,\n supportsLanguageHints: model?.capabilities?.supportsLanguageHints ?? provider.capabilities.supportsLanguageHints,\n supportsProcessingModes: model?.capabilities?.supportsProcessingModes ?? provider.capabilities.supportsProcessingModes,\n supportsSegmentation: model?.capabilities?.supportsSegmentation ?? provider.capabilities.supportsSegmentation,\n outputFormats: model?.capabilities?.outputFormats ?? provider.capabilities.outputFormats,\n },\n\n // Merge input requirements\n inputRequirements: {\n inputType: model?.inputRequirements?.inputType ?? provider.inputRequirements.inputType,\n acceptedMethods: model?.inputRequirements?.acceptedMethods ?? provider.inputRequirements.acceptedMethods,\n },\n\n // Merge node compatibility\n compatibleNodes: {\n parse: model?.compatibleNodes?.parse ?? provider.compatibleNodes.parse,\n extract: model?.compatibleNodes?.extract ?? provider.compatibleNodes.extract,\n categorize: model?.compatibleNodes?.categorize ?? provider.compatibleNodes.categorize,\n qualify: model?.compatibleNodes?.qualify ?? provider.compatibleNodes.qualify,\n split: model?.compatibleNodes?.split ?? provider.compatibleNodes.split,\n },\n\n // Features (inherited from provider - models don't override features)\n features: { ...provider.features },\n\n // Merge pricing\n pricing: {\n model: provider.pricing.model,\n inputPer1kTokens: model?.pricing?.inputPer1kTokens ?? provider.pricing.inputPer1kTokens,\n outputPer1kTokens: model?.pricing?.outputPer1kTokens ?? provider.pricing.outputPer1kTokens,\n perPage: model?.pricing?.perPage ?? provider.pricing.perPage,\n currency: provider.pricing.currency,\n notes: provider.pricing.notes,\n },\n\n // Model limits\n limits: model?.limits,\n };\n}\n\n/**\n * Query models with filters.\n * Returns all models that match the filter criteria.\n *\n * @param filter - Query filters\n * @returns Array of matching resolved model metadata\n *\n * @example\n * ```typescript\n * // Get all reasoning models\n * const reasoningModels = queryModels({ supports: { reasoning: true } });\n *\n * // Get models with large context windows\n * const largeContextModels = queryModels({ minContextTokens: 100000 });\n *\n * // Get OpenAI models compatible with extract()\n * const openaiExtract = queryModels({\n * providerId: 'openai',\n * compatibleWith: ['extract']\n * });\n * ```\n */\nexport function queryModels(filter: ModelQueryFilter = {}): ResolvedModelMetadata[] {\n const results: ResolvedModelMetadata[] = [];\n\n // Collect all models from model registry\n for (const [providerId, provider] of modelRegistry) {\n // Check provider-level filters first\n if (filter.providerId) {\n const providerIds = Array.isArray(filter.providerId) ? filter.providerId : [filter.providerId];\n if (!providerIds.includes(providerId)) continue;\n }\n\n if (filter.source) {\n const sources = Array.isArray(filter.source) ? filter.source : [filter.source];\n if (!sources.includes(provider.source)) continue;\n }\n\n // Resolve each model\n const models = provider.models ?? [{ id: provider.id }];\n for (const model of models) {\n const resolved = resolveFromProviderWithModels(provider, model.id);\n if (matchesModelFilter(resolved, filter)) {\n results.push(resolved);\n }\n }\n }\n\n // Also include providers from basic registry that don't have detailed models\n for (const provider of getAllProviders()) {\n // Skip if already in model registry\n if (modelRegistry.has(provider.id)) continue;\n\n // Check provider-level filters\n if (filter.providerId) {\n const providerIds = Array.isArray(filter.providerId) ? filter.providerId : [filter.providerId];\n if (!providerIds.includes(provider.id)) continue;\n }\n\n if (filter.source) {\n const sources = Array.isArray(filter.source) ? filter.source : [filter.source];\n if (!sources.includes(provider.source)) continue;\n }\n\n const resolved = resolveModelMetadata(provider.id);\n if (resolved && matchesModelFilter(resolved, filter)) {\n results.push(resolved);\n }\n }\n\n return results;\n}\n\n/**\n * Check if resolved model matches filter criteria (internal helper)\n */\nfunction matchesModelFilter(model: ResolvedModelMetadata, filter: ModelQueryFilter): boolean {\n // Check capabilities\n if (filter.supports) {\n if (filter.supports.images !== undefined && model.capabilities.supportsImages !== filter.supports.images) {\n return false;\n }\n if (filter.supports.pdfs !== undefined && model.capabilities.supportsPDFs !== filter.supports.pdfs) {\n return false;\n }\n if (filter.supports.documents !== undefined && model.capabilities.supportsDocuments !== filter.supports.documents) {\n return false;\n }\n if (filter.supports.reasoning !== undefined && model.capabilities.supportsReasoning !== filter.supports.reasoning) {\n return false;\n }\n if (filter.supports.structuredOutput !== undefined && model.capabilities.supportsStructuredOutput !== filter.supports.structuredOutput) {\n return false;\n }\n // Extended capability filters\n if (filter.supports.prompts !== undefined && model.capabilities.supportsPrompts !== filter.supports.prompts) {\n return false;\n }\n if (filter.supports.citations !== undefined && model.capabilities.supportsCitations !== filter.supports.citations) {\n return false;\n }\n if (filter.supports.chunking !== undefined && model.capabilities.supportsChunking !== filter.supports.chunking) {\n return false;\n }\n if (filter.supports.imageExtraction !== undefined && model.capabilities.supportsImageExtraction !== filter.supports.imageExtraction) {\n return false;\n }\n if (filter.supports.pageMarkers !== undefined && model.capabilities.supportsPageMarkers !== filter.supports.pageMarkers) {\n return false;\n }\n if (filter.supports.languageHints !== undefined && model.capabilities.supportsLanguageHints !== filter.supports.languageHints) {\n return false;\n }\n if (filter.supports.processingModes !== undefined && model.capabilities.supportsProcessingModes !== filter.supports.processingModes) {\n return false;\n }\n if (filter.supports.segmentation !== undefined && model.capabilities.supportsSegmentation !== filter.supports.segmentation) {\n return false;\n }\n }\n\n // Check specific features (all must be supported)\n // Uses isFeatureEnabled() to treat 'deprecated' and 'derived' as truthy\n if (filter.hasFeatures && filter.hasFeatures.length > 0) {\n for (const feature of filter.hasFeatures) {\n if (!isFeatureEnabled(model.features[feature])) {\n return false;\n }\n }\n }\n\n // Check output format support\n if (filter.outputFormat) {\n if (model.capabilities.outputFormats[filter.outputFormat] !== true) {\n return false;\n }\n }\n\n // Check input requirements\n if (filter.inputRequirements?.inputType !== undefined) {\n const inputTypes = Array.isArray(filter.inputRequirements.inputType)\n ? filter.inputRequirements.inputType\n : [filter.inputRequirements.inputType];\n if (!inputTypes.includes(model.inputRequirements.inputType)) {\n return false;\n }\n }\n\n // Check node compatibility\n if (filter.compatibleWith && filter.compatibleWith.length > 0) {\n for (const node of filter.compatibleWith) {\n if (!model.compatibleNodes[node]) {\n return false;\n }\n }\n }\n\n // Check context tokens\n if (filter.minContextTokens !== undefined) {\n const contextTokens = model.limits?.maxContextTokens ?? 0;\n if (contextTokens < filter.minContextTokens) {\n return false;\n }\n }\n\n // Custom filter\n if (filter.filter && !filter.filter(model)) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Get all models compatible with a specific node type.\n *\n * @param nodeType - Node type to check compatibility\n * @returns Array of resolved model metadata\n *\n * @example\n * ```typescript\n * // Get all models that can be used with extract()\n * const extractModels = getModelsForNode('extract');\n *\n * // Get all models that can be used with parse()\n * const parseModels = getModelsForNode('parse');\n * ```\n */\nexport function getModelsForNode(nodeType: NodeTypeName): ResolvedModelMetadata[] {\n return queryModels({ compatibleWith: [nodeType] });\n}\n\n/**\n * Get all registered models (resolved)\n */\nexport function getAllModels(): ResolvedModelMetadata[] {\n return queryModels({});\n}\n\n/**\n * Clear model registry (useful for testing)\n */\nexport function clearModelRegistry(): void {\n modelRegistry.clear();\n}\n\n// ============================================================================\n// Identity-Based Query Functions\n// ============================================================================\n\n/**\n * Query providers by 3-layer identity (provider/model/method)\n *\n * @example\n * ```typescript\n * // Get all Datalab providers\n * const datalabProviders = queryByIdentity({ provider: 'datalab' });\n *\n * // Get specific model\n * const surya = queryByIdentity({ provider: 'datalab', model: 'surya' });\n *\n * // Get all self-hosted capable providers\n * const selfHosted = queryByIdentity({ method: 'self-hosted' });\n * ```\n */\nexport function queryByIdentity(filter: {\n provider?: ProviderVendor | string;\n model?: string;\n method?: AccessMethod;\n}): NormalizedProviderMetadata[] {\n return queryProviders({\n provider: filter.provider,\n model: filter.model,\n method: filter.method,\n });\n}\n\n/**\n * Get all models available for a specific provider vendor\n *\n * @example\n * ```typescript\n * const datalabModels = getModelsForProvider('datalab');\n * // => ['surya', 'marker-ocr', 'marker-vlm']\n * ```\n */\nexport function getModelsForProvider(provider: ProviderVendor | string): string[] {\n const providers = queryProviders({ provider });\n const models = new Set<string>();\n for (const p of providers) {\n if (p.identity?.model) {\n models.add(p.identity.model);\n }\n }\n return [...models];\n}\n\n/**\n * Get available access methods for a provider/model combination\n *\n * @example\n * ```typescript\n * const methods = getMethodsForModel('datalab', 'surya');\n * // => ['native', 'self-hosted']\n * ```\n */\nexport function getMethodsForModel(\n provider: ProviderVendor | string,\n model: string\n): AccessMethod[] {\n const providers = queryProviders({ provider, model });\n const methods = new Set<AccessMethod>();\n for (const p of providers) {\n if (p.identity?.method) {\n methods.add(p.identity.method);\n }\n }\n return [...methods];\n}\n\n/**\n * Get all unique provider vendors in the registry\n *\n * @example\n * ```typescript\n * const vendors = getAllProviderVendors();\n * // => ['datalab', 'reducto', 'unsiloed', 'openai', 'anthropic', ...]\n * ```\n */\nexport function getAllProviderVendors(): string[] {\n const providers = getAllProviders();\n const vendors = new Set<string>();\n for (const p of providers) {\n if (p.identity?.provider) {\n vendors.add(p.identity.provider);\n }\n }\n return [...vendors];\n}\n\n// ============================================================================\n// Derived Feature Transformation Utilities\n// ============================================================================\n\n/**\n * Page indexing convention by provider source.\n * Used when converting maxPages to pageRange.\n */\nconst PAGE_INDEXING: Record<string, PageIndexing> = {\n datalab: '0-indexed',\n reducto: '1-indexed',\n mistral: '0-indexed',\n unsiloed: '1-indexed', // Default assumption\n llm: '1-indexed', // N/A but default\n};\n\n/**\n * Get the page indexing convention for a provider.\n *\n * @param provider - Provider metadata or source string\n * @returns Page indexing convention ('0-indexed' or '1-indexed')\n */\nexport function getPageIndexing(provider: NormalizedProviderMetadata | string): PageIndexing {\n const source = typeof provider === 'string' ? provider : provider.source;\n return PAGE_INDEXING[source] ?? '1-indexed';\n}\n\n/**\n * Options that can be transformed for derived features.\n */\nexport type DerivedFeatureOptions = {\n maxPages?: number;\n pageRange?: string;\n};\n\n/**\n * Result of derived feature transformation.\n */\nexport type TransformedOptions = {\n /** The transformed page_range parameter (provider-specific format) */\n page_range?: string;\n /** Array format for providers that support it (e.g., Mistral) */\n pages?: number[];\n /** Original options minus the derived ones */\n remainingOptions: Record<string, unknown>;\n};\n\n/**\n * Transform maxPages to provider-specific pageRange format.\n *\n * This utility handles the conversion when a provider has `maxPages: 'derived'`,\n * meaning the SDK provides maxPages functionality via the underlying pageRange API.\n *\n * @param options - User-provided options including maxPages\n * @param provider - Provider metadata to determine format\n * @returns Transformed options with provider-specific pageRange\n *\n * @example\n * ```typescript\n * // User wants first 5 pages from Reducto (1-indexed)\n * const result = transformDerivedFeatures({ maxPages: 5 }, reductoProvider);\n * // => { page_range: '1-5', remainingOptions: {} }\n *\n * // User wants first 5 pages from Datalab (0-indexed)\n * const result = transformDerivedFeatures({ maxPages: 5 }, datalabProvider);\n * // => { page_range: '0-4', remainingOptions: {} }\n *\n * // User wants first 5 pages from Mistral (0-indexed, array format)\n * const result = transformDerivedFeatures({ maxPages: 5 }, mistralProvider);\n * // => { page_range: '0-4', pages: [0,1,2,3,4], remainingOptions: {} }\n * ```\n */\nexport function transformDerivedFeatures(\n options: DerivedFeatureOptions & Record<string, unknown>,\n provider: NormalizedProviderMetadata\n): TransformedOptions {\n const { maxPages, pageRange, ...remainingOptions } = options;\n const result: TransformedOptions = { remainingOptions };\n\n // If user provided explicit pageRange, pass it through\n if (pageRange !== undefined) {\n result.page_range = pageRange;\n return result;\n }\n\n // If maxPages provided and provider has derived maxPages support\n if (maxPages !== undefined && provider.features.maxPages === 'derived') {\n const indexing = getPageIndexing(provider);\n\n if (indexing === '0-indexed') {\n // 0-indexed: first N pages = 0 to N-1\n result.page_range = `0-${maxPages - 1}`;\n\n // Mistral also supports array format\n if (provider.source === 'mistral') {\n result.pages = Array.from({ length: maxPages }, (_, i) => i);\n }\n } else {\n // 1-indexed: first N pages = 1 to N\n result.page_range = `1-${maxPages}`;\n }\n } else if (maxPages !== undefined && isFeatureEnabled(provider.features.maxPages)) {\n // Provider natively supports maxPages, pass it through in remainingOptions\n result.remainingOptions.maxPages = maxPages;\n }\n\n return result;\n}\n\n/**\n * Check if a provider requires derived feature transformation for maxPages.\n *\n * @param provider - Provider metadata\n * @returns true if maxPages needs to be transformed to pageRange\n */\nexport function requiresMaxPagesTransformation(provider: NormalizedProviderMetadata): boolean {\n return provider.features.maxPages === 'derived';\n}\n\n// ============================================================================\n// Mistral Provider Normalizer\n// ============================================================================\n\nfunction normalizeMistralProvider(id: string, d: Record<string, any>): NormalizedProviderMetadata {\n const opts = d.supportedOptions ?? {};\n const isVLM = d.type === 'VLM';\n const isOCR = d.type === 'OCR';\n\n // Extract model from metadata\n const model = d.model ?? id;\n\n // Output formats based on provider type\n const outputFormats: OutputFormatSupport = {\n text: true,\n markdown: d.outputFormat?.features?.markdown ?? isOCR,\n html: d.outputFormat?.features?.htmlTables ?? isOCR, // OCR 3 can output HTML tables\n json: d.outputFormat?.features?.structuredJSON ?? isVLM,\n };\n\n // Map Mistral supportedOptions to normalized features\n // Mistral VLM: bbox_annotation supports 1000 pages, document_annotation limited to 8 pages\n const features: NormalizedFeatures = {\n maxPages: d.inputFormats?.maxPages !== undefined,\n pageRange: true, // Mistral supports pages param: \"0-5\" or [0,2,5] (0-indexed)\n languageHints: false, // Mistral doesn't support language hints\n processingModes: false, // Mistral doesn't have processing modes\n agenticMode: false, // Mistral doesn't have agentic mode\n customPrompts: false, // Mistral OCR 3 doesn't support custom prompts\n imageExtraction: opts.includeImageBase64 ?? false, // Can include embedded images\n pageMarkers: false, // Mistral doesn't add page markers\n citations: false, // Mistral doesn't provide citations\n chunking: false, // Mistral doesn't do chunking\n segmentation: false, // Mistral doesn't do segmentation\n stripExistingOCR: false,\n formatLines: false,\n forceOCR: true, // OCR 3 always does OCR\n tableOutputFormats: opts.tableFormat ?? isOCR, // html or markdown table format\n tableMerging: false,\n confidence: false, // Mistral doesn't provide confidence scores\n boundingBoxes: false, // Mistral does NOT provide text-level bounding boxes\n imageBoundingBoxes: true, // Mistral provides image/figure bounding boxes only\n schemaValidation: d.outputFormat?.features?.schemaValidation ?? isVLM, // VLM supports schema\n handwrittenText: d.outputFormat?.features?.handwrittenText ?? true, // Excellent handwriting support\n headerFooterExtraction: opts.extractHeader ?? opts.extractFooter ?? false, // extract_header/extract_footer\n // Extended features\n embedOptimized: false,\n passwordProtected: false,\n contentFiltering: false,\n ocrMode: false,\n webhookCallback: false, // Mistral is synchronous\n mediaResolution: false,\n changeTracking: false,\n hyperlinkExtraction: true, // Response pages[].hyperlinks[] auto-extracted\n chartUnderstanding: false, // Not available as separate feature in Mistral\n imageCaptions: false, // Not available in Mistral\n signatureExtraction: false, // Not available in Mistral\n commentExtraction: false, // Not available in Mistral\n highlightExtraction: false, // Not available in Mistral\n figureSummaries: false, // Not available in Mistral\n outputFormats,\n };\n\n return {\n id: d.id ?? id,\n name: d.name ?? id,\n source: 'mistral',\n type: d.type ?? 'OCR',\n // 3-layer identity\n identity: {\n provider: 'mistral',\n model,\n method: 'native' as const,\n },\n capabilities: {\n supportsImages: d.capabilities?.supportsImages ?? true,\n supportsPDFs: d.capabilities?.supportsPDFs ?? true,\n supportsDocuments: d.capabilities?.supportsDocuments ?? true, // Supports DOCX, PPTX, TXT, EPUB, RTF, ODT, etc. (NOT XLSX)\n supportsReasoning: false, // OCR 3 doesn't do reasoning\n supportsStructuredOutput: d.capabilities?.supportsStructuredOutput ?? isVLM,\n // Extended capabilities\n supportsPrompts: false,\n supportsCitations: false,\n supportsChunking: false,\n supportsImageExtraction: opts.includeImageBase64 ?? false,\n supportsPageMarkers: false,\n supportsLanguageHints: false,\n supportsProcessingModes: false,\n supportsSegmentation: false,\n outputFormats,\n },\n features,\n // Mistral providers always need raw document input\n inputRequirements: {\n inputType: d.inputRequirements?.inputType ?? 'raw-document',\n acceptedMethods: d.inputRequirements?.acceptedMethods ?? ['base64', 'url'],\n },\n compatibleNodes: {\n parse: d.compatibleNodes?.parse ?? isOCR,\n extract: d.compatibleNodes?.extract ?? isVLM,\n categorize: d.compatibleNodes?.categorize ?? false,\n qualify: d.compatibleNodes?.qualify ?? false,\n split: d.compatibleNodes?.split ?? false,\n },\n inputFormats: {\n imageMimeTypes: (d.inputFormats?.mimeTypes ?? []).filter((m: string) => m.startsWith('image/')),\n documentMimeTypes: (d.inputFormats?.mimeTypes ?? []).filter((m: string) => !m.startsWith('image/')),\n inputMethods: d.inputFormats?.inputMethods ?? ['base64', 'url'],\n maxFileSize: d.inputFormats?.maxFileSize ?? 50, // 50MB limit\n maxPages: d.inputFormats?.maxPages ?? 1000,\n },\n pricing: {\n model: 'per-page',\n perPage: d.pricing?.perPage ?? 0.002, // $2/1000 pages\n currency: 'USD',\n notes: d.pricing?.notes ?? '$2 per 1000 pages',\n },\n rateLimits: {\n docsPerMinute: d.apiConfig?.rateLimit?.docsPerMinute,\n },\n raw: d,\n };\n}\n","/**\n * @doclo/core - Retry Utilities\n *\n * Shared retry infrastructure for LLM and OCR providers.\n * Includes exponential backoff, circuit breaker pattern, and error classification.\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Configuration for retry behavior\n */\nexport interface RetryConfig {\n /** Maximum number of retry attempts (default: 2) */\n maxRetries?: number;\n /** Base delay in milliseconds between retries (default: 1000) */\n retryDelay?: number;\n /** Enable exponential backoff (default: true) */\n useExponentialBackoff?: boolean;\n /** Maximum delay cap in milliseconds (default: 30000) */\n maxDelay?: number;\n}\n\n/**\n * Configuration for circuit breaker behavior\n */\nexport interface CircuitBreakerConfig {\n /** Number of consecutive failures before opening circuit (default: 3) */\n threshold?: number;\n /** Time in milliseconds before trying again after circuit opens (default: 30000) */\n resetTimeout?: number;\n}\n\n/**\n * Internal state for a circuit breaker\n */\nexport interface CircuitBreakerState {\n consecutiveFailures: number;\n lastFailureTime?: number;\n isOpen: boolean;\n}\n\n/**\n * Circuit breaker interface\n */\nexport interface CircuitBreaker {\n /** Check if circuit is currently open (should skip requests) */\n isOpen(): boolean;\n /** Record a successful request (resets failure count) */\n recordSuccess(): void;\n /** Record a failed request (may open circuit) */\n recordFailure(): void;\n /** Get current state for inspection */\n getState(): CircuitBreakerState;\n}\n\n/**\n * Options for the withRetry wrapper\n */\nexport interface WithRetryOptions<T> extends RetryConfig {\n /** Called before each retry attempt (for logging/observability) */\n onRetry?: (attempt: number, error: Error, delay: number) => void | Promise<void>;\n /** Override to parse Retry-After header from response errors */\n getRetryAfter?: (error: Error) => number | undefined;\n /** Circuit breaker to use (optional) */\n circuitBreaker?: CircuitBreaker;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Default retry configuration */\nexport const DEFAULT_RETRY_CONFIG: Required<RetryConfig> = {\n maxRetries: 2,\n retryDelay: 1000,\n useExponentialBackoff: true,\n maxDelay: 30000,\n};\n\n/** Default circuit breaker configuration */\nexport const DEFAULT_CIRCUIT_BREAKER_CONFIG: Required<CircuitBreakerConfig> = {\n threshold: 3,\n resetTimeout: 30000,\n};\n\n/**\n * HTTP status codes that are retryable\n * - 408: Request Timeout\n * - 429: Too Many Requests (rate limited)\n * - 500: Internal Server Error\n * - 502: Bad Gateway\n * - 503: Service Unavailable\n * - 504: Gateway Timeout\n */\nconst RETRYABLE_STATUS_CODES = ['408', '429', '500', '502', '503', '504'];\n\n/**\n * Error message patterns that indicate retryable errors\n */\nconst RETRYABLE_ERROR_PATTERNS = [\n 'timeout',\n 'rate limit',\n 'overloaded',\n 'econnreset',\n 'etimedout',\n 'enotfound',\n 'econnrefused',\n 'socket hang up',\n 'network error',\n];\n\n// ============================================================================\n// Error Classification\n// ============================================================================\n\n/**\n * Determines if an error is retryable based on its message content.\n *\n * Retryable errors include:\n * - HTTP 408, 429, 500, 502, 503, 504\n * - Timeout errors\n * - Rate limit errors\n * - Network errors (ECONNRESET, ETIMEDOUT, etc.)\n *\n * Non-retryable errors include:\n * - HTTP 400, 401, 403, 404 (client errors)\n * - Business logic failures\n *\n * @param error - The error to classify\n * @returns true if the error is retryable\n */\nexport function isRetryableError(error: Error): boolean {\n const message = error.message.toLowerCase();\n\n // Check for retryable HTTP status codes\n for (const code of RETRYABLE_STATUS_CODES) {\n if (message.includes(code)) {\n return true;\n }\n }\n\n // Check for retryable error patterns\n for (const pattern of RETRYABLE_ERROR_PATTERNS) {\n if (message.includes(pattern)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Extracts HTTP status code from an error message if present.\n *\n * @param error - The error to extract status from\n * @returns The HTTP status code or undefined\n */\nexport function extractStatusCode(error: Error): number | undefined {\n // Common patterns: \"HTTP 429\", \"status: 503\", \"failed: 500\"\n const patterns = [\n /\\b(\\d{3})\\b/, // Just the status code\n /status[:\\s]+(\\d{3})/i,\n /http[:\\s]+(\\d{3})/i,\n /failed[:\\s]+(\\d{3})/i,\n ];\n\n for (const pattern of patterns) {\n const match = error.message.match(pattern);\n if (match && match[1]) {\n const code = parseInt(match[1], 10);\n if (code >= 100 && code < 600) {\n return code;\n }\n }\n }\n\n return undefined;\n}\n\n/**\n * Parses Retry-After header value from error message or response.\n * Supports both seconds (integer) and HTTP-date formats.\n *\n * @param error - Error that may contain Retry-After information\n * @returns Delay in milliseconds, or undefined if not found\n */\nexport function parseRetryAfter(error: Error): number | undefined {\n const message = error.message;\n\n // Look for \"retry-after: X\" or \"Retry-After: X\" patterns\n const match = message.match(/retry-after[:\\s]+(\\d+)/i);\n if (match && match[1]) {\n const seconds = parseInt(match[1], 10);\n if (!isNaN(seconds) && seconds > 0 && seconds < 3600) {\n return seconds * 1000;\n }\n }\n\n return undefined;\n}\n\n// ============================================================================\n// Delay Calculation\n// ============================================================================\n\n/**\n * Calculates the delay before the next retry attempt.\n *\n * With exponential backoff enabled (default):\n * - Attempt 1: baseDelay * 2^0 = 1x baseDelay\n * - Attempt 2: baseDelay * 2^1 = 2x baseDelay\n * - Attempt 3: baseDelay * 2^2 = 4x baseDelay\n * Plus random jitter (0-1000ms) to prevent thundering herd.\n *\n * @param attempt - Current attempt number (1-indexed)\n * @param config - Retry configuration\n * @returns Delay in milliseconds\n */\nexport function calculateRetryDelay(\n attempt: number,\n config: RetryConfig = {}\n): number {\n const {\n retryDelay = DEFAULT_RETRY_CONFIG.retryDelay,\n useExponentialBackoff = DEFAULT_RETRY_CONFIG.useExponentialBackoff,\n maxDelay = DEFAULT_RETRY_CONFIG.maxDelay,\n } = config;\n\n if (!useExponentialBackoff) {\n return retryDelay;\n }\n\n // Exponential backoff: baseDelay * (2 ^ (attempt - 1)) + jitter\n const exponentialDelay = retryDelay * Math.pow(2, attempt - 1);\n const jitter = Math.random() * 1000; // 0-1000ms jitter\n return Math.min(exponentialDelay + jitter, maxDelay);\n}\n\n// ============================================================================\n// Circuit Breaker\n// ============================================================================\n\n// Global registry of circuit breakers by key\nconst circuitBreakerRegistry = new Map<string, CircuitBreaker>();\n\n/**\n * Creates or retrieves a circuit breaker for a given key.\n *\n * Circuit breakers prevent cascading failures by:\n * 1. Tracking consecutive failures per provider/endpoint\n * 2. \"Opening\" the circuit after threshold failures (skipping requests)\n * 3. Allowing a retry after resetTimeout (half-open state)\n * 4. Closing the circuit on success\n *\n * @param key - Unique identifier (e.g., \"datalab:surya\" or \"openai:gpt-4\")\n * @param config - Circuit breaker configuration\n * @returns CircuitBreaker instance\n */\nexport function createCircuitBreaker(\n key: string,\n config: CircuitBreakerConfig = {}\n): CircuitBreaker {\n // Return existing circuit breaker if one exists for this key\n const existing = circuitBreakerRegistry.get(key);\n if (existing) {\n return existing;\n }\n\n const {\n threshold = DEFAULT_CIRCUIT_BREAKER_CONFIG.threshold,\n resetTimeout = DEFAULT_CIRCUIT_BREAKER_CONFIG.resetTimeout,\n } = config;\n\n let state: CircuitBreakerState = {\n consecutiveFailures: 0,\n isOpen: false,\n };\n\n const circuitBreaker: CircuitBreaker = {\n isOpen(): boolean {\n if (!state.isOpen) return false;\n\n // Check if enough time has passed to try again (half-open state)\n if (state.lastFailureTime && Date.now() - state.lastFailureTime > resetTimeout) {\n // Reset to allow a trial request\n state = {\n consecutiveFailures: 0,\n isOpen: false,\n };\n return false;\n }\n\n return true;\n },\n\n recordSuccess(): void {\n state = {\n consecutiveFailures: 0,\n isOpen: false,\n };\n },\n\n recordFailure(): void {\n state.consecutiveFailures++;\n state.lastFailureTime = Date.now();\n\n if (state.consecutiveFailures >= threshold) {\n state.isOpen = true;\n console.warn(`Circuit breaker opened for ${key} after ${state.consecutiveFailures} consecutive failures`);\n }\n },\n\n getState(): CircuitBreakerState {\n return { ...state };\n },\n };\n\n circuitBreakerRegistry.set(key, circuitBreaker);\n return circuitBreaker;\n}\n\n/**\n * Clears all circuit breakers. Useful for testing.\n */\nexport function clearCircuitBreakers(): void {\n circuitBreakerRegistry.clear();\n}\n\n/**\n * Gets the circuit breaker for a key without creating one.\n *\n * @param key - Unique identifier\n * @returns CircuitBreaker or undefined if not found\n */\nexport function getCircuitBreaker(key: string): CircuitBreaker | undefined {\n return circuitBreakerRegistry.get(key);\n}\n\n// ============================================================================\n// Retry Wrapper\n// ============================================================================\n\n/**\n * Wraps an async function with retry logic.\n *\n * @example\n * ```typescript\n * const result = await withRetry(\n * () => fetchWithTimeout(url, options),\n * {\n * maxRetries: 3,\n * retryDelay: 1000,\n * useExponentialBackoff: true,\n * onRetry: (attempt, error, delay) => {\n * console.log(`Retry ${attempt} after ${delay}ms: ${error.message}`);\n * }\n * }\n * );\n * ```\n *\n * @param fn - Async function to retry\n * @param options - Retry options\n * @returns Result of the function\n * @throws Last error if all retries fail\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n options: WithRetryOptions<T> = {}\n): Promise<T> {\n const {\n maxRetries = DEFAULT_RETRY_CONFIG.maxRetries,\n retryDelay = DEFAULT_RETRY_CONFIG.retryDelay,\n useExponentialBackoff = DEFAULT_RETRY_CONFIG.useExponentialBackoff,\n maxDelay = DEFAULT_RETRY_CONFIG.maxDelay,\n onRetry,\n getRetryAfter,\n circuitBreaker,\n } = options;\n\n // Check circuit breaker before first attempt\n if (circuitBreaker?.isOpen()) {\n throw new Error('Circuit breaker is open');\n }\n\n let lastError: Error | null = null;\n\n // maxRetries = 0 means no retries (just one attempt)\n // maxRetries = 2 means up to 3 total attempts (1 initial + 2 retries)\n const totalAttempts = maxRetries + 1;\n\n for (let attempt = 1; attempt <= totalAttempts; attempt++) {\n try {\n const result = await fn();\n\n // Success - record it and return\n circuitBreaker?.recordSuccess();\n return result;\n } catch (error) {\n lastError = error as Error;\n\n // Check if we should retry\n const isLastAttempt = attempt === totalAttempts;\n const canRetry = !isLastAttempt && isRetryableError(lastError);\n\n if (!canRetry) {\n break;\n }\n\n // Calculate delay (may be overridden by Retry-After header)\n let delay = calculateRetryDelay(attempt, { retryDelay, useExponentialBackoff, maxDelay });\n\n // Check for Retry-After header\n const retryAfter = getRetryAfter?.(lastError) ?? parseRetryAfter(lastError);\n if (retryAfter !== undefined && retryAfter > 0) {\n delay = Math.min(retryAfter, maxDelay);\n }\n\n // Call onRetry hook\n if (onRetry) {\n await onRetry(attempt, lastError, delay);\n }\n\n // Wait before next attempt\n await sleep(delay);\n }\n }\n\n // All attempts failed\n circuitBreaker?.recordFailure();\n throw lastError!;\n}\n\n/**\n * Helper to sleep for a given duration\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n"],"mappings":"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","names":["node","validStarters","schema","maxSize","node"]}
|
|
1
|
+
{"version":3,"sources":["../src/internal/validation-utils.ts","../src/security/url-validator.ts","../src/security/resource-limits.ts","../src/runtime/base64.ts","../src/mime-detection.ts","../src/internal/file-utils.ts","../src/pdf-utils.ts","../src/provider-config.ts","../src/provider-identity.ts","../src/provider-query.ts","../src/retry.ts"],"sourcesContent":["/**\n * Browser-safe validation utilities\n *\n * This module contains all validation code with ZERO Node.js dependencies.\n * It can be safely bundled for browser environments.\n */\n\n// Edge Runtime compatible - no AJV dependency\n\n// Re-export all types and constants from the validation section of index.ts\n// This file has NO fs imports and is completely browser-safe\n\n/** Page-centric IR */\nexport type BBox = { x: number; y: number; w: number; h: number };\nexport type IRLine = {\n text: string;\n bbox?: BBox;\n startChar?: number; // Character offset in full document text\n endChar?: number; // Character offset in full document text\n lineId?: string; // Unique line identifier (e.g., \"p1_l5\" for page 1, line 5)\n};\nexport type IRPage = {\n pageNumber?: number; // Explicit 1-indexed page number (for chunked documents)\n width: number;\n height: number;\n lines: IRLine[];\n markdown?: string; // Rich markdown preserving layout (tables, headers, lists)\n html?: string; // Rich HTML preserving layout (tables, headers, lists)\n extras?: Record<string, unknown>\n};\n\n/** Standard extras fields for DocumentIR */\nexport type DocumentIRExtras = {\n /** Total number of pages in the original document (for PDFs, DOCX, etc.) */\n pageCount?: number;\n /** Cost in USD for processing this document */\n costUSD?: number;\n /** Provider-specific raw response */\n raw?: unknown;\n /** For chunked documents: which chunk this is (0-indexed) */\n chunkIndex?: number;\n /** For chunked documents: total number of chunks */\n totalChunks?: number;\n /** For chunked documents: page range [startPage, endPage] (1-indexed, inclusive) */\n pageRange?: [number, number];\n /** For Unsiloed: total semantic chunks (not traditional pages) */\n totalSemanticChunks?: number;\n /** Allow arbitrary additional fields */\n [key: string]: unknown;\n};\n\nexport type DocumentIR = {\n pages: IRPage[];\n extras?: DocumentIRExtras;\n};\n\n/** Provider identity for 3-layer hierarchy (provider/model/method) */\nimport type { ProviderIdentity } from '../provider-identity.js';\n\n/** Provider capability contracts */\nexport type OCRProvider = {\n /** Full 3-layer identity (provider/model/method) */\n identity?: ProviderIdentity;\n /** Canonical name in \"provider:model\" format */\n name: string;\n parseToIR: (input: { url?: string; base64?: string }) => Promise<DocumentIR>;\n};\n\n/** Multimodal input for VLM providers */\nexport type MultimodalInput = {\n text?: string;\n images?: Array<{ url?: string; base64?: string; mimeType: string }>;\n pdfs?: Array<{ url?: string; base64?: string; fileId?: string }>;\n /** Optional system prompt (text-only, prepended to conversation) */\n systemPrompt?: string;\n};\n\n/** Effort level type for reasoning configuration */\nexport type ReasoningEffort = 'xhigh' | 'high' | 'medium' | 'low' | 'minimal' | 'none';\n\n/** Reasoning configuration (normalized across providers) */\nexport type ReasoningConfig = {\n /** Effort level - normalized across providers (xhigh: 95%, high: 80%, medium: 50%, low: 20%, minimal: 10%, none: 0%) */\n effort?: ReasoningEffort;\n /** Direct token budget - used by Anthropic/Google/Qwen models */\n max_tokens?: number;\n /** Exclude reasoning tokens from response (only use for accuracy, not visible) */\n exclude?: boolean;\n /** Enable reasoning with default (medium) effort. Set to false to explicitly disable. */\n enabled?: boolean;\n};\n\n/** Base LLM provider (text-only) */\nexport type LLMProvider = {\n /** Full 3-layer identity (provider/model/method) */\n identity?: ProviderIdentity;\n /** Canonical name in \"provider:model\" format */\n name: string;\n completeJson: (input: { prompt: string; schema: object; max_tokens?: number; reasoning?: ReasoningConfig }) =>\n Promise<{ json: unknown; rawText?: string; costUSD?: number; inputTokens?: number; outputTokens?: number; cacheCreationInputTokens?: number; cacheReadInputTokens?: number }>;\n};\n\n/** Text completion response (for non-JSON outputs like JSX/code) */\nexport type TextResponse = {\n text: string;\n rawText?: string;\n inputTokens?: number;\n outputTokens?: number;\n costUSD?: number;\n};\n\n/** Vision-capable LLM provider */\nexport type VLMProvider = {\n /** Full 3-layer identity (provider/model/method) */\n identity?: ProviderIdentity;\n /** Canonical name in \"provider:model\" format */\n name: string;\n completeJson: (input: { prompt: string | MultimodalInput; schema: object; max_tokens?: number; reasoning?: ReasoningConfig }) =>\n Promise<{ json: unknown; rawText?: string; costUSD?: number; inputTokens?: number; outputTokens?: number; cacheCreationInputTokens?: number; cacheReadInputTokens?: number }>;\n /**\n * Complete a text prompt without JSON mode (optional).\n * Use this when you need raw text output (JSX, code, markdown, etc.)\n */\n completeText?: (input: { input: MultimodalInput; max_tokens?: number; reasoning?: ReasoningConfig }) =>\n Promise<TextResponse>;\n capabilities: {\n supportsImages: true;\n supportsPDFs: boolean;\n maxPDFPages?: number;\n };\n};\n\n/** Legacy alias for backward compatibility */\nexport type LLMJsonProvider = VLMProvider;\n\n// ============================================================================\n// Processing Options - Normalized types for provider-agnostic configuration\n// ============================================================================\n\n/**\n * Processing quality/speed tradeoff modes\n * Providers map their specific modes to these normalized values\n */\nexport type ProcessingMode = 'fast' | 'balanced' | 'high_accuracy';\n\n/**\n * Page range specification for partial document processing\n * Allows processing a subset of pages for cost savings\n */\nexport type PageRangeOptions = {\n /** Process only the first N pages */\n maxPages?: number;\n /** Specific page range (0-indexed), e.g., \"0,2-4,10\" */\n pageRange?: string;\n};\n\n/**\n * Language hints for OCR processing\n */\nexport type LanguageOptions = {\n /** ISO language codes for OCR, e.g., ['en', 'de', 'fr'] */\n langs?: string[];\n};\n\n/**\n * Document segmentation result for splitting \"stapled\" PDFs\n * Returns page boundaries for each detected document type\n */\nexport type SegmentationResult = {\n segments: Array<{\n /** Document type name (e.g., 'invoice', 'contract') */\n name: string;\n /** Page indices (0-indexed) belonging to this segment */\n pages: number[];\n /** Confidence level of segmentation */\n confidence: 'high' | 'medium' | 'low';\n }>;\n metadata: {\n /** Total pages in the original document */\n totalPages: number;\n /** How segmentation was performed */\n segmentationMethod: 'auto' | 'schema' | 'manual';\n };\n};\n\n/**\n * Extracted image from a document\n * Represents figures, charts, or embedded images\n */\nexport type ExtractedImage = {\n /** Block ID or reference (provider-specific) */\n id: string;\n /** Page number where image appears (0-indexed) */\n pageNumber: number;\n /** Base64-encoded image data */\n base64: string;\n /** MIME type of the image */\n mimeType: string;\n /** Location on page (normalized 0-1 coordinates) */\n bbox?: NormalizedBBox;\n /** Caption text if detected */\n caption?: string;\n};\n\n/**\n * Extended OCR provider options (beyond basic parseToIR)\n * These options are normalized across different OCR providers\n */\nexport type OCRProviderOptions = PageRangeOptions & LanguageOptions & {\n /** Processing quality/speed tradeoff */\n mode?: ProcessingMode;\n /** Force OCR even on text-based PDFs */\n forceOCR?: boolean;\n /** Extract embedded images from document */\n extractImages?: boolean;\n /** Add page delimiters to output */\n paginate?: boolean;\n /** Remove and redo existing OCR */\n stripExistingOCR?: boolean;\n};\n\n/**\n * Output format options for LLM-based text fields\n * Controls how text content is formatted in the response\n */\nexport type OutputFormat = 'markdown' | 'html' | 'json' | 'text';\n\n/**\n * Table format options for tabular data in responses\n */\nexport type TableFormat = 'markdown' | 'html' | 'csv';\n\n/**\n * Chunking strategy options for document segmentation\n */\nexport type ChunkingStrategy = 'page' | 'section' | 'paragraph' | 'semantic';\n\n/**\n * LLM-derived feature options\n * These features are implemented via prompting rather than native API support\n */\nexport type LLMDerivedOptions = {\n /** Format for text output in string fields */\n outputFormat?: OutputFormat;\n /** Format for tables within text fields */\n tableFormat?: TableFormat;\n /** Add page break markers (---) between pages */\n pageMarkers?: boolean;\n /** Include per-field confidence scores (attached to result, not in JSON) */\n includeConfidence?: boolean;\n /** Include source citations with bounding boxes (attached to result, not in JSON) */\n includeSources?: boolean;\n /** Include block type classification for each extracted element */\n includeBlockTypes?: boolean;\n /** Extract document headers (repeated content at top of pages) */\n extractHeaders?: boolean;\n /** Extract document footers (repeated content at bottom of pages) */\n extractFooters?: boolean;\n /** Document chunking strategy */\n chunkingStrategy?: ChunkingStrategy;\n /** Maximum chunk size in characters (when using chunking) */\n maxChunkSize?: number;\n /** Language hints for the document (e.g., ['English', 'German']) */\n languageHints?: string[];\n};\n\n/**\n * Extended VLM provider options for document extraction\n * These options are normalized across different VLM providers\n */\nexport type VLMProviderOptions = PageRangeOptions & LanguageOptions & LLMDerivedOptions & {\n /** Processing quality/speed tradeoff */\n mode?: ProcessingMode;\n /** Force OCR even on text-based PDFs */\n forceOCR?: boolean;\n /** Additional prompt/instructions for extraction */\n prompt?: string;\n /** Schema for auto-segmentation of multi-document PDFs */\n segmentationSchema?: object;\n};\n\n/**\n * Provider citation from source document\n * Maps extracted fields to their source locations\n */\nexport type ProviderCitation = {\n /** JSON path to extracted field (e.g., \"invoice.total\") */\n fieldPath: string;\n /** Source block IDs from the provider */\n blockIds: string[];\n /** Confidence score (0-1) */\n confidence?: number;\n};\n\n/** Consensus configuration for any node */\nexport type ConsensusConfig = {\n runs: number; // Number of times to run\n strategy?: 'majority' | 'unanimous'; // Default: majority\n onTie?: 'random' | 'fail' | 'retry'; // Default: random\n parallel?: boolean; // Run consensus in parallel (default: true)\n includeMetadata?: boolean; // Include detailed consensus metadata (default: false)\n level?: 'object' | 'field'; // Voting level: object (default) or per-field\n retryOnFailure?: boolean; // Retry failed/empty runs (default: false)\n maxRetries?: number; // Max retries per run (default: 1)\n};\n\n/** Individual consensus run result */\nexport type ConsensusRunResult<T = any> = {\n runIndex: number;\n value: T | null;\n success: boolean;\n error?: string;\n startTime: number;\n endTime: number;\n duration: number;\n attempts?: number; // Number of attempts (1 = no retry, >1 = retried)\n};\n\n/** Field-level voting details */\nexport type FieldVotingDetails = {\n fieldPath: string;\n values: Array<{\n /** The actual value for this voting option - can be any JSON-serializable type */\n value: unknown;\n count: number;\n percentage: number;\n runIndices: number[];\n }>;\n /** The winning value from consensus - can be any JSON-serializable type */\n winner: unknown;\n isTie: boolean;\n agreementScore: number; // 0.0 to 1.0\n};\n\n/** Consensus execution metadata */\nexport type ConsensusMetadata<T = unknown> = {\n totalRuns: number;\n successfulRuns: number;\n failedRuns: number;\n strategy: 'majority' | 'unanimous';\n selectedResult: T;\n selectedRunIndex: number;\n confidence: 'high' | 'medium' | 'low';\n overallAgreement: number; // 0.0 to 1.0\n fieldAgreement: Record<string, number>; // Field path -> agreement score\n votingDetails: FieldVotingDetails[];\n runs: ConsensusRunResult<T>[];\n executionTime: number;\n wasRetry: boolean;\n tieBreakerUsed?: 'random' | 'retry' | 'fail' | null;\n // New fields for enhanced consensus features\n votingLevel?: 'object' | 'field';\n isSyntheticResult?: boolean; // true if field-level voting composed a new object\n totalRetries?: number; // Total retry attempts across all runs\n emptyResultsFiltered?: number; // Number of empty results filtered out\n};\n\n/** Output with consensus metadata wrapper */\nexport type OutputWithConsensus<T = unknown> = {\n data: T;\n consensus: ConsensusMetadata<T>;\n};\n\n/** Conditional type helper for consensus metadata */\nexport type MaybeWithConsensusMetadata<T, Config> = Config extends { includeMetadata: true }\n ? OutputWithConsensus<T>\n : T;\n\n/** Flow input/output types */\nexport type FlowInput = {\n url?: string;\n base64?: string;\n pages?: number[]; // For post-split runs\n bounds?: BBox; // For post-split runs\n};\n\n/**\n * All MIME types supported by at least one provider.\n * This is the union of all provider capabilities.\n */\nexport type SupportedMimeType =\n // PDF\n | 'application/pdf'\n // Images - common\n | 'image/jpeg'\n | 'image/png'\n | 'image/gif'\n | 'image/webp'\n // Images - additional\n | 'image/tiff'\n | 'image/bmp'\n | 'image/heic'\n | 'image/heif'\n | 'image/vnd.adobe.photoshop' // PSD\n // Microsoft Office\n | 'application/msword' // DOC\n | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' // DOCX\n | 'application/vnd.ms-excel' // XLS\n | 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' // XLSX\n | 'application/vnd.ms-powerpoint' // PPT\n | 'application/vnd.openxmlformats-officedocument.presentationml.presentation' // PPTX\n // OpenDocument formats (Datalab)\n | 'application/vnd.oasis.opendocument.text' // ODT\n | 'application/vnd.oasis.opendocument.spreadsheet' // ODS\n | 'application/vnd.oasis.opendocument.presentation' // ODP\n // Text formats\n | 'text/plain' // TXT\n | 'text/csv' // CSV\n | 'text/html' // HTML\n | 'application/rtf' // RTF\n // Other\n | 'application/epub+zip'; // EPUB\n\n/**\n * Flow-level input validation configuration\n *\n * Allows specifying accepted MIME types for early validation\n * before flow execution begins.\n */\nexport type FlowInputValidation = {\n /**\n * List of accepted MIME types.\n * If specified, input must match one of these types or validation fails.\n * If empty/undefined, all supported types are accepted.\n */\n acceptedFormats?: SupportedMimeType[];\n /**\n * Whether to throw on validation failure.\n * @default true\n */\n throwOnInvalid?: boolean;\n};\n\nexport type FlowResult<T = any> = {\n output: T;\n metrics: StepMetric[];\n aggregated: AggregatedMetrics;\n artifacts: Record<string, any>;\n error?: Error;\n};\n\nexport type SplitDocument = {\n type: string; // 'invoice', 'bunker', 'other'\n schema?: object; // Matched schema (optional - only present when schemas provided)\n pages: number[]; // Page numbers\n bounds?: BBox; // Bounding box\n input: FlowInput; // Original input for re-processing\n};\n\n/** Citation and source tracking types */\n\n/** Citation source type indicating data provenance */\nexport type CitationSourceType = 'ocr' | 'vlm' | 'llm' | 'inferred';\n\n/** Normalized bounding box (0-1 coordinates relative to page dimensions) */\nexport type NormalizedBBox = {\n x: number; // Left edge (0-1)\n y: number; // Top edge (0-1)\n w: number; // Width (0-1)\n h: number; // Height (0-1)\n};\n\n/** Line-level citation reference with spatial information */\nexport type LineCitation = {\n pageNumber: number; // 1-indexed page number\n lineIndex: number; // 0-indexed line position on page\n bbox?: NormalizedBBox; // Normalized bounding box (0-1 coordinates)\n text: string; // Text snippet for verification\n confidence?: number; // 0-1 confidence score\n sourceType: CitationSourceType;\n startChar?: number; // Character offset in full document\n endChar?: number; // Character offset in full document\n};\n\n/** Field-level citation mapping extracted values to sources */\nexport type FieldCitation = {\n fieldPath: string; // JSON path to field (e.g., \"invoice.lineItems[0].amount\")\n /** Extracted value - can be any JSON-serializable type */\n value: unknown;\n citations: LineCitation[]; // Source lines supporting this value\n reasoning?: string; // LLM explanation for inferred values\n confidence?: number; // Overall confidence (0-1)\n};\n\n/** Citation configuration for nodes */\nexport type CitationConfig = {\n enabled: boolean; // Enable citation tracking (default: false)\n includeTextSnippets?: boolean; // Include text snippets in citations (default: true)\n includeBoundingBoxes?: boolean; // Include bboxes when available (default: true)\n includeConfidence?: boolean; // Include confidence scores (default: true)\n minConfidence?: number; // Minimum confidence threshold (0-1, default: 0.0)\n detectInferred?: boolean; // Use LLM to detect inferred values (default: false)\n};\n\n/** Extended output with citations */\nexport type OutputWithCitations<T> = {\n data: T; // Extracted data\n citations: FieldCitation[]; // Field-level citations\n metadata: {\n totalPages?: number; // Total pages processed\n sourceType: CitationSourceType; // Primary source type\n hasInferredValues?: boolean; // Whether any values were inferred\n processingTime?: number; // Processing time in ms\n };\n};\n\n/** Node configuration types */\nexport type ParseNodeConfig = {\n provider: OCRProvider | VLMProvider;\n consensus?: ConsensusConfig;\n chunked?: {\n maxPagesPerChunk: number;\n overlap?: number; // Default: 0\n parallel?: boolean; // Default: true - process chunks in parallel for speed\n };\n format?: 'text' | 'markdown' | 'html'; // Output format: text (default, line-level citations), markdown/html (page-level citations, preserves structure)\n describeFigures?: boolean; // When true, VLM providers describe charts/figures/diagrams in text. Default: false\n includeImages?: boolean; // When true, providers extract images (figures/tables/charts) from documents. Supported by Surya/Marker. Default: false\n additionalPrompt?: string; // Custom OCR guidance or instructions\n citations?: CitationConfig; // Citation tracking config\n\n // NEW: Prompt asset support\n promptRef?: string; // Reference to prompt asset (e.g., \"default-parse@1.0.0\")\n /**\n * Optional custom variables for prompt rendering (e.g., language, strictMode, tenantId).\n *\n * Auto-injected variables (no need to pass manually):\n * - format: From config.format\n * - schema: Constructed schema (if applicable)\n * - describeFigures: From config.describeFigures\n * - citationsEnabled: From config.citations?.enabled\n *\n * Use promptVariables only for runtime context (localization, multi-tenancy, behavioral flags).\n */\n promptVariables?: Record<string, any>;\n\n /**\n * Additional instructions to append to the default prompt.\n * This provides a simple way to customize the prompt without creating a custom prompt asset.\n * The instructions will be added after the main prompt content.\n *\n * @example\n * ```typescript\n * parse({\n * provider: vlmProvider,\n * format: 'markdown',\n * additionalInstructions: \"Pay special attention to preserving table structures and footnotes.\"\n * })\n * ```\n */\n additionalInstructions?: string;\n\n /**\n * When using promptRef, automatically inject format instruction if {{format}} placeholder is not present.\n * This ensures the UI format selection always takes effect.\n * Default: true\n *\n * @example\n * ```typescript\n * parse({\n * provider: vlmProvider,\n * format: 'markdown',\n * promptRef: 'my-custom-prompt',\n * autoInjectFormat: false // Disable auto-injection\n * })\n * ```\n */\n autoInjectFormat?: boolean;\n\n /**\n * Enable extended reasoning/thinking for VLM providers that support it.\n * Only applies when using a VLM provider (not OCR).\n *\n * @example\n * ```typescript\n * parse({\n * provider: vlmProvider,\n * format: 'markdown',\n * reasoning: { enabled: true, effort: 'medium' }\n * })\n * ```\n */\n reasoning?: {\n effort?: 'xhigh' | 'high' | 'medium' | 'low' | 'minimal' | 'none';\n max_tokens?: number;\n exclude?: boolean;\n enabled?: boolean;\n };\n\n /**\n * Maximum tokens for the LLM response.\n * If not specified, the provider's default will be used (typically 4096).\n */\n maxTokens?: number;\n};\n\nexport type SplitNodeConfig = {\n provider: VLMProvider;\n\n /**\n * Simple category definitions (recommended).\n * Each category can be a string or an object with name and optional description.\n *\n * @example\n * ```typescript\n * split({\n * provider: vlmProvider,\n * categories: [\n * 'invoice',\n * { name: 'cover_letter', description: 'Cover letter or transmittal pages' },\n * { name: 'contract', description: 'Legal agreements with terms and signatures' }\n * ]\n * })\n * ```\n */\n categories?: (string | { name: string; description?: string })[];\n\n /**\n * @deprecated Use `categories` instead. Full schema definitions for backwards compatibility.\n * Schema names are used as category names, but schemas are no longer attached to output.\n */\n schemas?: Record<string, object>; // { invoice: Schema, bunker: Schema }\n\n includeOther?: boolean; // Default: true\n consensus?: ConsensusConfig;\n schemaRef?: string; // Reference to schema asset (e.g., \"document-split@2.0.0\")\n\n /**\n * Enable extended reasoning/thinking for providers that support it.\n *\n * @example\n * ```typescript\n * split({\n * provider: vlmProvider,\n * categories: ['invoice', 'receipt', 'contract'],\n * reasoning: { enabled: true, effort: 'high' }\n * })\n * ```\n */\n reasoning?: {\n effort?: 'xhigh' | 'high' | 'medium' | 'low' | 'minimal' | 'none';\n max_tokens?: number;\n exclude?: boolean;\n enabled?: boolean;\n };\n\n /**\n * Maximum tokens for the LLM response.\n * If not specified, the provider's default will be used (typically 4096).\n */\n maxTokens?: number;\n};\n\nexport type CategorizeNodeConfig = {\n provider: LLMProvider | VLMProvider;\n categories: (string | { name: string; description?: string })[];\n consensus?: ConsensusConfig;\n additionalPrompt?: string; // Custom categorization instructions\n\n // NEW: Prompt asset support\n promptRef?: string; // Reference to prompt asset (e.g., \"default-categorize@1.0.0\")\n /**\n * Optional custom variables for prompt rendering (e.g., language, strictMode, tenantId).\n *\n * Auto-injected variables (no need to pass manually):\n * - categories: From config.categories\n * - documentText: Computed from DocumentIR input\n *\n * Use promptVariables only for runtime context (localization, multi-tenancy, behavioral flags).\n */\n promptVariables?: Record<string, any>;\n\n /**\n * Additional instructions to append to the default prompt.\n * This provides a simple way to customize the prompt without creating a custom prompt asset.\n * The instructions will be added after the main prompt content.\n *\n * @example\n * ```typescript\n * categorize({\n * provider: llmProvider,\n * categories: ['invoice', 'receipt', 'contract'],\n * additionalInstructions: \"Consider the document's header and footer when categorizing.\"\n * })\n * ```\n */\n additionalInstructions?: string;\n\n /**\n * Enable extended reasoning/thinking for providers that support it.\n *\n * @example\n * ```typescript\n * categorize({\n * provider: vlmProvider,\n * categories: ['invoice', 'receipt', 'contract'],\n * reasoning: { enabled: true, effort: 'low' }\n * })\n * ```\n */\n reasoning?: {\n effort?: 'xhigh' | 'high' | 'medium' | 'low' | 'minimal' | 'none';\n max_tokens?: number;\n exclude?: boolean;\n enabled?: boolean;\n };\n\n /**\n * Maximum tokens for the LLM response.\n * If not specified, the provider's default will be used (typically 4096).\n */\n maxTokens?: number;\n};\n\n/**\n * Controls what inputs the extract node ingests.\n * - 'auto': Automatically detect input type and route appropriately (default)\n * - 'ir': Only DocumentIR from previous step (text-only extraction)\n * - 'ir+source': Both DocumentIR AND source document (multimodal with parsed text)\n * - 'source': Only raw source document (direct VLM extraction, no parsed text)\n *\n * Auto mode logic:\n * - If DocumentIR available AND source available AND VLM provider -> 'ir+source'\n * - If only DocumentIR available -> 'ir'\n * - If only FlowInput available AND VLM provider -> 'source'\n */\nexport type ExtractInputMode = 'auto' | 'ir' | 'ir+source' | 'source';\n\nexport type ExtractNodeConfig<T = any> = {\n provider: LLMProvider | VLMProvider;\n schema: object | EnhancedExtractionSchema<T> | { ref: string }; // Accept plain, enhanced, or reference\n consensus?: ConsensusConfig;\n reasoning?: {\n effort?: 'xhigh' | 'high' | 'medium' | 'low' | 'minimal' | 'none';\n max_tokens?: number;\n exclude?: boolean;\n enabled?: boolean;\n };\n additionalPrompt?: string; // Custom extraction instructions (appended after schema)\n citations?: CitationConfig; // Citation tracking config\n\n // NEW: Prompt asset support\n promptRef?: string; // Reference to prompt asset (e.g., \"default-extraction@1.0.0\")\n /**\n * Optional custom variables for prompt rendering (e.g., language, strictMode, tenantId).\n *\n * Auto-injected variables (no need to pass manually):\n * - schema: From config.schema\n * - documentText: Computed from DocumentIR or FlowInput\n * - schemaTitle: From schema.title or default \"the provided schema\"\n * - schemaDescription: From schema.description or empty string\n * - structuredFormat: Generated formatting instructions (for markdown/html)\n *\n * Use promptVariables only for runtime context (localization, multi-tenancy, behavioral flags).\n */\n promptVariables?: Record<string, any>;\n\n /**\n * Additional instructions to append to the default prompt.\n * This provides a simple way to customize the prompt without creating a custom prompt asset.\n * The instructions will be added after the main prompt content.\n *\n * @example\n * ```typescript\n * extract({\n * provider: llmProvider,\n * schema: mySchema,\n * additionalInstructions: \"Be strict with date formats. Use YYYY-MM-DD format only.\"\n * })\n * ```\n */\n additionalInstructions?: string;\n\n /**\n * Controls what inputs the extract node ingests.\n * - 'auto': Automatically detect input type and route appropriately (default)\n * - 'ir': Only DocumentIR from previous step (text-only extraction)\n * - 'ir+source': Both DocumentIR AND source document (multimodal with parsed text)\n * - 'source': Only raw source document (direct VLM extraction, no parsed text)\n * @default 'auto'\n */\n inputMode?: ExtractInputMode;\n\n /**\n * In split/forEach contexts, use the original unsplit document instead of the segment.\n * Only applies when inputMode includes source ('ir+source' or 'source').\n * @default false (uses split segment source)\n */\n useOriginalSource?: boolean;\n\n /**\n * When auto mode has both IR and source available with VLM provider:\n * - true: use 'ir+source' for maximum context (hybrid multimodal)\n * - false: use 'ir' for text-only extraction (lower cost)\n * Only applies when inputMode='auto'.\n * @default true\n */\n preferVisual?: boolean;\n\n /**\n * Maximum tokens for the LLM response.\n * If not specified, the provider's default will be used (typically 4096).\n */\n maxTokens?: number;\n};\n\n/** Chunk output structure */\nexport type ChunkMetadata = {\n // Core content\n content: string;\n id: string; // Unique chunk identifier\n\n // Position metadata\n index: number; // Chunk position in sequence\n startChar: number;\n endChar: number;\n\n // Document context\n pageNumbers: number[]; // Pages this chunk spans\n section?: string; // Section/chapter title\n headers?: string[]; // Hierarchy of headers above this chunk\n\n // Chunking metadata\n strategy: string; // Which strategy created this chunk\n tokenCount?: number; // For LLM context planning\n wordCount: number;\n charCount: number;\n};\n\nexport type ChunkOutput = {\n chunks: ChunkMetadata[];\n totalChunks: number;\n averageChunkSize: number;\n sourceMetadata?: {\n providerType?: string; // 'ocr' | 'vlm' - original provider type\n };\n sourceDocument?: DocumentIR; // Original DocumentIR for citation mapping\n};\n\nexport type ChunkNodeConfig = {\n strategy: 'recursive' | 'section' | 'page' | 'fixed';\n maxSize?: number; // Max characters per chunk (recursive, section)\n minSize?: number; // Min characters per chunk (default: 100)\n overlap?: number; // Character overlap between chunks (default: 0)\n separators?: string[]; // Hierarchical separators (recursive)\n pagesPerChunk?: number; // Pages per chunk (page strategy)\n combineShortPages?: boolean; // Combine short pages (page strategy)\n minPageContent?: number; // Min content length to keep page (page strategy)\n size?: number; // Fixed size for fixed strategy\n unit?: 'tokens' | 'characters'; // Unit for fixed strategy\n};\n\nexport type CombineNodeConfig = {\n strategy: 'merge' | 'concatenate' | 'first' | 'last';\n};\n\nexport type OutputNodeConfig = {\n source?: string | string[];\n transform?: 'first' | 'last' | 'merge' | 'pick' | 'custom';\n fields?: string[];\n name?: string;\n /**\n * Custom transform function for 'custom' transform mode.\n * @param inputs - The input value(s) from the source step(s)\n * @param artifacts - All artifacts from the flow execution\n * @returns The transformed output value\n */\n customTransform?: (inputs: unknown | unknown[], artifacts: Record<string, unknown>) => unknown;\n};\n\n/** Enhanced extraction schema with examples and guidance */\nexport type EnhancedExtractionSchema<T = unknown> = {\n // Core schema (JSON Schema or Zod schema)\n schema: object;\n\n // Optional extraction enhancements\n examples?: Array<{\n description: string; // Description of this example\n input: string; // Sample input text\n output: T; // Expected output matching schema\n }>;\n\n extractionRules?: string; // Extraction guidelines (e.g., \"Focus on tables in appendix\")\n contextPrompt?: string; // Document context (e.g., \"This is a legal document\")\n hints?: string[]; // Additional hints for the extractor\n};\n\n/** Node & runner */\nexport type StepMetric = {\n step: string;\n configStepId?: string; // Flow-level step ID for config lookups (schemaRef, promptRef)\n startMs: number; // Absolute timestamp when step started (Date.now())\n provider?: string;\n model?: string;\n ms: number; // Total duration; for wrappers with rollup=true, includes child work\n costUSD?: number;\n inputTokens?: number;\n outputTokens?: number;\n cacheCreationInputTokens?: number;\n cacheReadInputTokens?: number;\n attemptNumber?: number; // Retry attempt number (1 = first attempt, 2+ = retries)\n metadata?: {\n kind?: 'leaf' | 'wrapper' | 'prep'; // 'leaf' = actual LLM call, 'wrapper' = composite overhead, 'prep' = preparation step\n rollup?: boolean; // True if ms includes child work (for wrappers with children)\n overheadMs?: number; // Pure overhead time excluding child work (for wrappers with children)\n /** Additional metadata fields */\n [key: string]: string | number | boolean | undefined;\n };\n};\n\n/** Aggregated metrics for multi-step flows */\nexport interface AggregatedMetrics {\n totalDurationMs: number;\n totalCostUSD: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n totalCacheCreationTokens: number;\n totalCacheReadTokens: number;\n stepCount: number;\n byProvider: Record<string, {\n costUSD: number;\n inputTokens: number;\n outputTokens: number;\n callCount: number;\n }>;\n}\n\n/**\n * Aggregate metrics from multiple steps\n * @param metrics - Array of step metrics\n * @returns Aggregated totals and per-provider breakdowns\n */\nexport function aggregateMetrics(metrics: StepMetric[]): AggregatedMetrics {\n const byProvider: Record<string, {\n costUSD: number;\n inputTokens: number;\n outputTokens: number;\n callCount: number;\n }> = {};\n\n const result = metrics.reduce((acc, m) => {\n acc.totalDurationMs += m.ms;\n acc.totalCostUSD += m.costUSD || 0;\n acc.totalInputTokens += m.inputTokens || 0;\n acc.totalOutputTokens += m.outputTokens || 0;\n acc.totalCacheCreationTokens += m.cacheCreationInputTokens || 0;\n acc.totalCacheReadTokens += m.cacheReadInputTokens || 0;\n\n // Group by provider\n if (m.provider) {\n if (!byProvider[m.provider]) {\n byProvider[m.provider] = { costUSD: 0, inputTokens: 0, outputTokens: 0, callCount: 0 };\n }\n byProvider[m.provider].costUSD += m.costUSD || 0;\n byProvider[m.provider].inputTokens += m.inputTokens || 0;\n byProvider[m.provider].outputTokens += m.outputTokens || 0;\n byProvider[m.provider].callCount += 1;\n }\n\n return acc;\n }, {\n totalDurationMs: 0,\n totalCostUSD: 0,\n totalInputTokens: 0,\n totalOutputTokens: 0,\n totalCacheCreationTokens: 0,\n totalCacheReadTokens: 0,\n stepCount: metrics.length,\n byProvider\n });\n\n return result;\n}\n\n/**\n * Execution context passed to conditional functions and trigger nodes\n * Provides access to artifacts and metrics from all previous steps\n */\nexport interface FlowContext {\n /** Outputs from all completed steps, indexed by step ID */\n artifacts: Record<string, any>;\n /** Performance metrics from all completed steps */\n metrics: StepMetric[];\n /** Call stack for tracking nested flow execution (for circular dependency detection) */\n callStack?: string[];\n /** Maximum nesting depth for flow triggers (default: 10) */\n maxDepth?: number;\n}\n\n/**\n * W3C Trace Context for distributed tracing.\n * Compatible with observability module's TraceContext.\n */\nexport interface TraceContextLite {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n traceFlags: number; // W3C trace flags (0x01 = sampled), required for compatibility\n traceState?: string;\n}\n\n/**\n * Observability context passed to node executions.\n * Uses 'any' for config and traceContext to avoid circular imports and\n * maintain compatibility with the full observability types.\n */\nexport type NodeObservabilityContext = {\n /** Observability configuration - full type in observability module */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n config?: any;\n flowId?: string;\n executionId?: string;\n stepId?: string;\n stepIndex?: number;\n /** W3C Trace Context - compatible with TraceContext from observability module */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n traceContext?: any;\n metadata?: Record<string, unknown>;\n};\n\nexport type NodeCtx = {\n stepId?: string; // Flow-level step ID for metrics tracking\n artifacts: Record<string, unknown>;\n emit: (key: string, value: unknown) => void;\n metrics: { push: (m: StepMetric) => void };\n /** Observability context for hooks (optional) */\n observability?: NodeObservabilityContext;\n};\n\n/** Node type metadata for runtime validation */\nexport type NodeTypeInfo = {\n /** Input types this node accepts (e.g., ['FlowInput', 'DocumentIR']) */\n inputTypes: string[];\n /**\n * Output type this node produces - can be string or function for config-dependent types.\n * When a function, it receives the node's specific config and returns the output type string.\n * Uses 'any' parameter to allow nodes to use their specific config types.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n outputType: string | ((config: any) => string);\n /** Provider types this node requires (if any) */\n requiresProvider?: ('OCR' | 'VLM' | 'LLM')[];\n /** Whether this node can accept array input */\n acceptsArray?: boolean;\n /**\n * Whether this node always outputs an array (or function for config-dependent).\n * Uses 'any' parameter to allow nodes to use their specific config types.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n outputsArray?: boolean | ((config: any) => boolean);\n /** Human-readable description of what this node does */\n description?: string;\n};\n\nexport type NodeDef<I, O> = {\n key: string;\n run: (input: I, ctx: NodeCtx) => Promise<O>;\n /** Optional type metadata for validation */\n __meta?: NodeTypeInfo;\n};\n\nexport const node = <I, O>(key: string, run: NodeDef<I, O>[\"run\"]): NodeDef<I, O> => ({ key, run });\n\nexport async function runPipeline(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n steps: NodeDef<any, any>[],\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n input: any,\n observabilityContext?: NodeObservabilityContext,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n flowArtifacts?: Record<string, any>\n) {\n // Merge flow artifacts with local (flow artifacts as read-only base for source access)\n const artifacts: Record<string, unknown> = flowArtifacts ? { ...flowArtifacts } : {};\n const metrics: StepMetric[] = [];\n const ctx: NodeCtx = {\n stepId: observabilityContext?.stepId,\n artifacts,\n emit: (k, v) => { artifacts[k] = v; },\n metrics: { push: (m) => metrics.push(m) },\n observability: observabilityContext\n };\n let acc = input;\n for (const s of steps) {\n acc = await s.run(acc, ctx);\n ctx.emit(s.key, acc);\n }\n return { output: acc, artifacts, metrics };\n}\n\n/**\n * Flow execution error with step context\n *\n * Thrown when a flow step fails during execution. Includes:\n * - Which step failed (ID, index, type)\n * - Which steps completed successfully\n * - Partial artifacts from completed steps (for debugging)\n * - The original error that caused the failure\n *\n * This makes debugging flow failures much easier by showing exactly where the error occurred\n * and what data was produced before the failure.\n *\n * @example\n * ```typescript\n * try {\n * await flow.run(input);\n * } catch (error) {\n * if (error instanceof FlowExecutionError) {\n * console.error(`Failed at step ${error.failedStepIndex}: ${error.failedStepType}`);\n * console.error(`Step ID: ${error.failedStep}`);\n * console.error(`Completed: ${error.completedSteps.join(', ')}`);\n * console.error(`Original error: ${error.originalError.message}`);\n *\n * // Access partial results from completed steps\n * if (error.partialArtifacts?.qualify) {\n * console.log('Quality assessment completed:', error.partialArtifacts.qualify);\n * }\n * }\n * }\n * ```\n */\n\n/**\n * Extracts a human-readable error message from potentially JSON error responses.\n *\n * Handles common API error formats:\n * - { \"detail\": \"...\" } (Surya-style)\n * - { \"error\": { \"message\": \"...\" } } (OpenAI, Anthropic)\n * - { \"error\": \"...\" } (Simple format)\n * - { \"message\": \"...\" } (Direct format)\n * - Plain text (returned as-is)\n *\n * @param errorText - The error text which may contain JSON\n * @returns A human-readable error message\n */\nexport function extractErrorMessage(errorText: string): string {\n // If it's short or doesn't look like JSON, return as-is\n if (errorText.length < 10 || !errorText.trim().startsWith('{')) {\n return errorText;\n }\n\n try {\n const parsed = JSON.parse(errorText);\n\n // Surya-style: { \"detail\": \"...\" }\n if (parsed.detail) {\n return parsed.detail;\n }\n\n // OpenAI/Anthropic style: { error: { message: \"...\" } }\n if (parsed.error?.message) {\n return parsed.error.message;\n }\n\n // Simple style: { error: \"...\" }\n if (typeof parsed.error === 'string') {\n return parsed.error;\n }\n\n // Direct style: { message: \"...\" }\n if (parsed.message) {\n return parsed.message;\n }\n\n // Google style: { error: { status: \"...\", message: \"...\" } }\n if (parsed.error?.status && parsed.error?.message) {\n return `${parsed.error.status}: ${parsed.error.message}`;\n }\n\n // Fallback: return original but truncated if very long\n return errorText.length > 200\n ? errorText.substring(0, 200) + '...'\n : errorText;\n } catch {\n // Not valid JSON, return as-is (truncated if needed)\n return errorText.length > 500\n ? errorText.substring(0, 500) + '...'\n : errorText;\n }\n}\n\n/**\n * Represents a step location in a flow hierarchy.\n * Used to track the execution path through nested flows.\n */\nexport interface FlowStepLocation {\n /** Step ID */\n stepId: string;\n /** Step index within this flow (0-based) */\n stepIndex: number;\n /** Step type (e.g., 'parse', 'conditional', 'forEach') */\n stepType: string;\n /** Branch name if within a conditional (e.g., \"Invoice\", \"Receipt\") */\n branch?: string;\n /** Item index if within a forEach iteration */\n itemIndex?: number;\n}\n\nexport class FlowExecutionError extends Error {\n constructor(\n message: string,\n /** The ID of the step that failed (e.g., 'parse_node123') */\n public readonly failedStep: string,\n /** The index of the failed step in the flow (0-based) */\n public readonly failedStepIndex: number,\n /** The type of the failed step (e.g., 'parse', 'extract', 'step', 'conditional', 'forEach') */\n public readonly failedStepType: string,\n /** Array of step IDs that completed successfully before the failure */\n public readonly completedSteps: string[],\n /** The original error that caused the failure */\n public readonly originalError: Error,\n /** Partial artifacts from steps that completed before the failure */\n public readonly partialArtifacts?: Record<string, any>,\n /** Execution path through nested flows (for hierarchical context) */\n public readonly flowPath?: FlowStepLocation[],\n /** All completed steps aggregated across flow boundaries */\n public readonly allCompletedSteps?: string[]\n ) {\n super(message);\n this.name = 'FlowExecutionError';\n\n // Maintain proper stack trace for V8 engines\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, FlowExecutionError);\n }\n }\n\n /**\n * Returns a formatted string showing the execution path.\n * Example: \"parse → conditional:Invoice → extract\"\n */\n getFormattedPath(): string {\n if (!this.flowPath || this.flowPath.length === 0) {\n return this.failedStep;\n }\n\n return this.flowPath.map(loc => {\n let label = loc.stepId;\n if (loc.branch) {\n label += `:${loc.branch}`;\n }\n if (loc.itemIndex !== undefined) {\n label += `[${loc.itemIndex}]`;\n }\n return label;\n }).join(' → ');\n }\n\n /**\n * Returns the root cause error (innermost originalError).\n * Useful when errors are nested multiple levels deep.\n */\n getRootCause(): Error {\n let cause: Error = this.originalError;\n while (cause instanceof FlowExecutionError && cause.originalError) {\n cause = cause.originalError;\n }\n return cause;\n }\n}\n\n/**\n * Flow validation error for invalid node connections\n *\n * Thrown when building a flow with incompatible node connections.\n * Provides helpful error messages and suggestions for fixing the issue.\n *\n * @example\n * ```typescript\n * try {\n * const flow = createFlow()\n * .step('parse', parse({ provider: ocrProvider }))\n * .step('combine', combine()) // Invalid: combine needs array input\n * .build();\n * } catch (error) {\n * if (error instanceof FlowValidationError) {\n * console.error(error.message);\n * console.error('Reason:', error.reason);\n * console.log('Suggestions:', error.suggestions?.join('\\n'));\n * }\n * }\n * ```\n */\nexport class FlowValidationError extends Error {\n constructor(\n message: string,\n public readonly reason?: string,\n public readonly suggestions?: string[],\n public readonly sourceNode?: string,\n public readonly targetNode?: string,\n public readonly sourceOutputType?: string,\n public readonly targetInputTypes?: string[]\n ) {\n super(message);\n this.name = 'FlowValidationError';\n\n // Maintain proper stack trace for V8 engines\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, FlowValidationError);\n }\n }\n}\n\n/** Node type names for validation */\nexport type NodeTypeName = 'parse' | 'split' | 'categorize' | 'extract' | 'chunk' | 'combine' | 'trigger' | 'output';\n\n/** Compatibility rule for node connections */\nexport type CompatibilityRule = {\n valid: boolean;\n requiresForEach?: boolean;\n /** Indicates this connection cannot be fully validated at build-time and requires runtime type checking */\n requiresRuntimeValidation?: boolean;\n reason?: string;\n note?: string;\n};\n\n/**\n * Node Compatibility Matrix\n *\n * Defines which nodes can connect to which other nodes.\n * This is the single source of truth for node connection validation.\n *\n * Rules based on input/output type compatibility:\n * - parse: FlowInput → DocumentIR (or DocumentIR[] if chunked)\n * - split: FlowInput → SplitDocument[] (requires forEach)\n * - categorize: DocumentIR|FlowInput → {input, category}\n * - extract: DocumentIR|FlowInput|ChunkOutput → T (typed JSON)\n * - chunk: DocumentIR|DocumentIR[] → ChunkOutput\n * - combine: T[] → T|T[] (merges forEach results)\n * - trigger: any → TOutput (depends on child flow)\n *\n * Special behaviors:\n * - forEach auto-unwraps SplitDocument.input → FlowInput\n * - Conditional auto-unwraps {input, category} → input\n * - parse with chunked:true outputs DocumentIR[] instead of DocumentIR\n */\nexport const NODE_COMPATIBILITY_MATRIX: Record<NodeTypeName, Record<NodeTypeName, CompatibilityRule>> = {\n parse: {\n parse: {\n valid: false,\n reason: 'Cannot chain parse nodes. Parse is typically the starting node.'\n },\n split: {\n valid: false,\n reason: 'Split requires FlowInput, but parse outputs DocumentIR. Use split directly on input instead.',\n note: 'If you need to re-split after parsing, use trigger to invoke a child flow with FlowInput.'\n },\n categorize: {\n valid: true,\n note: 'categorize accepts DocumentIR and wraps it with {input, category}'\n },\n extract: {\n valid: true,\n note: 'extract accepts DocumentIR and produces typed JSON'\n },\n chunk: {\n valid: true,\n note: 'chunk accepts DocumentIR and produces ChunkOutput for RAG'\n },\n combine: {\n valid: false,\n reason: 'Parse outputs DocumentIR (single document), not an array. Combine requires array input from forEach.',\n note: 'Use parse with chunked:true to output DocumentIR[], then use combine.'\n },\n trigger: {\n valid: true,\n note: 'trigger accepts any input type'\n },\n output: {\n valid: true,\n note: 'output node can follow any node to select or transform results'\n }\n },\n split: {\n parse: {\n valid: true,\n requiresForEach: true,\n reason: 'Split outputs SplitDocument[] which requires forEach. forEach auto-unwraps SplitDocument.input → FlowInput for parse.',\n note: 'Enable forEach on split node before connecting to parse.'\n },\n split: {\n valid: false,\n reason: 'Cannot nest split operations. Split nodes cannot appear in forEach itemFlow.'\n },\n categorize: {\n valid: true,\n requiresForEach: true,\n reason: 'Split outputs SplitDocument[] which requires forEach. forEach auto-unwraps SplitDocument.input for categorize.'\n },\n extract: {\n valid: true,\n requiresForEach: true,\n reason: 'Split outputs SplitDocument[] which requires forEach. forEach auto-unwraps SplitDocument.input for extract.'\n },\n chunk: {\n valid: false,\n reason: 'SplitDocument output is incompatible with Chunk input. Chunk expects DocumentIR or DocumentIR[].',\n note: 'Use parse in forEach after split to convert SplitDocument → DocumentIR, then chunk.'\n },\n combine: {\n valid: false,\n reason: 'Combine should appear AFTER forEach completes, not as a forEach itemFlow step.',\n note: 'Place combine after the forEach block to merge results.'\n },\n trigger: {\n valid: true,\n requiresForEach: true,\n reason: 'Split outputs SplitDocument[] which requires forEach for processing.',\n note: 'forEach auto-unwraps SplitDocument.input for child flow.'\n },\n output: {\n valid: true,\n note: 'output node can follow any node to select or transform results'\n }\n },\n categorize: {\n parse: {\n valid: true,\n note: 'categorize outputs {input, category}. Conditional can unwrap this or use directly.'\n },\n split: {\n valid: false,\n reason: 'Split requires FlowInput, but categorize outputs {input, category}.',\n note: 'Use conditional to unwrap and pass input field to split.'\n },\n categorize: {\n valid: true,\n note: 'Can chain categorize nodes for multi-level classification.'\n },\n extract: {\n valid: true,\n note: 'extract can process the categorized document.'\n },\n chunk: {\n valid: false,\n reason: 'Categorize wraps input as {input, category}. Chunk needs unwrapped DocumentIR.',\n note: 'Use conditional to unwrap input field before chunk.'\n },\n combine: {\n valid: false,\n reason: 'Categorize outputs single result {input, category}, not an array. Combine requires array input.'\n },\n trigger: {\n valid: true,\n note: 'trigger accepts any input type, including {input, category}'\n },\n output: {\n valid: true,\n note: 'output node can follow any node to select or transform results'\n }\n },\n extract: {\n parse: {\n valid: false,\n reason: 'Extract outputs typed JSON (terminal node). Cannot pipe JSON to parse.',\n note: 'Extract should be one of the last steps in a flow. Use combine if extracting in parallel.'\n },\n split: {\n valid: false,\n reason: 'Extract outputs typed JSON (terminal node). Cannot pipe JSON to split.'\n },\n categorize: {\n valid: false,\n reason: 'Extract outputs typed JSON (terminal node). Cannot pipe JSON to categorize.'\n },\n extract: {\n valid: false,\n reason: 'Extract outputs typed JSON (terminal node). Cannot chain extractions on JSON output.',\n note: 'If you need multi-step extraction, extract from DocumentIR/ChunkOutput in parallel, then combine.'\n },\n chunk: {\n valid: false,\n reason: 'Extract outputs typed JSON, not DocumentIR. Chunk expects DocumentIR input.'\n },\n combine: {\n valid: true,\n note: 'Use combine to merge parallel extraction results from forEach.'\n },\n trigger: {\n valid: true,\n note: 'trigger accepts any input type, including extracted JSON'\n },\n output: {\n valid: true,\n note: 'output node can follow any node to select or transform results'\n }\n },\n chunk: {\n parse: {\n valid: false,\n reason: 'Chunk outputs ChunkOutput (specialized type), not FlowInput. Parse expects FlowInput as input.'\n },\n split: {\n valid: false,\n reason: 'Chunk outputs ChunkOutput, incompatible with Split input (FlowInput).'\n },\n categorize: {\n valid: false,\n reason: 'Chunk outputs ChunkOutput, incompatible with Categorize input (DocumentIR|FlowInput).',\n note: 'Categorize before chunking, not after.'\n },\n extract: {\n valid: true,\n note: 'extract has special handling for ChunkOutput - extracts data from chunks.'\n },\n chunk: {\n valid: false,\n reason: 'Cannot chain chunk operations. Chunk only once per document.',\n note: 'Different chunking strategies should be applied to the original DocumentIR, not to chunks.'\n },\n combine: {\n valid: false,\n reason: 'Chunk outputs ChunkOutput (specialized type), not an array type. Combine expects T[].',\n note: 'Use chunk on individual documents in forEach, then extract, then combine extractions.'\n },\n trigger: {\n valid: true,\n note: 'trigger accepts any input type, including ChunkOutput'\n },\n output: {\n valid: true,\n note: 'output node can follow any node to select or transform results'\n }\n },\n combine: {\n parse: {\n valid: true,\n note: 'After combining, result can be re-parsed if needed.'\n },\n split: {\n valid: false,\n reason: 'Combine output depends on strategy. Split requires FlowInput.',\n note: 'Most combine strategies output merged objects/arrays, not FlowInput.'\n },\n categorize: {\n valid: true,\n note: 'Can categorize combined results.'\n },\n extract: {\n valid: true,\n note: 'Can extract from combined results.'\n },\n chunk: {\n valid: true,\n note: 'Can chunk combined DocumentIR. Only valid if combine output is DocumentIR or DocumentIR[].'\n },\n combine: {\n valid: false,\n reason: 'Cannot chain combine nodes. Combine once per forEach operation.'\n },\n trigger: {\n valid: true,\n note: 'trigger accepts any input type'\n },\n output: {\n valid: true,\n note: 'output node can follow any node to select or transform results'\n }\n },\n trigger: {\n parse: {\n valid: true,\n requiresRuntimeValidation: true,\n note: 'Valid only if child flow returns FlowInput. Type safety cannot be guaranteed at build-time.'\n },\n split: {\n valid: true,\n requiresRuntimeValidation: true,\n note: 'Valid only if child flow returns FlowInput. Type safety cannot be guaranteed at build-time.'\n },\n categorize: {\n valid: true,\n requiresRuntimeValidation: true,\n note: 'Valid only if child flow returns DocumentIR or FlowInput. Type safety cannot be guaranteed at build-time.'\n },\n extract: {\n valid: true,\n requiresRuntimeValidation: true,\n note: 'Valid only if child flow returns DocumentIR, FlowInput, or ChunkOutput. Type safety cannot be guaranteed at build-time.'\n },\n chunk: {\n valid: true,\n requiresRuntimeValidation: true,\n note: 'Valid only if child flow returns DocumentIR or DocumentIR[]. Type safety cannot be guaranteed at build-time.'\n },\n combine: {\n valid: true,\n requiresRuntimeValidation: true,\n note: 'Valid only if child flow returns an array (T[]). Type safety cannot be guaranteed at build-time.'\n },\n trigger: {\n valid: true,\n requiresRuntimeValidation: true,\n note: 'Can nest trigger nodes (with circular dependency detection and max depth limits). Output type depends on nested child flow.'\n },\n output: {\n valid: true,\n note: 'output node can follow any node to select or transform results'\n }\n },\n output: {\n parse: {\n valid: false,\n reason: 'Output is a terminal node that selects/transforms results. Cannot chain to other nodes.'\n },\n split: {\n valid: false,\n reason: 'Output is a terminal node that selects/transforms results. Cannot chain to other nodes.'\n },\n categorize: {\n valid: false,\n reason: 'Output is a terminal node that selects/transforms results. Cannot chain to other nodes.'\n },\n extract: {\n valid: false,\n reason: 'Output is a terminal node that selects/transforms results. Cannot chain to other nodes.'\n },\n chunk: {\n valid: false,\n reason: 'Output is a terminal node that selects/transforms results. Cannot chain to other nodes.'\n },\n combine: {\n valid: false,\n reason: 'Output is a terminal node that selects/transforms results. Cannot chain to other nodes.'\n },\n trigger: {\n valid: false,\n reason: 'Output is a terminal node that selects/transforms results. Cannot chain to other nodes.'\n },\n output: {\n valid: true,\n note: 'Multiple output nodes are allowed to create multiple named outputs from a flow.'\n }\n }\n};\n\n/**\n * Get node type name from a NodeDef\n * @param node - Node definition\n * @returns Node type name (e.g., 'parse', 'extract')\n */\nexport function getNodeTypeName(node: NodeDef<any, any>): NodeTypeName | null {\n if (!node || !node.key) return null;\n const key = node.key;\n\n // Check if it's a known node type\n const knownTypes: NodeTypeName[] = ['parse', 'split', 'categorize', 'extract', 'chunk', 'combine', 'trigger', 'output'];\n return knownTypes.includes(key as NodeTypeName) ? (key as NodeTypeName) : null;\n}\n\n/**\n * Get type information from a node\n * @param node - Node definition\n * @returns NodeTypeInfo if available\n */\nexport function getNodeTypeInfo(node: NodeDef<any, any>): NodeTypeInfo | null {\n return node.__meta || null;\n}\n\n/**\n * Get compatible target nodes for a given source node\n * @param sourceType - Source node type name\n * @param includeForEach - Include connections that require forEach\n * @returns Array of compatible target node types\n */\nexport function getCompatibleTargets(sourceType: NodeTypeName, includeForEach: boolean = false): NodeTypeName[] {\n const rules = NODE_COMPATIBILITY_MATRIX[sourceType];\n if (!rules) return [];\n\n return Object.entries(rules)\n .filter(([_, rule]) => {\n if (!rule.valid) return false;\n if (rule.requiresForEach && !includeForEach) return false;\n return true;\n })\n .map(([targetType, _]) => targetType as NodeTypeName);\n}\n\n/**\n * Get suggested connections when a connection is invalid\n * @param sourceType - Source node type name\n * @returns Array of suggestion strings\n */\nexport function getSuggestedConnections(sourceType: NodeTypeName): string[] {\n const compatibleTargets = getCompatibleTargets(sourceType, false);\n const forEachTargets = getCompatibleTargets(sourceType, true).filter(\n t => !compatibleTargets.includes(t)\n );\n\n if (compatibleTargets.length === 0 && forEachTargets.length === 0) {\n return [`${sourceType} has no standard outgoing connections (terminal node).`];\n }\n\n const suggestions: string[] = [];\n\n if (compatibleTargets.length > 0) {\n suggestions.push(`${sourceType} can connect to:`);\n compatibleTargets.forEach(target => {\n const rule = NODE_COMPATIBILITY_MATRIX[sourceType][target];\n suggestions.push(` • ${target}${rule.note ? ` - ${rule.note}` : ''}`);\n });\n }\n\n if (forEachTargets.length > 0) {\n suggestions.push(`${sourceType} can connect to (with forEach enabled):`);\n forEachTargets.forEach(target => {\n const rule = NODE_COMPATIBILITY_MATRIX[sourceType][target];\n suggestions.push(` • ${target}${rule.note ? ` - ${rule.note}` : ''}`);\n });\n }\n\n return suggestions;\n}\n\n/**\n * Validation result for node connections\n */\nexport type ValidationResult = {\n valid: boolean;\n reason?: string;\n suggestions?: string[];\n requiresForEach?: boolean;\n /** Warning message for connections that are valid but require runtime type checking */\n warning?: string;\n};\n\n/**\n * Validate if two node types can be connected\n * @param sourceType - Source node type name\n * @param targetType - Target node type name\n * @param forEachEnabled - Whether forEach is enabled on the source node\n * @returns Validation result with reason and suggestions\n */\nexport function validateNodeConnection(\n sourceType: NodeTypeName,\n targetType: NodeTypeName,\n forEachEnabled: boolean = false\n): ValidationResult {\n const rule = NODE_COMPATIBILITY_MATRIX[sourceType]?.[targetType];\n\n if (!rule) {\n return {\n valid: false,\n reason: `Unknown node type combination: ${sourceType} → ${targetType}`,\n suggestions: ['Ensure both nodes are valid node types.']\n };\n }\n\n if (!rule.valid) {\n return {\n valid: false,\n reason: rule.reason,\n suggestions: getSuggestedConnections(sourceType)\n };\n }\n\n // Check forEach requirement\n if (rule.requiresForEach && !forEachEnabled) {\n return {\n valid: false,\n reason: `Cannot connect ${sourceType} to ${targetType} without forEach enabled.`,\n suggestions: [\n `Enable forEach on the ${sourceType} node:`,\n ` 1. Click the ${sourceType} node`,\n ` 2. Enable \"forEach Processing\" in the configuration`,\n ` 3. Try connecting again`,\n '',\n ...getSuggestedConnections(sourceType)\n ],\n requiresForEach: true\n };\n }\n\n // Check if runtime validation is required\n if (rule.requiresRuntimeValidation) {\n return {\n valid: true,\n warning: `⚠️ ${sourceType} → ${targetType}: ${rule.note || 'Type compatibility depends on runtime values and cannot be validated at build-time.'}`\n };\n }\n\n return {\n valid: true\n };\n}\n\n/**\n * Get valid starting nodes for forEach itemFlow based on parent node type\n *\n * When a node outputs an array and uses forEach, the itemFlow receives individual\n * array items. This function returns which node types can accept those items.\n *\n * @param parentType - The node type that outputs the array (e.g., 'split', 'parse')\n * @returns Array of node types that can start the forEach itemFlow\n *\n * @example\n * ```typescript\n * // split outputs SplitDocument[], itemFlow gets SplitDocument\n * getValidForEachStarters('split') // ['parse', 'extract', 'categorize', 'trigger']\n *\n * // parse(chunked:true) outputs DocumentIR[], itemFlow gets DocumentIR\n * getValidForEachStarters('parse') // ['categorize', 'extract', 'chunk']\n * ```\n */\nexport function getValidForEachStarters(parentType: NodeTypeName): NodeTypeName[] {\n const rules = NODE_COMPATIBILITY_MATRIX[parentType];\n if (!rules) return [];\n\n // Get all targets that require forEach (these are valid itemFlow starters)\n return Object.entries(rules)\n .filter(([_, rule]) => rule.valid && rule.requiresForEach)\n .map(([targetType, _]) => targetType as NodeTypeName);\n}\n\n/**\n * Validate if a node type can start a forEach itemFlow for a given parent\n *\n * @param parentType - The node type that outputs the array (e.g., 'split')\n * @param starterType - The node type to validate as itemFlow starter\n * @returns ValidationResult with detailed error messages and suggestions\n *\n * @example\n * ```typescript\n * // Valid: split → forEach → parse\n * canStartForEachItemFlow('split', 'parse') // { valid: true }\n *\n * // Invalid: split → forEach → chunk\n * canStartForEachItemFlow('split', 'chunk')\n * // {\n * // valid: false,\n * // reason: 'chunk cannot start forEach itemFlow after split...',\n * // suggestions: ['Valid starters: parse, extract, categorize, trigger']\n * // }\n * ```\n */\nexport function canStartForEachItemFlow(\n parentType: NodeTypeName,\n starterType: NodeTypeName\n): ValidationResult {\n const rule = NODE_COMPATIBILITY_MATRIX[parentType]?.[starterType];\n\n if (!rule) {\n return {\n valid: false,\n reason: `Unknown node type combination: ${parentType} → forEach → ${starterType}`,\n suggestions: ['Ensure both nodes are valid node types.']\n };\n }\n\n // Check if this connection requires forEach (meaning it's valid in itemFlow)\n if (rule.valid && rule.requiresForEach) {\n return {\n valid: true\n };\n }\n\n // If the rule is invalid, provide error\n if (!rule.valid) {\n const validStarters = getValidForEachStarters(parentType);\n return {\n valid: false,\n reason: `${starterType} cannot start forEach itemFlow after ${parentType}. ${rule.reason || 'Type incompatible with forEach unwrapped item.'}`,\n suggestions: validStarters.length > 0\n ? [`Valid itemFlow starters for ${parentType}: ${validStarters.join(', ')}`]\n : [`${parentType} has no valid forEach itemFlow starters.`]\n };\n }\n\n // If valid but doesn't require forEach, it's not a valid itemFlow starter\n const validStarters = getValidForEachStarters(parentType);\n return {\n valid: false,\n reason: `${starterType} cannot start forEach itemFlow after ${parentType}. This connection does not require forEach, meaning it expects the full array, not individual items.`,\n suggestions: validStarters.length > 0\n ? [`Valid itemFlow starters for ${parentType}: ${validStarters.join(', ')}`]\n : [`${parentType} has no valid forEach itemFlow starters.`]\n };\n}\n\n/**\n * JSON Schema node structure for validation.\n * Represents a node in a JSON Schema definition.\n */\nexport interface JSONSchemaNode {\n type?: string | string[];\n properties?: Record<string, JSONSchemaNode>;\n items?: JSONSchemaNode | JSONSchemaNode[];\n required?: string[];\n enum?: (string | number | boolean | null)[];\n nullable?: boolean;\n anyOf?: JSONSchemaNode[];\n oneOf?: JSONSchemaNode[];\n allOf?: JSONSchemaNode[];\n const?: unknown;\n additionalProperties?: boolean | JSONSchemaNode;\n minLength?: number;\n maxLength?: number;\n minimum?: number;\n maximum?: number;\n minItems?: number;\n maxItems?: number;\n pattern?: string;\n format?: string;\n description?: string;\n default?: unknown;\n $ref?: string;\n}\n\n/**\n * Lightweight JSON Schema validator for Edge Runtime compatibility\n *\n * Validates data against a JSON Schema without using AJV's code generation.\n * This is fully Edge Runtime compatible with zero dependencies.\n *\n * @param data - The data to validate\n * @param schema - JSON Schema object (plain object, not AJV JSONSchemaType)\n * @returns The validated data cast to type T\n * @throws Error if validation fails\n */\nexport function validateJson<T>(data: unknown, schema: JSONSchemaNode): T {\n const errors: string[] = [];\n const MAX_DEPTH = 50; // Prevent DoS via deeply nested objects\n\n function validate(value: unknown, schema: JSONSchemaNode, path: string = '', depth: number = 0): void {\n // Check recursion depth to prevent DoS attacks\n if (depth > MAX_DEPTH) {\n errors.push(`${path || 'root'}: maximum nesting depth (${MAX_DEPTH}) exceeded`);\n return;\n }\n\n // Handle nullable values\n if (schema.nullable && (value === null || value === undefined)) {\n return;\n }\n\n if (value === null || value === undefined) {\n if (schema.nullable !== true) {\n errors.push(`${path || 'root'}: value is null or undefined`);\n }\n return;\n }\n\n // Validate type\n const actualType = Array.isArray(value) ? 'array' : typeof value;\n const expectedType = schema.type;\n\n if (expectedType) {\n // Handle type validation\n if (expectedType === 'integer') {\n if (typeof value !== 'number' || !Number.isInteger(value)) {\n errors.push(`${path || 'root'}: expected integer, got ${actualType}`);\n return;\n }\n } else if (expectedType === 'number') {\n if (typeof value !== 'number') {\n errors.push(`${path || 'root'}: expected number, got ${actualType}`);\n return;\n }\n } else if (expectedType === 'string') {\n if (typeof value !== 'string') {\n errors.push(`${path || 'root'}: expected string, got ${actualType}`);\n return;\n }\n } else if (expectedType === 'boolean') {\n if (typeof value !== 'boolean') {\n errors.push(`${path || 'root'}: expected boolean, got ${actualType}`);\n return;\n }\n } else if (expectedType === 'object') {\n if (typeof value !== 'object' || Array.isArray(value)) {\n errors.push(`${path || 'root'}: expected object, got ${actualType}`);\n return;\n }\n\n // Validate required properties\n if (schema.required && Array.isArray(schema.required)) {\n for (const reqProp of schema.required) {\n if (!(reqProp in value)) {\n errors.push(`${path}.${reqProp}: required property missing`);\n }\n }\n }\n\n // Validate additionalProperties and check for prototype pollution\n const dangerousProps = ['__proto__', 'constructor', 'prototype'];\n\n if (schema.additionalProperties === false && schema.properties) {\n const allowedProps = Object.keys(schema.properties);\n // required can be boolean in malformed schemas - must check Array.isArray\n const requiredProps = Array.isArray(schema.required) ? schema.required : [];\n const allAllowedProps = new Set([...allowedProps, ...requiredProps]);\n\n // Check all keys including potentially dangerous ones\n for (const key of [...Object.keys(value), ...Object.getOwnPropertyNames(value)]) {\n // Explicitly reject dangerous properties\n if (dangerousProps.includes(key)) {\n errors.push(`${path}.${key}: dangerous property not allowed`);\n continue;\n }\n\n if (!allAllowedProps.has(key)) {\n errors.push(`${path}.${key}: additional property not allowed`);\n }\n }\n } else {\n // Even without additionalProperties: false, reject dangerous properties\n for (const key of dangerousProps) {\n if (key in value && Object.prototype.hasOwnProperty.call(value, key)) {\n errors.push(`${path}.${key}: dangerous property not allowed`);\n }\n }\n }\n\n // Validate properties\n if (schema.properties) {\n const valueObj = value as Record<string, unknown>;\n for (const [propName, propSchema] of Object.entries(schema.properties)) {\n if (propName in valueObj) {\n validate(valueObj[propName], propSchema, path ? `${path}.${propName}` : propName, depth + 1);\n }\n }\n }\n } else if (expectedType === 'array') {\n if (!Array.isArray(value)) {\n errors.push(`${path || 'root'}: expected array, got ${actualType}`);\n return;\n }\n\n // Validate array items\n if (schema.items && !Array.isArray(schema.items)) {\n const itemSchema = schema.items;\n value.forEach((item, index) => {\n validate(item, itemSchema, `${path}[${index}]`, depth + 1);\n });\n }\n }\n }\n }\n\n validate(data, schema);\n\n if (errors.length > 0) {\n throw new Error(`Schema validation failed:\\n${errors.join('\\n')}`);\n }\n\n return data as T;\n}\n\n/**\n * Reserved variables that are auto-injected per node type.\n * These variables come from config or computed data and cannot be overridden by users.\n */\nexport const RESERVED_VARIABLES = {\n extract: ['schema', 'documentText', 'schemaTitle', 'schemaDescription', 'structuredFormat'],\n categorize: ['categories', 'documentText'],\n parse: ['format', 'schema', 'describeFigures', 'citationsEnabled']\n} as const;\n\n/**\n * Validates that user-provided promptVariables don't attempt to override reserved variables.\n * Emits console warnings if reserved variables are found in user variables and removes them.\n *\n * @param nodeType - The type of node (extract, categorize, parse)\n * @param userVariables - The user-provided promptVariables object\n * @param autoInjectedVariables - The auto-injected variables object\n * @returns A cleaned variables object with reserved variables protected\n */\nexport function protectReservedVariables(\n nodeType: 'extract' | 'categorize' | 'parse',\n userVariables: Record<string, any> | undefined,\n autoInjectedVariables: Record<string, any>\n): Record<string, any> {\n if (!userVariables || Object.keys(userVariables).length === 0) {\n return autoInjectedVariables;\n }\n\n const reserved = RESERVED_VARIABLES[nodeType];\n const warnings: string[] = [];\n\n // Check for reserved variable override attempts\n for (const key of reserved) {\n if (key in userVariables) {\n warnings.push(key);\n }\n }\n\n // Emit warnings if any reserved variables were attempted\n if (warnings.length > 0) {\n console.warn(\n `[doclo] Attempted to override reserved variables in ${nodeType} node: ${warnings.join(', ')}. ` +\n `These variables are auto-injected from config and cannot be overridden. ` +\n `They will be ignored.`\n );\n }\n\n // Merge: auto-injected first, then user variables (but reserved vars take precedence)\n return {\n ...autoInjectedVariables,\n ...userVariables,\n // Restore reserved variables to ensure they can't be overridden\n ...Object.fromEntries(\n reserved.map(key => [key, autoInjectedVariables[key]])\n )\n };\n}\n","/**\n * URL Validation and SSRF Protection\n *\n * ⚠️ SECURITY CRITICAL: SSRF (Server-Side Request Forgery) Prevention\n *\n * This module blocks URLs that could be used in Server-Side Request Forgery attacks:\n * - Internal IP addresses (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)\n * - Loopback addresses (127.0.0.0/8, ::1)\n * - Cloud metadata services (AWS, GCP, Aliyun, etc.)\n * - Link-local addresses (169.254.0.0/16)\n *\n * Attack example: An attacker tricks your server to make requests to:\n * - http://169.254.169.254 (AWS credentials endpoint)\n * - http://10.0.0.1:8080/admin (internal service)\n * - http://localhost/api/admin (localhost admin endpoint)\n *\n * Prevents Server-Side Request Forgery attacks by validating URLs against blocklist\n */\n\n/**\n * IP ranges that should be blocked (internal networks)\n * RFC 1918 private ranges + loopback + cloud metadata\n */\nconst BLOCKED_IP_RANGES = [\n // Loopback\n { start: '127.0.0.0', end: '127.255.255.255' },\n // Private Class A\n { start: '10.0.0.0', end: '10.255.255.255' },\n // Private Class B\n { start: '172.16.0.0', end: '172.31.255.255' },\n // Private Class C\n { start: '192.168.0.0', end: '192.168.255.255' },\n // Link Local\n { start: '169.254.0.0', end: '169.254.255.255' },\n];\n\nconst BLOCKED_METADATA_HOSTS = [\n '169.254.169.254', // AWS metadata service\n '169.254.169.253', // AWS metadata service (Windows)\n 'metadata.google.internal', // GCP metadata service\n 'metadata', // GCP alias\n '100.100.100.200', // Aliyun metadata service\n 'instance-data', // OpenStack alias\n];\n\n/**\n * IPv6 address patterns that should be blocked\n * Prevents SSRF attacks using IPv6 addresses\n */\nconst BLOCKED_IPV6_PATTERNS = [\n /^::1$/, // Loopback (::1)\n /^::$/, // Any address (::)\n /^::ffff:/i, // IPv4-mapped IPv6 (::ffff:0:0/96) - matches ::ffff:127.0.0.1\n /^::ffff:0:/i, // IPv4-mapped IPv6 alternative\n /^fe80:/i, // Link-local (fe80::/10)\n /^fec0:/i, // Site-local deprecated (fec0::/10)\n /^fc00:/i, // Unique local address (fc00::/7)\n /^fd00:/i, // Unique local address (fd00::/8)\n /^ff00:/i, // Multicast (ff00::/8)\n /^0:0:0:0:0:0:0:1$/i, // Loopback expanded form\n];\n\n/**\n * Convert IPv4 string to number for range comparison\n */\nfunction ipToNumber(ip: string): number {\n const parts = ip.split('.').map(Number);\n if (parts.length !== 4 || parts.some((p) => p < 0 || p > 255)) {\n return -1;\n }\n return (parts[0] << 24) + (parts[1] << 16) + (parts[2] << 8) + parts[3];\n}\n\n/**\n * Check if IP is in blocked range\n */\nfunction isIpInBlockedRange(ip: string): boolean {\n const ipNum = ipToNumber(ip);\n if (ipNum === -1) return false;\n\n return BLOCKED_IP_RANGES.some((range) => {\n const startNum = ipToNumber(range.start);\n const endNum = ipToNumber(range.end);\n return ipNum >= startNum && ipNum <= endNum;\n });\n}\n\n/**\n * Check if IPv6 address is blocked\n * Handles both bracket notation ([::1]) and standard notation (::1)\n */\nfunction isIPv6Blocked(hostname: string): boolean {\n // Remove brackets if present ([::1] -> ::1)\n const addr = hostname.replace(/^\\[|\\]$/g, '');\n\n return BLOCKED_IPV6_PATTERNS.some(pattern => pattern.test(addr));\n}\n\n/**\n * Validate a URL to prevent SSRF (Server-Side Request Forgery) attacks\n *\n * ⚠️ SECURITY CRITICAL: SSRF Prevention\n *\n * This function blocks URLs that could be used to exploit the server:\n * - Internal IP addresses (breaks firewall perimeter security)\n * - Cloud metadata services (leaks credentials, API keys)\n * - Localhost/loopback (access admin services, debug ports)\n *\n * By default, blocks internal network access automatically.\n *\n * @param urlString - The URL to validate\n * @param options - Validation options\n * - blockInternal (default: true) - Block internal IP ranges. ⚠️ Set to false only if you understand SSRF risks\n * - allowedProtocols (default: ['http:', 'https:']) - Restrict to specific protocols\n * @throws Error if URL is invalid, uses blocked protocol, or points to blocked IP/host\n * @returns The validated URL object\n * @security Always validate user-provided URLs. Do not set blockInternal to false without SSRF security review\n *\n * @example\n * ```typescript\n * // Validate user-provided URL\n * try {\n * const url = validateUrl(userInput);\n * const response = await fetch(url.toString());\n * } catch (error) {\n * // URL was malicious or pointed to internal resource\n * }\n * ```\n */\nexport function validateUrl(\n urlString: string,\n options: {\n blockInternal?: boolean;\n allowedProtocols?: string[];\n } = {}\n): URL {\n const {\n blockInternal = true,\n allowedProtocols = ['http:', 'https:'],\n } = options;\n\n let url: URL;\n\n try {\n url = new URL(urlString);\n } catch (error) {\n throw new Error(`Invalid URL: ${urlString}`);\n }\n\n // Check protocol\n if (!allowedProtocols.includes(url.protocol)) {\n throw new Error(\n `Blocked protocol: ${url.protocol}. Allowed: ${allowedProtocols.join(', ')}`\n );\n }\n\n // Check for internal access if enabled\n if (blockInternal) {\n const hostname = url.hostname;\n\n // Check blocked metadata hosts\n if (BLOCKED_METADATA_HOSTS.includes(hostname)) {\n throw new Error(`Blocked metadata service: ${hostname}`);\n }\n\n // Check IPv6 addresses (includes bracket notation)\n if (hostname.includes(':') || hostname.startsWith('[')) {\n if (isIPv6Blocked(hostname)) {\n throw new Error(`Blocked IPv6 address: ${hostname}`);\n }\n }\n\n // Check if IPv4 is in blocked range\n if (isIpInBlockedRange(hostname)) {\n throw new Error(`Blocked internal IP address: ${hostname}`);\n }\n\n // Block localhost keyword\n if (hostname === 'localhost') {\n throw new Error('Blocked localhost access');\n }\n }\n\n return url;\n}\n\n/**\n * Fetch a URL with SSRF protection and timeout\n * @param url - The URL to fetch\n * @param timeoutMs - Request timeout in milliseconds (default 30s)\n * @throws Error if URL is invalid, blocked, or request times out\n */\nexport async function secureFetch(\n url: string,\n timeoutMs: number = 30000\n): Promise<Response> {\n // Validate URL first\n const validatedUrl = validateUrl(url);\n\n // Create abort controller for timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(validatedUrl.toString(), {\n signal: controller.signal,\n });\n return response;\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\n/**\n * Extract hostname from URL string for validation\n * Useful for pre-validation checks before full URL parsing\n */\nexport function getHostnameFromUrl(urlString: string): string {\n try {\n const url = new URL(urlString);\n return url.hostname;\n } catch {\n return '';\n }\n}\n","/**\n * Resource Limits and DoS Protection\n *\n * ⚠️ SECURITY WARNING: Resource limits are critical for protecting against:\n * - Resource exhaustion attacks (large file downloads)\n * - Denial of Service (slow loris, timeout attacks)\n * - Memory exhaustion (deeply nested JSON, large arrays)\n *\n * Prevents resource exhaustion attacks through file size limits and timeouts\n */\n\n/**\n * Default resource limits\n *\n * ⚠️ SECURITY CRITICAL: These are conservative defaults designed to prevent DoS attacks.\n *\n * - MAX_FILE_SIZE (100MB): Prevents downloading very large files that could exhaust memory or disk\n * - REQUEST_TIMEOUT (30s): Prevents slow-loris attacks and hung connections\n * - MAX_JSON_DEPTH (100): Prevents billion laughs attack (deeply nested JSON)\n *\n * Do not increase these limits without understanding the security implications:\n * - Higher file size limit → Greater risk of resource exhaustion\n * - Lower timeout → May reject legitimate slow requests\n * - Lower JSON depth → May reject valid documents\n *\n * @security These limits can be overridden by SDK users, but doing so reduces security.\n */\nexport const DEFAULT_LIMITS = {\n // Maximum file size: 100MB\n MAX_FILE_SIZE: 100 * 1024 * 1024,\n // Request timeout: 30 seconds\n REQUEST_TIMEOUT: 30000,\n // Maximum JSON parse depth\n MAX_JSON_DEPTH: 100,\n};\n\n/**\n * Validate file size before processing\n *\n * ⚠️ SECURITY WARNING: File size validation prevents resource exhaustion.\n * - Without limits: attackers can force downloads of multi-gigabyte files\n * - Memory impact: files are loaded into memory for base64 encoding\n * - Disk impact: temporary storage of downloaded files\n *\n * @param size - The file size in bytes\n * @param maxSize - Maximum allowed size in bytes (default 100MB, can be customized)\n * @throws Error if file exceeds size limit\n * @security Do not disable this check without understanding resource implications\n *\n * @example\n * ```typescript\n * // Standard check with default limit (100MB)\n * validateFileSize(fileSize);\n *\n * // Custom limit for use case that requires larger files\n * validateFileSize(fileSize, 500 * 1024 * 1024); // 500MB\n * ```\n */\nexport function validateFileSize(\n size: number,\n maxSize: number = DEFAULT_LIMITS.MAX_FILE_SIZE\n): void {\n if (size > maxSize) {\n const maxMB = Math.round(maxSize / 1024 / 1024);\n const sizeMB = Math.round(size / 1024 / 1024);\n throw new Error(\n `File size ${sizeMB}MB exceeds maximum allowed size of ${maxMB}MB`\n );\n }\n}\n\n/**\n * Create a fetch controller with timeout\n * @param timeoutMs - Timeout in milliseconds (default 30s)\n * @returns AbortController with timeout configured\n */\nexport function createFetchController(\n timeoutMs: number = DEFAULT_LIMITS.REQUEST_TIMEOUT\n): AbortController {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n // Store timeout ID so caller can clean it up\n (controller as any).__timeoutId = timeoutId;\n\n return controller;\n}\n\n/**\n * Clean up fetch controller timeout\n * @param controller - The AbortController to clean up\n */\nexport function cleanupFetchController(controller: AbortController): void {\n const timeoutId = (controller as any).__timeoutId;\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n}\n\n/**\n * Execute a fetch with automatic timeout and cleanup\n *\n * ⚠️ SECURITY WARNING: Timeout protection prevents slow-loris and other timing attacks.\n * - Without timeout: requests can hang indefinitely, consuming server resources\n * - Slow-loris attack: attacker sends requests slowly to exhaust connection pool\n * - Zombie connections: closed clients but open server connections\n *\n * Default timeout (30s) balances security with legitimate use:\n * - Too short: may reject slow networks or legitimate large downloads\n * - Too long: keeps server resources tied up, enables DoS attacks\n *\n * @param url - The URL to fetch\n * @param options - Fetch options (method, headers, body, etc.)\n * @param timeoutMs - Timeout in milliseconds (default 30s, can be customized)\n * @returns The fetch response\n * @throws Error if request times out\n * @security Do not use very long timeouts without understanding DoS implications\n *\n * @example\n * ```typescript\n * // Default timeout (30 seconds)\n * const response = await fetchWithTimeout('https://example.com/file.pdf');\n *\n * // Custom timeout for slower connections\n * const response = await fetchWithTimeout(url, options, 60000); // 60 seconds\n * ```\n */\nexport async function fetchWithTimeout(\n url: string,\n options: RequestInit = {},\n timeoutMs: number = DEFAULT_LIMITS.REQUEST_TIMEOUT\n): Promise<Response> {\n const controller = createFetchController(timeoutMs);\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n cache: 'no-store', // Prevent Next.js cache revalidation which can cause AbortError (see: github.com/vercel/next.js/issues/54045)\n });\n return response;\n } finally {\n cleanupFetchController(controller);\n }\n}\n\n/**\n * Check buffer size before allocation\n * @param size - The buffer size in bytes\n * @param maxSize - Maximum allowed size (default 100MB)\n * @throws Error if buffer would exceed memory limit\n */\nexport function validateBufferSize(\n size: number,\n maxSize: number = DEFAULT_LIMITS.MAX_FILE_SIZE\n): void {\n validateFileSize(size, maxSize);\n}\n\n/**\n * Memory-safe JSON parse with depth limit\n *\n * ⚠️ SECURITY WARNING: Depth limits prevent billion laughs / XML bomb attacks in JSON format.\n * - Billion laughs attack: deeply nested JSON causes exponential memory expansion\n * - Stack overflow: deeply nested structures can exhaust call stack\n * - Resource exhaustion: parsing deeply nested JSON consumes CPU and memory\n *\n * Attack example (evil.json):\n * ```json\n * {\"a\":{\"b\":{\"c\":{\"d\":{\"e\":...}}}}} // 1000+ levels deep\n * ```\n *\n * Default limit (100 levels) prevents attacks while allowing legitimate documents.\n *\n * @param text - JSON string to parse\n * @param maxDepth - Maximum nesting depth in levels (default 100, can be customized)\n * @returns Parsed object\n * @throws Error if JSON exceeds depth limit, size limit, or is invalid\n * @security Do not disable depth checking without understanding XML bomb implications\n *\n * @example\n * ```typescript\n * // Standard parsing with default depth limit (100 levels)\n * const data = safeJsonParse(jsonString);\n *\n * // Custom depth limit for data that needs deeper nesting\n * const data = safeJsonParse(jsonString, 200); // 200 levels\n * ```\n */\nexport function safeJsonParse(\n text: string,\n maxDepth: number = DEFAULT_LIMITS.MAX_JSON_DEPTH\n): unknown {\n try {\n // Quick size check first\n if (text.length > DEFAULT_LIMITS.MAX_FILE_SIZE) {\n throw new Error('JSON string exceeds maximum size');\n }\n\n const obj = JSON.parse(text);\n\n // Check nesting depth\n function checkDepth(obj: unknown, depth: number = 0): void {\n if (depth > maxDepth) {\n throw new Error(`JSON nesting depth exceeds maximum of ${maxDepth}`);\n }\n\n if (typeof obj === 'object' && obj !== null) {\n for (const value of Object.values(obj)) {\n checkDepth(value, depth + 1);\n }\n }\n }\n\n checkDepth(obj);\n return obj;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Invalid JSON: ${String(error)}`);\n }\n}\n","/**\n * Universal Base64 Adapter\n *\n * Provides base64 encoding/decoding for both Node.js and Edge Runtime.\n * Replaces Node.js Buffer usage with Web APIs for Edge compatibility.\n *\n * @module @doclo/core/runtime/base64\n */\n\n/**\n * Convert ArrayBuffer to base64 string\n *\n * Uses different strategies depending on runtime:\n * - Edge Runtime / Browser: btoa() with binary string conversion\n * - Node.js: Buffer.toString('base64')\n *\n * @param buffer - ArrayBuffer to encode\n * @returns Base64 encoded string\n *\n * @example\n * ```typescript\n * const buffer = new Uint8Array([72, 101, 108, 108, 111]).buffer;\n * const base64 = arrayBufferToBase64(buffer); // \"SGVsbG8=\"\n * ```\n */\nexport function arrayBufferToBase64(buffer: ArrayBuffer): string {\n // Node.js: Use Buffer for best performance\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(buffer).toString('base64');\n }\n\n // Edge Runtime / Browser: Use btoa() with binary string\n const bytes = new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n\n/**\n * Convert base64 string to ArrayBuffer\n *\n * Uses different strategies depending on runtime:\n * - Edge Runtime / Browser: atob() with Uint8Array conversion\n * - Node.js: Buffer.from(base64, 'base64')\n *\n * @param base64 - Base64 encoded string (with or without data URI prefix)\n * @returns Decoded ArrayBuffer\n *\n * @example\n * ```typescript\n * const buffer = base64ToArrayBuffer(\"SGVsbG8=\");\n * const text = new TextDecoder().decode(buffer); // \"Hello\"\n *\n * // Also handles data URIs\n * const buffer2 = base64ToArrayBuffer(\"data:image/png;base64,iVBORw0KG...\");\n * ```\n */\nexport function base64ToArrayBuffer(base64: string): ArrayBuffer {\n // Remove data URI prefix if present\n const cleanBase64 = base64.replace(/^data:[^;]+;base64,/, '');\n\n // Node.js: Use Buffer for best performance\n if (typeof Buffer !== 'undefined') {\n const buffer = Buffer.from(cleanBase64, 'base64');\n // Convert Node.js Buffer to ArrayBuffer\n return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);\n }\n\n // Edge Runtime / Browser: Use atob()\n const binaryString = atob(cleanBase64);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes.buffer;\n}\n\n/**\n * Convert Uint8Array to base64 string\n *\n * Convenience wrapper around arrayBufferToBase64 for Uint8Array inputs.\n *\n * @param bytes - Uint8Array to encode\n * @returns Base64 encoded string\n */\nexport function uint8ArrayToBase64(bytes: Uint8Array): string {\n return arrayBufferToBase64(bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer);\n}\n\n/**\n * Convert base64 string to Uint8Array\n *\n * Convenience wrapper around base64ToArrayBuffer with Uint8Array result.\n *\n * @param base64 - Base64 encoded string\n * @returns Decoded Uint8Array\n */\nexport function base64ToUint8Array(base64: string): Uint8Array {\n return new Uint8Array(base64ToArrayBuffer(base64));\n}\n\n/**\n * Create a data URI from ArrayBuffer\n *\n * @param buffer - Data to encode\n * @param mimeType - MIME type (default: application/octet-stream)\n * @returns Data URI string\n *\n * @example\n * ```typescript\n * const buffer = new TextEncoder().encode(\"Hello, World!\");\n * const dataUri = createDataUri(buffer.buffer, 'text/plain');\n * // \"data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==\"\n * ```\n */\nexport function createDataUri(buffer: ArrayBuffer, mimeType = 'application/octet-stream'): string {\n const base64 = arrayBufferToBase64(buffer);\n return `data:${mimeType};base64,${base64}`;\n}\n\n/**\n * Check if a string is a valid data URI\n *\n * @param input - String to check\n * @returns True if valid data URI format\n */\nexport function isDataUri(input: string): boolean {\n return /^data:[^;,]+;base64,/.test(input);\n}\n\n/**\n * Extract MIME type from data URI\n *\n * @param dataUri - Data URI string\n * @returns MIME type or null if invalid\n *\n * @example\n * ```typescript\n * const mime = extractMimeType(\"data:image/png;base64,iVBOR...\");\n * console.log(mime); // \"image/png\"\n * ```\n */\nexport function extractMimeType(dataUri: string): string | null {\n const match = dataUri.match(/^data:([^;,]+);base64,/);\n return match ? match[1] : null;\n}\n","/**\n * MIME Type Detection Utility\n *\n * Detects MIME types from actual file data (magic bytes) to prevent mismatches\n * between declared MIME types and actual file content.\n *\n * Uses the `file-type` package for comprehensive format detection, with\n * manual fallback for basic types in synchronous contexts.\n */\n\nimport { fileTypeFromBuffer } from 'file-type';\n\n/**\n * Detects MIME type from base64-encoded data using the file-type package.\n * This is the preferred async method that supports 100+ file formats.\n *\n * @param base64Data - Base64 string (with or without data URI prefix)\n * @returns Detected MIME type (e.g., \"image/jpeg\", \"application/pdf\")\n * @throws Error if format is unsupported or data is invalid\n *\n * @example\n * ```typescript\n * const base64 = \"data:image/jpeg;base64,/9j/4AAQSkZJRg...\";\n * const mimeType = await detectMimeTypeFromBase64Async(base64);\n * console.log(mimeType); // \"image/jpeg\"\n * ```\n */\nexport async function detectMimeTypeFromBase64Async(base64Data: string): Promise<string> {\n // Strip data URI prefix if present\n const base64Only = base64Data.includes(',')\n ? base64Data.split(',')[1]\n : base64Data;\n\n // Decode to Uint8Array\n const binaryString = atob(base64Only);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n\n // Use file-type for detection\n const result = await fileTypeFromBuffer(bytes);\n if (result) {\n return result.mime;\n }\n\n throw new Error(\n `Unsupported file format. Magic bytes: ${Array.from(bytes.slice(0, 4))\n .map(b => b.toString(16).padStart(2, '0'))\n .join(' ')}`\n );\n}\n\n/**\n * Detects MIME type from base64-encoded data by examining magic bytes.\n * This is a synchronous fallback for basic formats.\n *\n * Supports:\n * - Images: JPEG, PNG, WebP, GIF, TIFF, BMP\n * - Documents: PDF, RTF\n * - Archives: ZIP (for DOCX, XLSX, PPTX, EPUB detection via extension)\n *\n * @param base64Data - Base64 string (with or without data URI prefix)\n * @returns Detected MIME type (e.g., \"image/jpeg\", \"application/pdf\")\n * @throws Error if format is unsupported or data is invalid\n *\n * @example\n * ```typescript\n * const base64 = \"data:image/jpeg;base64,/9j/4AAQSkZJRg...\";\n * const mimeType = detectMimeTypeFromBase64(base64);\n * console.log(mimeType); // \"image/jpeg\"\n * ```\n */\nexport function detectMimeTypeFromBase64(base64Data: string): string {\n // Strip data URI prefix if present\n const base64Only = base64Data.includes(',')\n ? base64Data.split(',')[1]\n : base64Data;\n\n // Decode first 16 bytes (enough for all magic byte checks)\n const binaryString = atob(base64Only.substring(0, 24));\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n\n return detectMimeTypeFromBytes(bytes);\n}\n\n/**\n * Detects MIME type from raw byte array.\n *\n * @param bytes - Uint8Array containing file data\n * @returns Detected MIME type\n * @throws Error if format is unsupported\n */\nexport function detectMimeTypeFromBytes(bytes: Uint8Array): string {\n if (bytes.length < 4) {\n throw new Error('Insufficient data to detect MIME type (need at least 4 bytes)');\n }\n\n // JPEG: FF D8 FF\n if (bytes[0] === 0xFF && bytes[1] === 0xD8 && bytes[2] === 0xFF) {\n return 'image/jpeg';\n }\n\n // PNG: 89 50 4E 47 0D 0A 1A 0A\n if (bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4E && bytes[3] === 0x47) {\n return 'image/png';\n }\n\n // GIF: 47 49 46 38 (GIF8)\n if (bytes[0] === 0x47 && bytes[1] === 0x49 && bytes[2] === 0x46 && bytes[3] === 0x38) {\n return 'image/gif';\n }\n\n // WebP: RIFF .... WEBP (check positions 0-3 for RIFF, 8-11 for WEBP)\n if (bytes.length >= 12 &&\n bytes[0] === 0x52 && bytes[1] === 0x49 && bytes[2] === 0x46 && bytes[3] === 0x46 &&\n bytes[8] === 0x57 && bytes[9] === 0x45 && bytes[10] === 0x42 && bytes[11] === 0x50) {\n return 'image/webp';\n }\n\n // PDF: %PDF (25 50 44 46)\n if (bytes[0] === 0x25 && bytes[1] === 0x50 && bytes[2] === 0x44 && bytes[3] === 0x46) {\n return 'application/pdf';\n }\n\n // TIFF: Little-endian (49 49 2A 00) or Big-endian (4D 4D 00 2A)\n if ((bytes[0] === 0x49 && bytes[1] === 0x49 && bytes[2] === 0x2A && bytes[3] === 0x00) ||\n (bytes[0] === 0x4D && bytes[1] === 0x4D && bytes[2] === 0x00 && bytes[3] === 0x2A)) {\n return 'image/tiff';\n }\n\n // BMP: 42 4D (BM)\n if (bytes[0] === 0x42 && bytes[1] === 0x4D) {\n return 'image/bmp';\n }\n\n // RTF: 7B 5C 72 74 66 ({\\rtf)\n if (bytes[0] === 0x7B && bytes[1] === 0x5C && bytes[2] === 0x72 && bytes[3] === 0x74 && bytes[4] === 0x66) {\n return 'application/rtf';\n }\n\n // ZIP-based formats: 50 4B 03 04 (PK..)\n // This includes DOCX, XLSX, PPTX, ODT, ODS, ODP, EPUB\n // We return a generic marker - actual type should be determined by file extension\n if (bytes[0] === 0x50 && bytes[1] === 0x4B && bytes[2] === 0x03 && bytes[3] === 0x04) {\n return 'application/zip';\n }\n\n // MS Office Compound Document (DOC, XLS, PPT): D0 CF 11 E0 A1 B1 1A E1\n if (bytes.length >= 8 &&\n bytes[0] === 0xD0 && bytes[1] === 0xCF && bytes[2] === 0x11 && bytes[3] === 0xE0 &&\n bytes[4] === 0xA1 && bytes[5] === 0xB1 && bytes[6] === 0x1A && bytes[7] === 0xE1) {\n // Could be DOC, XLS, or PPT - return generic Office type\n return 'application/x-cfb'; // Compound File Binary\n }\n\n // Unknown format\n throw new Error(\n `Unsupported file format. Magic bytes: ${Array.from(bytes.slice(0, 4))\n .map(b => b.toString(16).padStart(2, '0'))\n .join(' ')}`\n );\n}\n\n/**\n * Validates that declared MIME type matches actual file data.\n *\n * @param base64Data - Base64 string (with or without data URI prefix)\n * @param declaredMimeType - MIME type that was declared/expected\n * @returns Object with validation result and actual MIME type\n *\n * @example\n * ```typescript\n * const result = validateMimeType(base64Data, \"image/jpeg\");\n * if (!result.isValid) {\n * console.warn(`MIME mismatch: declared ${result.declaredMimeType}, actual ${result.actualMimeType}`);\n * }\n * ```\n */\nexport function validateMimeType(\n base64Data: string,\n declaredMimeType: string\n): { isValid: boolean; actualMimeType: string; declaredMimeType: string } {\n const actualMimeType = detectMimeTypeFromBase64(base64Data);\n return {\n isValid: actualMimeType === declaredMimeType,\n actualMimeType,\n declaredMimeType\n };\n}\n\n/**\n * Async version of validateMimeType using file-type for comprehensive detection.\n */\nexport async function validateMimeTypeAsync(\n base64Data: string,\n declaredMimeType: string\n): Promise<{ isValid: boolean; actualMimeType: string; declaredMimeType: string }> {\n const actualMimeType = await detectMimeTypeFromBase64Async(base64Data);\n return {\n isValid: actualMimeType === declaredMimeType,\n actualMimeType,\n declaredMimeType\n };\n}\n\n/**\n * Extracts base64 data from a data URI or returns the data as-is if already base64.\n *\n * @param data - Data URI or base64 string\n * @returns Pure base64 string without prefix\n *\n * @example\n * ```typescript\n * extractBase64(\"data:image/jpeg;base64,/9j/4AAQ...\") // \"/9j/4AAQ...\"\n * extractBase64(\"/9j/4AAQ...\") // \"/9j/4AAQ...\"\n * ```\n */\nexport function extractBase64(data: string): string {\n if (data.startsWith('data:')) {\n const commaIndex = data.indexOf(',');\n if (commaIndex === -1) {\n throw new Error('Invalid data URI: missing comma separator');\n }\n return data.substring(commaIndex + 1);\n }\n return data;\n}\n","/**\n * File utilities for universal runtime (Edge Runtime + Node.js compatible)\n *\n * These utilities work in both Edge Runtime and Node.js environments.\n * File system operations have been removed for Edge Runtime compatibility.\n */\n\nimport { validateUrl } from '../security/url-validator';\nimport { fetchWithTimeout, validateFileSize, DEFAULT_LIMITS } from '../security/resource-limits';\nimport { arrayBufferToBase64, createDataUri } from '../runtime/base64.js';\nimport { detectMimeTypeFromBase64 } from '../mime-detection.js';\n\n/**\n * Supported document MIME types that can be detected.\n * This includes all formats supported by at least one provider:\n * - Datalab: PDF, images, Office, OpenDocument, HTML, EPUB\n * - Reducto: PDF, images (incl. HEIC, BMP, PSD), Office, RTF, TXT, CSV\n * - Unsiloed: PDF, images, Office (DOCX, XLSX, PPTX)\n */\nexport type DocumentMimeType =\n // PDF\n | 'application/pdf'\n // Images - common\n | 'image/jpeg'\n | 'image/png'\n | 'image/gif'\n | 'image/webp'\n // Images - additional\n | 'image/tiff'\n | 'image/bmp'\n | 'image/heic'\n | 'image/heif'\n | 'image/vnd.adobe.photoshop' // PSD\n // Microsoft Office\n | 'application/msword' // DOC\n | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' // DOCX\n | 'application/vnd.ms-excel' // XLS\n | 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' // XLSX\n | 'application/vnd.ms-powerpoint' // PPT\n | 'application/vnd.openxmlformats-officedocument.presentationml.presentation' // PPTX\n // OpenDocument formats (Datalab)\n | 'application/vnd.oasis.opendocument.text' // ODT\n | 'application/vnd.oasis.opendocument.spreadsheet' // ODS\n | 'application/vnd.oasis.opendocument.presentation' // ODP\n // Text formats\n | 'text/plain' // TXT\n | 'text/csv' // CSV\n | 'text/html' // HTML\n | 'application/rtf' // RTF\n // Other\n | 'application/epub+zip' // EPUB\n | 'unknown';\n\n/**\n * Detect input type: HTTP URL or data URI\n *\n * Note: File paths are not supported in Edge Runtime.\n * Use ArrayBuffer or data URIs instead.\n */\nfunction detectInputType(input: string): 'data-uri' | 'url' {\n if (input.startsWith('data:')) return 'data-uri';\n if (input.startsWith('http://') || input.startsWith('https://')) return 'url';\n throw new Error(\n 'Edge Runtime does not support file paths. ' +\n 'Use HTTP URLs, data URIs, or pass ArrayBuffer/base64 data directly.\\n' +\n 'Example: await resolveDocument(\"https://example.com/doc.pdf\") or ' +\n 'resolveDocument(\"data:application/pdf;base64,...\")'\n );\n}\n\n/**\n * Extract MIME type from various sources\n */\nfunction detectMimeType(input: string, contentType?: string): string {\n // Try data URI first\n if (input.startsWith('data:')) {\n const match = input.match(/^data:([^;,]+)/);\n if (match) return match[1];\n }\n\n // Try Content-Type header (from HTTP response)\n if (contentType) {\n const match = contentType.match(/^([^;]+)/);\n if (match) return match[1].trim();\n }\n\n // Try file extension (works for paths and URLs)\n const lower = input.toLowerCase();\n // PDF\n if (lower.endsWith('.pdf') || lower.includes('.pdf?')) return 'application/pdf';\n // Images - common\n if (lower.endsWith('.png') || lower.includes('.png?')) return 'image/png';\n if (lower.endsWith('.webp') || lower.includes('.webp?')) return 'image/webp';\n if (lower.endsWith('.jpg') || lower.includes('.jpg?')) return 'image/jpeg';\n if (lower.endsWith('.jpeg') || lower.includes('.jpeg?')) return 'image/jpeg';\n if (lower.endsWith('.gif') || lower.includes('.gif?')) return 'image/gif';\n // Images - additional\n if (lower.endsWith('.tiff') || lower.includes('.tiff?')) return 'image/tiff';\n if (lower.endsWith('.tif') || lower.includes('.tif?')) return 'image/tiff';\n if (lower.endsWith('.bmp') || lower.includes('.bmp?')) return 'image/bmp';\n if (lower.endsWith('.heic') || lower.includes('.heic?')) return 'image/heic';\n if (lower.endsWith('.heif') || lower.includes('.heif?')) return 'image/heif';\n if (lower.endsWith('.psd') || lower.includes('.psd?')) return 'image/vnd.adobe.photoshop';\n // Microsoft Office\n if (lower.endsWith('.doc') || lower.includes('.doc?')) return 'application/msword';\n if (lower.endsWith('.docx') || lower.includes('.docx?')) return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';\n if (lower.endsWith('.xls') || lower.includes('.xls?')) return 'application/vnd.ms-excel';\n if (lower.endsWith('.xlsx') || lower.includes('.xlsx?')) return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';\n if (lower.endsWith('.ppt') || lower.includes('.ppt?')) return 'application/vnd.ms-powerpoint';\n if (lower.endsWith('.pptx') || lower.includes('.pptx?')) return 'application/vnd.openxmlformats-officedocument.presentationml.presentation';\n // OpenDocument formats\n if (lower.endsWith('.odt') || lower.includes('.odt?')) return 'application/vnd.oasis.opendocument.text';\n if (lower.endsWith('.ods') || lower.includes('.ods?')) return 'application/vnd.oasis.opendocument.spreadsheet';\n if (lower.endsWith('.odp') || lower.includes('.odp?')) return 'application/vnd.oasis.opendocument.presentation';\n // Text formats\n if (lower.endsWith('.txt') || lower.includes('.txt?')) return 'text/plain';\n if (lower.endsWith('.csv') || lower.includes('.csv?')) return 'text/csv';\n if (lower.endsWith('.html') || lower.includes('.html?')) return 'text/html';\n if (lower.endsWith('.htm') || lower.includes('.htm?')) return 'text/html';\n if (lower.endsWith('.rtf') || lower.includes('.rtf?')) return 'application/rtf';\n // Other\n if (lower.endsWith('.epub') || lower.includes('.epub?')) return 'application/epub+zip';\n\n // Default to octet-stream\n return 'application/octet-stream';\n}\n\n/**\n * Security limits configuration for file operations\n * @internal\n */\nexport interface FileLimitsConfig {\n /** Maximum file size in bytes (default: 100MB) - ⚠️ WARNING: Increasing this exposes to resource exhaustion attacks */\n maxFileSize?: number;\n /** Request timeout in milliseconds (default: 30s) - ⚠️ WARNING: Decreasing this may cause legitimate requests to fail */\n requestTimeout?: number;\n}\n\n/**\n * Detect document MIME type from various input formats\n *\n * Detection order (first match wins):\n * 1. Data URL prefix (data:application/pdf;base64,...)\n * 2. URL/path file extension (.pdf, .jpg, etc.)\n * 3. Magic bytes in base64 data (fallback for raw base64)\n *\n * @param input - Document input (data URL, URL, file path, or raw base64)\n * @returns Detected MIME type or 'unknown' if detection fails\n *\n * @example\n * ```typescript\n * detectDocumentType('data:application/pdf;base64,...') // 'application/pdf'\n * detectDocumentType('https://example.com/doc.pdf') // 'application/pdf'\n * detectDocumentType('JVBERi0xLjQK...') // 'application/pdf' (magic bytes)\n * detectDocumentType('/9j/4AAQSkZJRg...') // 'image/jpeg' (magic bytes)\n * ```\n */\n/**\n * All known MIME types that can be detected (for data URI prefix matching)\n */\nconst KNOWN_MIME_TYPES: DocumentMimeType[] = [\n // PDF\n 'application/pdf',\n // Images - common\n 'image/jpeg',\n 'image/png',\n 'image/gif',\n 'image/webp',\n // Images - additional\n 'image/tiff',\n 'image/bmp',\n 'image/heic',\n 'image/heif',\n 'image/vnd.adobe.photoshop',\n // Microsoft Office\n 'application/msword',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n 'application/vnd.ms-excel',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'application/vnd.ms-powerpoint',\n 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n // OpenDocument formats\n 'application/vnd.oasis.opendocument.text',\n 'application/vnd.oasis.opendocument.spreadsheet',\n 'application/vnd.oasis.opendocument.presentation',\n // Text formats\n 'text/plain',\n 'text/csv',\n 'text/html',\n 'application/rtf',\n // Other\n 'application/epub+zip',\n];\n\n/**\n * File extension to MIME type mapping\n */\nconst EXTENSION_TO_MIME: Record<string, DocumentMimeType> = {\n // PDF\n '.pdf': 'application/pdf',\n // Images - common\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.png': 'image/png',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n // Images - additional\n '.tiff': 'image/tiff',\n '.tif': 'image/tiff',\n '.bmp': 'image/bmp',\n '.heic': 'image/heic',\n '.heif': 'image/heif',\n '.psd': 'image/vnd.adobe.photoshop',\n // Microsoft Office\n '.doc': 'application/msword',\n '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n '.xls': 'application/vnd.ms-excel',\n '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n '.ppt': 'application/vnd.ms-powerpoint',\n '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n // OpenDocument formats\n '.odt': 'application/vnd.oasis.opendocument.text',\n '.ods': 'application/vnd.oasis.opendocument.spreadsheet',\n '.odp': 'application/vnd.oasis.opendocument.presentation',\n // Text formats\n '.txt': 'text/plain',\n '.csv': 'text/csv',\n '.html': 'text/html',\n '.htm': 'text/html',\n '.rtf': 'application/rtf',\n // Other\n '.epub': 'application/epub+zip',\n};\n\n/**\n * Get file extension from path or URL (handles query strings)\n */\nfunction getExtensionFromPath(path: string): string | null {\n // Remove query string if present\n const pathWithoutQuery = path.split('?')[0];\n const lastDot = pathWithoutQuery.lastIndexOf('.');\n if (lastDot === -1) return null;\n return pathWithoutQuery.slice(lastDot).toLowerCase();\n}\n\nexport function detectDocumentType(input: string | undefined): DocumentMimeType {\n if (!input) return 'unknown';\n\n // 1. Check data URL MIME type prefix\n if (input.startsWith('data:')) {\n const match = input.match(/^data:([^;,]+)/);\n if (match) {\n const mimeType = match[1] as DocumentMimeType;\n if (KNOWN_MIME_TYPES.includes(mimeType)) {\n return mimeType;\n }\n }\n // Unknown data URL type, try magic bytes below\n }\n\n // 2. Check URL/path file extension (only if not a data URL)\n if (!input.startsWith('data:')) {\n let ext: string | null = null;\n\n try {\n const url = new URL(input);\n ext = getExtensionFromPath(url.pathname);\n } catch {\n // Not a valid URL, try as file path\n ext = getExtensionFromPath(input);\n }\n\n if (ext && ext in EXTENSION_TO_MIME) {\n return EXTENSION_TO_MIME[ext];\n }\n }\n\n // 3. Magic byte detection (fallback for raw base64 or unknown data URLs)\n try {\n const mimeType = detectMimeTypeFromBase64(input);\n // Check if it's a known type\n if (KNOWN_MIME_TYPES.includes(mimeType as DocumentMimeType)) {\n return mimeType as DocumentMimeType;\n }\n } catch {\n // Magic byte detection failed\n }\n\n return 'unknown';\n}\n\n/**\n * Check if input represents a PDF document\n *\n * Handles various input formats:\n * - Data URLs with MIME type\n * - File paths with .pdf extension\n * - HTTP/HTTPS URLs (with or without query parameters)\n * - Raw base64 strings (detected via magic bytes)\n *\n * @param input - Document input (data URL, file path, URL, or raw base64)\n * @returns true if input appears to be a PDF\n *\n * @example\n * ```typescript\n * isPDFDocument('data:application/pdf;base64,...') // true\n * isPDFDocument('./document.pdf') // true\n * isPDFDocument('https://example.com/doc.pdf?token=123') // true\n * isPDFDocument('JVBERi0xLjQK...') // true (raw base64 PDF)\n * isPDFDocument('data:image/jpeg;base64,...') // false\n * ```\n */\nexport function isPDFDocument(input: string | undefined): boolean {\n return detectDocumentType(input) === 'application/pdf';\n}\n\n/**\n * Resolve document from any source (URL or data URI) to base64 data URL\n *\n * Supports two input types:\n * - HTTP/HTTPS URLs: 'https://example.com/document.pdf'\n * - Data URIs: 'data:application/pdf;base64,JVBERi0x...'\n *\n * Note: File paths are NOT supported in Edge Runtime.\n * Use HTTP URLs, data URIs, or pass ArrayBuffer/base64 directly.\n *\n * @param input - Document source (URL or data URI)\n * @param limits - Optional security limits for file size and request timeout (uses secure defaults if not specified)\n * @returns Promise resolving to base64 data URL\n *\n * @example\n * ```typescript\n * // Remote URL\n * const dataUrl = await resolveDocument('https://example.com/doc.pdf');\n *\n * // Remote URL with custom timeout\n * const dataUrl = await resolveDocument('https://example.com/doc.pdf', { requestTimeout: 60000 });\n *\n * // Data URI (pass-through)\n * const dataUrl = await resolveDocument('data:application/pdf;base64,JVBERi0x...');\n *\n * // For ArrayBuffer, use bufferToDataUri() instead\n * const dataUrl = bufferToDataUri(arrayBuffer, 'application/pdf');\n * ```\n */\nexport async function resolveDocument(input: string, limits?: FileLimitsConfig): Promise<string> {\n const inputType = detectInputType(input);\n\n switch (inputType) {\n case 'data-uri':\n // Already in data URI format - validate and return\n if (!input.match(/^data:[^;,]+;base64,/)) {\n throw new Error('Invalid data URI format. Expected: data:<mimetype>;base64,<data>');\n }\n return input;\n\n case 'url':\n // Fetch from HTTP/HTTPS with security validations\n try {\n // Validate URL for SSRF attacks\n validateUrl(input);\n\n // Use custom timeout or default\n const timeout = limits?.requestTimeout ?? DEFAULT_LIMITS.REQUEST_TIMEOUT;\n\n // Fetch with timeout protection\n const response = await fetchWithTimeout(input, {}, timeout);\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n // Check content-length header if available\n const contentLength = response.headers.get('content-length');\n if (contentLength) {\n const maxSize = limits?.maxFileSize ?? DEFAULT_LIMITS.MAX_FILE_SIZE;\n validateFileSize(parseInt(contentLength, 10), maxSize);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n\n // Validate actual downloaded size\n const maxSize = limits?.maxFileSize ?? DEFAULT_LIMITS.MAX_FILE_SIZE;\n validateFileSize(arrayBuffer.byteLength, maxSize);\n\n const base64 = arrayBufferToBase64(arrayBuffer);\n const mimeType = detectMimeType(input, response.headers.get('content-type') || undefined);\n return `data:${mimeType};base64,${base64}`;\n } catch (error) {\n throw new Error(`Failed to fetch URL ${input}: ${(error as Error).message}`);\n }\n }\n}\n\n/**\n * Convert ArrayBuffer or Uint8Array to base64 data URI\n *\n * Edge Runtime compatible - no file system access required.\n *\n * @param buffer - File buffer (ArrayBuffer or Uint8Array)\n * @param mimeType - MIME type (e.g., 'application/pdf', 'image/jpeg')\n * @returns Base64 data URI string\n *\n * @example\n * ```typescript\n * // From ArrayBuffer\n * const buffer = await response.arrayBuffer();\n * const dataUri = bufferToDataUri(buffer, 'application/pdf');\n *\n * // From Uint8Array\n * const bytes = new Uint8Array([72, 101, 108, 108, 111]);\n * const dataUri = bufferToDataUri(bytes, 'text/plain');\n * ```\n */\nexport function bufferToDataUri(buffer: ArrayBuffer | Uint8Array, mimeType: string): string {\n if (buffer instanceof Uint8Array) {\n // Convert Uint8Array to ArrayBuffer\n const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength) as ArrayBuffer;\n return createDataUri(arrayBuffer, mimeType);\n }\n return createDataUri(buffer, mimeType);\n}\n\n/**\n * @deprecated Use bufferToDataUri() instead. This function will be removed in v0.2.0.\n */\nexport function bufferToBase64(buffer: ArrayBuffer | Uint8Array, mimeType: string): string {\n return bufferToDataUri(buffer, mimeType);\n}\n\n/**\n * Accepted MIME types for flow input validation.\n * Excludes 'unknown' - only known provider-supported formats.\n */\nexport type AcceptedMimeType = Exclude<DocumentMimeType, 'unknown'>;\n\n/**\n * Error thrown when flow input doesn't match accepted formats\n */\nexport class FlowInputValidationError extends Error {\n /**\n * @param message - Human-readable error message\n * @param detectedType - The actual MIME type detected from the input\n * @param acceptedTypes - List of MIME types that would have been accepted\n */\n constructor(\n message: string,\n public readonly detectedType: string,\n public readonly acceptedTypes: string[]\n ) {\n super(message);\n this.name = 'FlowInputValidationError';\n // Maintain proper prototype chain for instanceof checks\n Object.setPrototypeOf(this, FlowInputValidationError.prototype);\n }\n}\n\n/**\n * Validate flow input against accepted MIME type formats\n *\n * @param input - Flow input string (base64, data URL, or URL)\n * @param acceptedFormats - List of accepted MIME types\n * @returns The detected MIME type if valid\n * @throws FlowInputValidationError if format doesn't match accepted types\n *\n * @example\n * ```typescript\n * // Validate PDF only\n * const mimeType = validateFlowInputFormat(pdfBase64, ['application/pdf']);\n *\n * // Validate images only\n * const mimeType = validateFlowInputFormat(jpgBase64, ['image/jpeg', 'image/png']);\n *\n * // Will throw FlowInputValidationError if input is a PDF but only images accepted\n * ```\n */\nexport function validateFlowInputFormat(\n input: string | undefined,\n acceptedFormats: AcceptedMimeType[]\n): AcceptedMimeType {\n if (!input) {\n throw new FlowInputValidationError(\n 'Flow input is empty or undefined',\n 'undefined',\n acceptedFormats\n );\n }\n\n const detected = detectDocumentType(input);\n\n if (detected === 'unknown') {\n const acceptedList = acceptedFormats.length > 0\n ? `Expected one of: ${acceptedFormats.join(', ')}`\n : 'Unable to determine document format';\n throw new FlowInputValidationError(\n `Unable to detect document format. ${acceptedList}. Ensure the input is a valid document (PDF, JPEG, PNG, GIF, or WebP).`,\n 'unknown',\n acceptedFormats\n );\n }\n\n if (acceptedFormats.length > 0 && !acceptedFormats.includes(detected as AcceptedMimeType)) {\n throw new FlowInputValidationError(\n `Document format '${detected}' is not accepted. Expected one of: ${acceptedFormats.join(', ')}`,\n detected,\n acceptedFormats\n );\n }\n\n return detected as AcceptedMimeType;\n}\n","/**\n * PDF Utilities\n *\n * Edge Runtime compatible PDF manipulation utilities using pdf-lib.\n * These functions work in Node.js, Vercel Edge Functions, Cloudflare Workers, and browsers.\n */\n\nimport { PDFDocument } from 'pdf-lib';\nimport { base64ToArrayBuffer, uint8ArrayToBase64 } from './runtime/base64.js';\nimport type { DocumentIR } from './internal/validation-utils.js';\n\n/**\n * Extract raw base64 data from PDF input.\n * Accepts both data URL format and raw base64 strings.\n *\n * @param input - PDF data URL (data:application/pdf;base64,...) or raw base64 string\n * @returns Raw base64 string without data URL prefix\n */\nfunction extractPDFBase64(input: string): string {\n // If it's a data URL, extract the base64 part\n const dataUrlMatch = input.match(/^data:application\\/pdf;base64,(.+)$/);\n if (dataUrlMatch) {\n return dataUrlMatch[1];\n }\n\n // Otherwise assume it's raw base64\n return input;\n}\n\n/**\n * Get the total number of pages in a PDF document\n *\n * @param input - PDF data URL (data:application/pdf;base64,...) or raw base64 string\n * @returns Total page count\n * @throws {Error} If the input is not valid PDF data\n *\n * @example\n * ```typescript\n * // With data URL\n * const pageCount = await getPDFPageCount('data:application/pdf;base64,JVBERi0...');\n *\n * // With raw base64\n * const pageCount = await getPDFPageCount('JVBERi0xLjQK...');\n *\n * console.log(`PDF has ${pageCount} pages`);\n * ```\n */\nexport async function getPDFPageCount(input: string): Promise<number> {\n const base64Data = extractPDFBase64(input);\n const pdfBytes = base64ToArrayBuffer(base64Data);\n const pdfDoc = await PDFDocument.load(pdfBytes);\n return pdfDoc.getPageCount();\n}\n\n/**\n * Split a PDF into multiple smaller PDFs based on page ranges\n *\n * @param input - PDF data URL (data:application/pdf;base64,...) or raw base64 string\n * @param pageRanges - Array of [startPage, endPage] tuples (1-indexed, inclusive)\n * @returns Array of PDF data URLs, one for each page range\n * @throws {Error} If the input is not valid PDF data or page ranges are invalid\n *\n * @example\n * ```typescript\n * // Split a 10-page PDF into three chunks\n * const chunks = await splitPDFIntoChunks(pdfDataUrl, [\n * [1, 3], // Pages 1-3\n * [4, 7], // Pages 4-7\n * [8, 10] // Pages 8-10\n * ]);\n * console.log(`Created ${chunks.length} PDF chunks`);\n * ```\n */\nexport async function splitPDFIntoChunks(\n input: string,\n pageRanges: Array<[number, number]>\n): Promise<string[]> {\n // Extract base64 data (accepts both data URL and raw base64)\n const base64Data = extractPDFBase64(input);\n const pdfBytes = base64ToArrayBuffer(base64Data);\n\n // Load the PDF\n const pdfDoc = await PDFDocument.load(pdfBytes);\n const totalPages = pdfDoc.getPageCount();\n\n const chunks: string[] = [];\n\n for (const [startPage, endPage] of pageRanges) {\n // Validate page range\n if (startPage < 1 || endPage > totalPages || startPage > endPage) {\n throw new Error(\n `Invalid page range [${startPage}, ${endPage}] for PDF with ${totalPages} pages. ` +\n `Page numbers must be 1-indexed and within bounds.`\n );\n }\n\n // Create new PDF with only these pages\n const chunkDoc = await PDFDocument.create();\n const pagesToCopy = Array.from(\n { length: endPage - startPage + 1 },\n (_, i) => startPage - 1 + i // Convert to 0-indexed\n );\n\n const copiedPages = await chunkDoc.copyPages(pdfDoc, pagesToCopy);\n copiedPages.forEach(page => chunkDoc.addPage(page));\n\n // Serialize to base64 using Edge Runtime compatible adapter\n const chunkBytes = await chunkDoc.save();\n const chunkBase64 = uint8ArrayToBase64(chunkBytes);\n chunks.push(`data:application/pdf;base64,${chunkBase64}`);\n }\n\n return chunks;\n}\n\n/**\n * Get the page count from a DocumentIR, with fallback logic\n *\n * This helper function checks multiple sources for page count:\n * 1. `extras.pageCount` (explicit page count from provider or PDF analysis)\n * 2. `pages.length` (fallback - number of pages in the IR)\n *\n * Note: For Unsiloed provider, `pages.length` represents semantic chunks,\n * not traditional pages. Use `extras.totalSemanticChunks` to distinguish.\n *\n * @param ir - DocumentIR to get page count from\n * @returns Page count (or chunk count for Unsiloed)\n *\n * @example\n * ```typescript\n * const ir = await parseNode.run(pdfUrl, { provider: ocrProvider });\n * const pageCount = getDocumentPageCount(ir);\n * console.log(`Document has ${pageCount} pages`);\n * ```\n */\nexport function getDocumentPageCount(ir: DocumentIR): number {\n // Prefer explicit pageCount from extras\n if (ir.extras?.pageCount !== undefined) {\n return ir.extras.pageCount;\n }\n\n // Fallback to pages array length\n return ir.pages.length;\n}\n\n/**\n * Get total page count across multiple DocumentIR objects (chunked results)\n *\n * For chunked parsing results, this sums up the page counts across all chunks.\n * It respects `extras.pageCount` if available, otherwise uses `pages.length`.\n *\n * @param irArray - Array of DocumentIR objects from chunked parsing\n * @returns Total page count across all chunks\n *\n * @example\n * ```typescript\n * const chunks = await parseNode.run(largePdfUrl, {\n * provider: ocrProvider,\n * chunked: { maxPagesPerChunk: 10 }\n * });\n * const totalPages = getTotalPageCount(chunks);\n * console.log(`Total pages across ${chunks.length} chunks: ${totalPages}`);\n * ```\n */\nexport function getTotalPageCount(irArray: DocumentIR[]): number {\n return irArray.reduce((sum, ir) => sum + getDocumentPageCount(ir), 0);\n}\n\n/**\n * Get comprehensive page-related metadata from a DocumentIR\n *\n * Returns detailed information about page counts, chunk information,\n * and whether the result is chunked or a complete document.\n *\n * @param ir - DocumentIR to analyze\n * @returns Metadata object with page count details\n *\n * @example\n * ```typescript\n * const metadata = getPageCountMetadata(ir);\n * console.log(`Document has ${metadata.pageCount} pages`);\n * if (metadata.isChunked) {\n * console.log(`This is chunk ${metadata.chunkIndex + 1} of ${metadata.totalChunks}`);\n * console.log(`Contains pages ${metadata.pageRange[0]} to ${metadata.pageRange[1]}`);\n * }\n * ```\n */\nexport function getPageCountMetadata(ir: DocumentIR): {\n /** Total page count (or chunk count for Unsiloed) */\n pageCount: number;\n /** Number of pages in the IR (may differ from pageCount for chunked docs) */\n pagesInIR: number;\n /** Whether this is a chunked result */\n isChunked: boolean;\n /** For chunked results: which chunk this is (0-indexed) */\n chunkIndex?: number;\n /** For chunked results: total number of chunks */\n totalChunks?: number;\n /** For chunked results: page range [start, end] (1-indexed, inclusive) */\n pageRange?: [number, number];\n /** For Unsiloed: total semantic chunks */\n totalSemanticChunks?: number;\n /** Whether this is from Unsiloed (semantic chunking, not traditional pages) */\n isSemanticChunking: boolean;\n} {\n const pagesInIR = ir.pages.length;\n const pageCount = ir.extras?.pageCount ?? pagesInIR;\n const isSemanticChunking = ir.extras?.totalSemanticChunks !== undefined;\n const isChunked = ir.extras?.chunkIndex !== undefined && ir.extras?.totalChunks !== undefined;\n\n return {\n pageCount,\n pagesInIR,\n isChunked,\n chunkIndex: ir.extras?.chunkIndex as number | undefined,\n totalChunks: ir.extras?.totalChunks as number | undefined,\n pageRange: ir.extras?.pageRange as [number, number] | undefined,\n totalSemanticChunks: ir.extras?.totalSemanticChunks as number | undefined,\n isSemanticChunking\n };\n}\n","/**\n * Provider Configuration\n *\n * Serializable provider configurations for doclo-sdk.\n * These configs can be stored in databases and reconstructed at runtime.\n */\n\n/**\n * Type for dynamically imported module with potential default export.\n * Used for ESM/CJS interop when dynamically loading provider packages.\n */\ninterface DynamicModuleExports {\n [exportName: string]: unknown;\n default?: Record<string, unknown>;\n}\n\n/**\n * Base provider configuration\n */\nexport type BaseProviderConfig = {\n id: string; // Unique identifier for this provider instance\n name?: string; // Human-readable name\n};\n\n/**\n * VLM (Vision Language Model) provider configuration\n */\nexport type VLMProviderConfig = BaseProviderConfig & {\n type: 'vlm';\n provider: 'openai' | 'anthropic' | 'google' | 'xai' | 'generic-or';\n model: string;\n via?: 'openrouter' | 'native';\n baseUrl?: string;\n // API key stored separately (not serialized)\n};\n\n/**\n * OCR provider configuration\n */\nexport type OCRProviderConfig = BaseProviderConfig & (\n | {\n type: 'ocr';\n provider: 'surya';\n endpoint?: string;\n // API key stored separately\n }\n | {\n type: 'ocr';\n provider: 'marker';\n force_ocr?: boolean;\n use_llm?: boolean;\n // API key stored separately\n }\n);\n\n/**\n * All provider configurations\n */\nexport type ProviderConfig = VLMProviderConfig | OCRProviderConfig;\n\n/**\n * Provider secrets (API keys, credentials)\n * Stored separately from provider configs for security\n */\nexport type ProviderSecrets = Record<string, {\n apiKey?: string;\n /** Additional secret values (e.g., endpoint URLs, tokens) */\n [key: string]: string | undefined;\n}>;\n\n/**\n * Base provider interface - common methods shared by all providers\n */\nexport interface ProviderInstance {\n /** Optional provider name for identification */\n name?: string;\n /** Capabilities of this provider instance */\n capabilities?: Record<string, unknown>;\n}\n\n/**\n * Provider registry - maps provider IDs to instantiated providers\n * Uses a generic constraint to allow type narrowing when the provider type is known\n */\nexport type ProviderRegistry<T extends ProviderInstance = ProviderInstance> = Record<string, T>;\n\n/**\n * Helper to create VLM provider config\n */\nexport function defineVLMProvider(config: Omit<VLMProviderConfig, 'type'>): VLMProviderConfig {\n return {\n type: 'vlm',\n ...config\n };\n}\n\n/**\n * Helper to create Surya OCR provider config\n */\nexport function defineSuryaProvider(config: Omit<Extract<OCRProviderConfig, { provider: 'surya' }>, 'type'>): OCRProviderConfig {\n return {\n type: 'ocr',\n ...config\n };\n}\n\n/**\n * Helper to create Marker OCR provider config\n */\nexport function defineMarkerProvider(config: Omit<Extract<OCRProviderConfig, { provider: 'marker' }>, 'type'>): OCRProviderConfig {\n return {\n type: 'ocr',\n ...config\n };\n}\n\n/**\n * Build a provider instance from config and secrets\n *\n * @param config - Provider configuration\n * @param secrets - Provider secrets (API keys)\n * @returns Provider instance\n *\n * @example\n * ```typescript\n * const config: VLMProviderConfig = {\n * type: 'vlm',\n * id: 'gemini-flash',\n * provider: 'google',\n * model: 'google/gemini-2.5-flash-preview-09-2025',\n * via: 'openrouter'\n * };\n *\n * const secrets: ProviderSecrets = {\n * 'gemini-flash': {\n * apiKey: process.env.OPENROUTER_API_KEY\n * }\n * };\n *\n * const provider = await buildProviderFromConfig(config, secrets);\n * ```\n */\nexport async function buildProviderFromConfig(\n config: ProviderConfig,\n secrets: ProviderSecrets\n): Promise<ProviderInstance> {\n const secret = secrets[config.id];\n\n if (!secret || !secret.apiKey) {\n throw new Error(`API key not found for provider \"${config.id}\"`);\n }\n\n if (config.type === 'vlm') {\n // Dynamic import to avoid build-time dependencies\n try {\n // @ts-ignore - Dynamic import, package may not be installed\n const module: DynamicModuleExports = await import(/* webpackIgnore: true */ '@doclo/providers-llm');\n const createVLMProvider = (module.createVLMProvider || module.default?.createVLMProvider) as\n | ((opts: {\n provider: string;\n model: string;\n apiKey: string;\n via?: 'openrouter';\n baseUrl?: string;\n }) => ProviderInstance)\n | undefined;\n\n if (!createVLMProvider) {\n throw new Error('@doclo/providers-llm does not export createVLMProvider');\n }\n\n return createVLMProvider({\n provider: config.provider,\n model: config.model,\n apiKey: secret.apiKey,\n via: config.via === 'openrouter' ? 'openrouter' : undefined,\n baseUrl: config.baseUrl\n });\n } catch (error) {\n throw new Error(\n `Failed to create VLM provider: ${(error as Error).message}. ` +\n `Make sure @doclo/providers-llm is installed.`\n );\n }\n } else if (config.type === 'ocr') {\n // Dynamic import to avoid build-time dependencies\n try {\n // @ts-ignore - Dynamic import, package may not be installed\n const module: DynamicModuleExports = await import(/* webpackIgnore: true */ '@doclo/providers-datalab');\n\n if (config.provider === 'surya') {\n const suryaProvider = (module.suryaProvider || module.default?.suryaProvider) as\n | ((opts: { endpoint?: string; apiKey: string }) => ProviderInstance)\n | undefined;\n if (!suryaProvider) {\n throw new Error('@doclo/providers-datalab does not export suryaProvider');\n }\n\n return suryaProvider({\n endpoint: config.endpoint,\n apiKey: secret.apiKey\n });\n } else if (config.provider === 'marker') {\n const markerProvider = (module.markerProvider || module.default?.markerProvider) as\n | ((opts: { apiKey: string; force_ocr?: boolean; use_llm?: boolean }) => ProviderInstance)\n | undefined;\n if (!markerProvider) {\n throw new Error('@doclo/providers-datalab does not export markerProvider');\n }\n\n return markerProvider({\n apiKey: secret.apiKey,\n force_ocr: config.force_ocr,\n use_llm: config.use_llm\n });\n } else {\n // This branch is unreachable due to discriminated union, but TypeScript doesn't know that\n const exhaustiveCheck: never = config;\n throw new Error(`Unknown OCR provider: ${(exhaustiveCheck as OCRProviderConfig).provider}`);\n }\n } catch (error) {\n throw new Error(\n `Failed to create OCR provider: ${(error as Error).message}. ` +\n `Make sure @doclo/providers-datalab is installed.`\n );\n }\n } else {\n // This branch is unreachable due to discriminated union, but TypeScript doesn't know that\n const exhaustiveCheck: never = config;\n throw new Error(`Unknown provider type: ${(exhaustiveCheck as ProviderConfig).type}`);\n }\n}\n\n/**\n * Build multiple providers from configs\n *\n * @param configs - Array of provider configurations\n * @param secrets - Provider secrets\n * @returns Provider registry (map of IDs to instances)\n *\n * @example\n * ```typescript\n * const configs: ProviderConfig[] = [\n * { type: 'vlm', id: 'gemini', provider: 'google', model: '...', via: 'openrouter' },\n * { type: 'ocr', id: 'surya', provider: 'surya' }\n * ];\n *\n * const secrets: ProviderSecrets = {\n * 'gemini': { apiKey: process.env.OPENROUTER_API_KEY },\n * 'surya': { apiKey: process.env.SURYA_API_KEY }\n * };\n *\n * const providers = await buildProvidersFromConfigs(configs, secrets);\n * // providers = { gemini: VLMProvider, surya: OCRProvider }\n * ```\n */\nexport async function buildProvidersFromConfigs(\n configs: ProviderConfig[],\n secrets: ProviderSecrets\n): Promise<ProviderRegistry> {\n const registry: ProviderRegistry = {};\n\n for (const config of configs) {\n try {\n registry[config.id] = await buildProviderFromConfig(config, secrets);\n } catch (error) {\n throw new Error(\n `Failed to build provider \"${config.id}\": ${(error as Error).message}`\n );\n }\n }\n\n return registry;\n}\n","/**\n * Provider Identity Types\n *\n * Implements the 3-layer hierarchy for provider identification:\n * 1. Provider (Company/Vendor) - e.g., datalab, openai, anthropic\n * 2. Model - e.g., surya, marker-ocr, claude-sonnet-4.5\n * 3. Method - e.g., native, openrouter, self-hosted\n */\n\n/**\n * Provider vendors (companies)\n * These represent the company or organization providing the service\n */\nexport type ProviderVendor =\n | 'datalab' // Datalab (surya, marker-ocr, marker-vlm)\n | 'reducto' // Reducto (unified document processing)\n | 'unsiloed' // Unsiloed (unified document processing)\n | 'extend' // Extend.ai (document parsing, extraction, classification, splitting)\n | 'openai' // OpenAI (gpt-4.1, o3, o4-mini)\n | 'anthropic' // Anthropic (claude-*)\n | 'google' // Google (gemini-*)\n | 'xai' // xAI (grok-*)\n | 'mistral'; // Mistral AI (ocr-3, pixtral, large, medium, small)\n\n/**\n * Access methods for providers\n * - native: Direct API call to provider's official endpoint\n * - openrouter: Via OpenRouter aggregator (LLM only)\n * - self-hosted: Self-hosted instance (e.g., pip install surya-ocr)\n */\nexport type AccessMethod = 'native' | 'openrouter' | 'self-hosted';\n\n/**\n * Complete provider identity combining all three layers\n */\nexport interface ProviderIdentity {\n /** The company/vendor (e.g., 'datalab') */\n readonly provider: ProviderVendor;\n\n /** The specific model/version (e.g., 'surya', 'marker-vlm') */\n readonly model: string;\n\n /** How the provider is accessed (e.g., 'native', 'self-hosted') */\n readonly method: AccessMethod;\n}\n\n/**\n * Convert provider identity to canonical string format\n * Format: \"provider:model\" (e.g., \"datalab:surya\")\n *\n * @example\n * ```typescript\n * toProviderString({ provider: 'datalab', model: 'surya', method: 'native' })\n * // => \"datalab:surya\"\n * ```\n */\nexport function toProviderString(identity: ProviderIdentity): string {\n return `${identity.provider}:${identity.model}`;\n}\n\n/**\n * Parse canonical provider string back to partial identity\n * Note: method cannot be determined from string alone\n *\n * @example\n * ```typescript\n * parseProviderString(\"datalab:surya\")\n * // => { provider: 'datalab', model: 'surya' }\n * ```\n */\nexport function parseProviderString(str: string): { provider: string; model: string } {\n const colonIndex = str.indexOf(':');\n if (colonIndex === -1) {\n // Legacy format: just model name (e.g., \"surya\")\n return { provider: str, model: str };\n }\n return {\n provider: str.slice(0, colonIndex),\n model: str.slice(colonIndex + 1)\n };\n}\n\n/**\n * Check if an endpoint appears to be self-hosted\n * Used to determine the access method for OCR providers\n */\nexport function isLocalEndpoint(endpoint?: string): boolean {\n if (!endpoint) return false;\n return (\n endpoint.includes('localhost') ||\n endpoint.includes('127.0.0.1') ||\n endpoint.includes('0.0.0.0') ||\n endpoint.startsWith('http://192.168.') ||\n endpoint.startsWith('http://10.')\n );\n}\n\n/**\n * Create a provider identity with inferred method\n *\n * @param provider - The vendor/company\n * @param model - The model name\n * @param opts - Options including endpoint for method inference\n */\nexport function createIdentity(\n provider: ProviderVendor,\n model: string,\n opts?: { endpoint?: string; via?: 'openrouter' | 'native' }\n): ProviderIdentity {\n let method: AccessMethod = 'native';\n\n if (opts?.via === 'openrouter') {\n method = 'openrouter';\n } else if (isLocalEndpoint(opts?.endpoint)) {\n method = 'self-hosted';\n }\n\n return { provider, model, method };\n}\n","/**\n * Unified Provider Query Interface\n *\n * Provides a unified way to query and filter provider metadata across\n * all provider packages (@doclo/providers-llm, @doclo/providers-datalab).\n *\n * @example\n * ```typescript\n * import { queryProviders, registerProviderMetadata } from '@doclo/core';\n *\n * // Register metadata from provider packages (done automatically if packages are imported)\n * import { PROVIDER_METADATA as LLM_METADATA } from '@doclo/providers-llm';\n * import { PROVIDER_METADATA as DATALAB_METADATA } from '@doclo/providers-datalab';\n *\n * registerProviderMetadata('llm', LLM_METADATA);\n * registerProviderMetadata('datalab', DATALAB_METADATA);\n *\n * // Query providers\n * const pdfProviders = queryProviders({ supports: { pdfs: true } });\n * const cheapProviders = queryProviders({ maxCostPerPage: 0.01 });\n * const largeFileProviders = queryProviders({ minFileSize: 100 }); // 100 MB+\n * ```\n */\n\n// ============================================================================\n// Input Requirements Types\n// ============================================================================\n\n/**\n * Input type requirements for providers/models.\n * More normalized than a boolean - allows for future extensibility.\n *\n * - 'raw-document': Needs FlowInput with base64/url (OCR/VLM providers like marker-vlm)\n * - 'parsed-text': Needs DocumentIR text output from parse step (text-only processors)\n * - 'any': Can work with either (most vision LLMs like GPT-4o, Claude with vision)\n */\nexport type ProviderInputType = 'raw-document' | 'parsed-text' | 'any';\n\n/**\n * Input requirements specification for a provider or model.\n * Determines what form of document input is expected.\n */\nexport type InputRequirements = {\n /**\n * What type of input this provider accepts.\n * - 'raw-document': Needs PDF/image bytes directly (marker-vlm, reducto-extract)\n * - 'parsed-text': Needs DocumentIR text (text-only processors)\n * - 'any': Can work with either (vision LLMs like GPT-4o, Claude)\n */\n inputType: ProviderInputType;\n\n /**\n * Accepted input methods when inputType is 'raw-document'.\n * Inherited from inputFormats.inputMethods if not specified.\n */\n acceptedMethods?: readonly ('url' | 'base64' | 'fileId')[];\n};\n\n// ============================================================================\n// Normalized Capabilities and Features\n// ============================================================================\n\n/**\n * Output format support flags\n */\nexport type OutputFormatSupport = {\n text: boolean;\n markdown: boolean;\n html: boolean;\n json: boolean;\n};\n\n/**\n * Feature status values for normalized features.\n * - `true`: Natively supported by the API\n * - `false`: Not supported\n * - `'deprecated'`: API deprecated this feature, may not work\n * - `'derived'`: SDK provides via transformation (e.g., maxPages from pageRange)\n */\nexport type FeatureStatus = true | false | 'deprecated' | 'derived';\n\n/**\n * Helper to check if a feature is enabled (true, deprecated, or derived)\n */\nexport function isFeatureEnabled(status: FeatureStatus): boolean {\n return status === true || status === 'deprecated' || status === 'derived';\n}\n\n/**\n * Page indexing convention used by provider\n */\nexport type PageIndexing = '0-indexed' | '1-indexed';\n\n/**\n * Normalized features across all providers.\n * Maps provider-specific option names to unified names.\n *\n * This enables UIs to query \"what features does this provider support?\"\n * and get a consistent answer across all providers.\n */\nexport type NormalizedFeatures = {\n // === Page Selection ===\n /** Limit to first N pages */\n maxPages: FeatureStatus;\n /** Specific page range selection */\n pageRange: FeatureStatus;\n\n // === Language ===\n /** OCR language hints (maps from 'langs') */\n languageHints: FeatureStatus;\n\n // === Processing Mode ===\n /** Quality/speed modes (fast/balanced/high_accuracy) */\n processingModes: FeatureStatus;\n /** Reducto agentic mode (higher accuracy, more cost) */\n agenticMode: FeatureStatus;\n\n // === Content Enhancement ===\n /** Custom prompts (maps from blockCorrectionPrompt, additionalPrompt, systemPrompt) */\n customPrompts: FeatureStatus;\n\n // === Output Features ===\n /** Extract embedded images (maps from extractImages, returnImages) */\n imageExtraction: FeatureStatus;\n /** Page delimiters (maps from paginate, addPageMarkers) */\n pageMarkers: FeatureStatus;\n /** Field-level citations with source references (page/char/block indices) */\n citations: FeatureStatus;\n /** Document chunking modes (RAG-optimized) */\n chunking: FeatureStatus;\n /** Auto-segmentation for multi-document PDFs */\n segmentation: FeatureStatus;\n\n // === OCR-Specific ===\n /** Re-run OCR on already-OCR'd documents */\n stripExistingOCR: FeatureStatus;\n /** Format lines in output */\n formatLines: FeatureStatus;\n /** Force OCR even if text layer exists */\n forceOCR: FeatureStatus;\n\n // === Table Handling ===\n /** Table format options (html/json/md/csv) */\n tableOutputFormats: FeatureStatus;\n /** Merge consecutive tables */\n tableMerging: FeatureStatus;\n\n // === Quality/Accuracy ===\n /** Block-level confidence scores */\n confidence: FeatureStatus;\n /** Bounding box coordinates for TEXT elements (pixel/normalized coords) */\n boundingBoxes: FeatureStatus;\n /** Bounding box coordinates for IMAGES/FIGURES only (not text) */\n imageBoundingBoxes: FeatureStatus;\n /** JSON schema validation for structured output */\n schemaValidation: FeatureStatus;\n /** Handwritten text recognition support */\n handwrittenText: FeatureStatus;\n /** Separate header/footer extraction from main content */\n headerFooterExtraction: FeatureStatus;\n\n // === NEW: Extended Features ===\n /** Optimize output for embeddings/RAG */\n embedOptimized: FeatureStatus;\n /** Handle encrypted/password-protected PDFs */\n passwordProtected: FeatureStatus;\n /** Filter block types (headers, footers, page numbers, etc.) */\n contentFiltering: FeatureStatus;\n /** OCR system/mode selection (standard/legacy, auto/full) */\n ocrMode: FeatureStatus;\n /** Async completion webhook callbacks */\n webhookCallback: FeatureStatus;\n /** Vision quality control (low/medium/high) - Gemini */\n mediaResolution: FeatureStatus;\n /** Track changes extraction from Word docs */\n changeTracking: FeatureStatus;\n /** Extract hyperlinks from documents */\n hyperlinkExtraction: FeatureStatus;\n /** Enhanced chart and graph interpretation (Datalab extras=chart_understanding) */\n chartUnderstanding: FeatureStatus;\n /** Control image caption generation (Datalab disable_image_captions) */\n imageCaptions: FeatureStatus;\n /** Extract signatures from documents (Reducto include: [\"signatures\"]) */\n signatureExtraction: FeatureStatus;\n /** Extract comments/annotations from documents (Reducto include: [\"comments\"]) */\n commentExtraction: FeatureStatus;\n /** Extract highlighted text from documents (Reducto include: [\"highlight\"]) */\n highlightExtraction: FeatureStatus;\n /** Summarize figures/charts with VLM (Reducto summarize_figures) */\n figureSummaries: FeatureStatus;\n\n // === Output Formats ===\n /** Supported output formats */\n outputFormats: OutputFormatSupport;\n};\n\n// ============================================================================\n// Normalized Provider Metadata\n// ============================================================================\n\nimport type { ProviderIdentity, ProviderVendor, AccessMethod } from './provider-identity.js';\n\n// Normalized provider metadata that works across all provider types\nexport type NormalizedProviderMetadata = {\n // Identity - legacy fields\n id: string;\n name: string;\n source: 'llm' | 'datalab' | 'unsiloed' | 'reducto' | string;\n type: 'LLM' | 'OCR' | 'VLM' | 'Split';\n\n // NEW: 3-layer identity (provider/model/method)\n identity?: {\n /** Provider vendor (company) */\n provider: ProviderVendor | string;\n /** Model identifier */\n model: string;\n /** Access method (native, openrouter, self-hosted) */\n method?: AccessMethod;\n };\n\n // Capabilities (high-level, queryable)\n capabilities: {\n // === Existing ===\n supportsImages: boolean;\n supportsPDFs: boolean;\n supportsDocuments: boolean; // Word, Excel, PowerPoint\n supportsReasoning: boolean;\n supportsStructuredOutput: boolean;\n\n // === NEW: Processing Features ===\n supportsPrompts: boolean; // Custom prompts/instructions\n supportsCitations: boolean; // Field-level citations\n supportsChunking: boolean; // Document chunking (RAG-optimized)\n supportsImageExtraction: boolean; // Extract embedded images\n supportsPageMarkers: boolean; // Add page delimiters to output\n supportsLanguageHints: boolean; // Language hints for OCR\n supportsProcessingModes: boolean; // Quality/speed modes\n supportsSegmentation: boolean; // Auto-segment multi-doc PDFs\n\n // === NEW: Output Formats ===\n outputFormats: OutputFormatSupport;\n };\n\n // Features - fine-grained capability flags for UI\n features: NormalizedFeatures;\n\n // Input requirements (what type of input the provider needs)\n inputRequirements: InputRequirements;\n\n // Node compatibility\n compatibleNodes: {\n parse: boolean;\n extract: boolean;\n categorize: boolean;\n qualify: boolean;\n split: boolean;\n };\n\n // Input formats\n inputFormats: {\n imageMimeTypes: readonly string[];\n documentMimeTypes: readonly string[];\n inputMethods: readonly ('url' | 'base64' | 'fileId')[];\n maxImageSize?: number; // MB\n maxPdfSize?: number; // MB\n maxFileSize?: number; // MB (general)\n maxPages?: number;\n };\n\n // Pricing (normalized)\n pricing: {\n model: 'per-token' | 'per-page';\n // Per-token pricing (LLM)\n inputPer1kTokens?: number;\n outputPer1kTokens?: number;\n // Per-page pricing (OCR/VLM)\n perPage?: number;\n currency: 'USD';\n notes?: string;\n };\n\n // Rate limits\n rateLimits?: {\n requestsPerMinute?: number;\n docsPerMinute?: number;\n };\n\n // Raw metadata for advanced use\n raw: unknown;\n};\n\n/**\n * Feature names that can be queried (excludes outputFormats which is nested)\n */\nexport type FeatureName = Exclude<keyof NormalizedFeatures, 'outputFormats'>;\n\n// Query filters\nexport type ProviderQueryFilter = {\n // Filter by source (legacy)\n source?: 'llm' | 'datalab' | 'unsiloed' | 'reducto' | string | string[];\n\n // Filter by type\n type?: 'LLM' | 'OCR' | 'VLM' | 'Split' | ('LLM' | 'OCR' | 'VLM' | 'Split')[];\n\n // NEW: Filter by 3-layer identity\n /** Filter by provider vendor (company) */\n provider?: ProviderVendor | ProviderVendor[] | string | string[];\n /** Filter by model ID (requires provider to be specified for best results) */\n model?: string | string[];\n /** Filter by access method */\n method?: AccessMethod | AccessMethod[];\n\n // Filter by capabilities\n supports?: {\n images?: boolean;\n pdfs?: boolean;\n documents?: boolean;\n reasoning?: boolean;\n structuredOutput?: boolean;\n // NEW: Extended capability filters\n prompts?: boolean;\n citations?: boolean;\n chunking?: boolean;\n imageExtraction?: boolean;\n pageMarkers?: boolean;\n languageHints?: boolean;\n processingModes?: boolean;\n segmentation?: boolean;\n };\n\n // NEW: Filter by specific features (all must be supported)\n hasFeatures?: FeatureName[];\n\n // NEW: Filter by output format support\n outputFormat?: 'text' | 'markdown' | 'html' | 'json';\n\n // Filter by input requirements\n inputRequirements?: {\n /**\n * Filter by input type requirement.\n * - 'raw-document': Only providers that need raw document input\n * - 'parsed-text': Only providers that need parsed text\n * - 'any': Only providers that accept any input type\n * - ['raw-document', 'any']: Providers that accept raw documents (raw-document OR any)\n */\n inputType?: ProviderInputType | ProviderInputType[];\n };\n\n // Filter by node compatibility\n compatibleWith?: ('parse' | 'extract' | 'categorize' | 'qualify' | 'split')[];\n\n // Filter by MIME type support\n mimeType?: string | string[];\n\n // Filter by file size limits (MB)\n minFileSize?: number; // Provider must support at least this size\n maxFileSize?: number; // Provider must have limit at most this size\n\n // Filter by pricing\n maxCostPerPage?: number; // For OCR/VLM\n maxCostPer1kTokens?: number; // For LLM (input)\n\n // Custom filter function\n filter?: (provider: NormalizedProviderMetadata) => boolean;\n};\n\n// Registry for provider metadata\nconst providerRegistry = new Map<string, Map<string, NormalizedProviderMetadata>>();\n\n/**\n * Register provider metadata from a provider package\n *\n * @param source - Source identifier (e.g., 'llm', 'datalab')\n * @param metadata - Raw metadata object from the provider package\n * @param normalizer - Function to normalize the metadata\n *\n * @example\n * ```typescript\n * import { PROVIDER_METADATA } from '@doclo/providers-llm';\n * registerProviderMetadata('llm', PROVIDER_METADATA, normalizeLLMMetadata);\n * ```\n */\nexport function registerProviderMetadata(\n source: string,\n metadata: Record<string, unknown>,\n normalizer?: (id: string, data: unknown, source: string) => NormalizedProviderMetadata\n): void {\n const normalized = new Map<string, NormalizedProviderMetadata>();\n\n for (const [id, data] of Object.entries(metadata)) {\n if (normalizer) {\n normalized.set(id, normalizer(id, data, source));\n } else {\n // Use default normalizer based on source\n normalized.set(id, defaultNormalizer(id, data, source));\n }\n }\n\n providerRegistry.set(source, normalized);\n}\n\n/**\n * Get all registered providers (normalized)\n */\nexport function getAllProviders(): NormalizedProviderMetadata[] {\n const all: NormalizedProviderMetadata[] = [];\n for (const providers of providerRegistry.values()) {\n all.push(...providers.values());\n }\n return all;\n}\n\n/**\n * Query providers with filters\n *\n * @param filter - Query filters\n * @returns Array of matching providers\n *\n * @example\n * ```typescript\n * // Get all providers that support PDFs\n * const pdfProviders = queryProviders({ supports: { pdfs: true } });\n *\n * // Get cheap OCR providers\n * const cheapOcr = queryProviders({\n * type: 'OCR',\n * maxCostPerPage: 0.02\n * });\n *\n * // Get providers that can handle large files\n * const largeFileProviders = queryProviders({ minFileSize: 100 });\n *\n * // Get providers compatible with extract() node\n * const extractProviders = queryProviders({\n * compatibleWith: ['extract']\n * });\n * ```\n */\nexport function queryProviders(filter: ProviderQueryFilter = {}): NormalizedProviderMetadata[] {\n let providers = getAllProviders();\n\n // Filter by source\n if (filter.source) {\n const sources = Array.isArray(filter.source) ? filter.source : [filter.source];\n providers = providers.filter(p => sources.includes(p.source));\n }\n\n // Filter by type\n if (filter.type) {\n const types = Array.isArray(filter.type) ? filter.type : [filter.type];\n providers = providers.filter(p => types.includes(p.type));\n }\n\n // NEW: Filter by 3-layer identity\n if (filter.provider) {\n const providerVendors = Array.isArray(filter.provider) ? filter.provider : [filter.provider];\n providers = providers.filter(p => p.identity?.provider && providerVendors.includes(p.identity.provider));\n }\n\n if (filter.model) {\n const models = Array.isArray(filter.model) ? filter.model : [filter.model];\n providers = providers.filter(p => p.identity?.model && models.includes(p.identity.model));\n }\n\n if (filter.method) {\n const methods = Array.isArray(filter.method) ? filter.method : [filter.method];\n providers = providers.filter(p => p.identity?.method && methods.includes(p.identity.method));\n }\n\n // Filter by capabilities\n if (filter.supports) {\n // Existing capability filters\n if (filter.supports.images !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsImages === filter.supports!.images);\n }\n if (filter.supports.pdfs !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsPDFs === filter.supports!.pdfs);\n }\n if (filter.supports.documents !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsDocuments === filter.supports!.documents);\n }\n if (filter.supports.reasoning !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsReasoning === filter.supports!.reasoning);\n }\n if (filter.supports.structuredOutput !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsStructuredOutput === filter.supports!.structuredOutput);\n }\n // NEW: Extended capability filters\n if (filter.supports.prompts !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsPrompts === filter.supports!.prompts);\n }\n if (filter.supports.citations !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsCitations === filter.supports!.citations);\n }\n if (filter.supports.chunking !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsChunking === filter.supports!.chunking);\n }\n if (filter.supports.imageExtraction !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsImageExtraction === filter.supports!.imageExtraction);\n }\n if (filter.supports.pageMarkers !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsPageMarkers === filter.supports!.pageMarkers);\n }\n if (filter.supports.languageHints !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsLanguageHints === filter.supports!.languageHints);\n }\n if (filter.supports.processingModes !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsProcessingModes === filter.supports!.processingModes);\n }\n if (filter.supports.segmentation !== undefined) {\n providers = providers.filter(p => p.capabilities.supportsSegmentation === filter.supports!.segmentation);\n }\n }\n\n // NEW: Filter by specific features (all must be supported)\n // Uses isFeatureEnabled() to treat 'deprecated' and 'derived' as truthy\n if (filter.hasFeatures && filter.hasFeatures.length > 0) {\n providers = providers.filter(p =>\n filter.hasFeatures!.every(feature => isFeatureEnabled(p.features[feature]))\n );\n }\n\n // NEW: Filter by output format support\n if (filter.outputFormat) {\n providers = providers.filter(p =>\n p.capabilities.outputFormats[filter.outputFormat!] === true\n );\n }\n\n // Filter by input requirements\n if (filter.inputRequirements?.inputType !== undefined) {\n const inputTypes = Array.isArray(filter.inputRequirements.inputType)\n ? filter.inputRequirements.inputType\n : [filter.inputRequirements.inputType];\n providers = providers.filter(p => inputTypes.includes(p.inputRequirements.inputType));\n }\n\n // Filter by node compatibility\n if (filter.compatibleWith && filter.compatibleWith.length > 0) {\n providers = providers.filter(p =>\n filter.compatibleWith!.every(node => p.compatibleNodes[node])\n );\n }\n\n // Filter by MIME type support\n if (filter.mimeType) {\n const mimeTypes = Array.isArray(filter.mimeType) ? filter.mimeType : [filter.mimeType];\n providers = providers.filter(p => {\n const allMimes = [...p.inputFormats.imageMimeTypes, ...p.inputFormats.documentMimeTypes];\n return mimeTypes.every(mime => allMimes.includes(mime));\n });\n }\n\n // Filter by minimum file size support\n if (filter.minFileSize !== undefined) {\n providers = providers.filter(p => {\n const maxSize = p.inputFormats.maxFileSize ??\n Math.max(p.inputFormats.maxImageSize ?? 0, p.inputFormats.maxPdfSize ?? 0);\n return maxSize >= filter.minFileSize!;\n });\n }\n\n // Filter by maximum file size limit\n if (filter.maxFileSize !== undefined) {\n providers = providers.filter(p => {\n const maxSize = p.inputFormats.maxFileSize ??\n Math.max(p.inputFormats.maxImageSize ?? Infinity, p.inputFormats.maxPdfSize ?? Infinity);\n return maxSize <= filter.maxFileSize!;\n });\n }\n\n // Filter by cost\n if (filter.maxCostPerPage !== undefined) {\n providers = providers.filter(p =>\n p.pricing.perPage !== undefined && p.pricing.perPage <= filter.maxCostPerPage!\n );\n }\n\n if (filter.maxCostPer1kTokens !== undefined) {\n providers = providers.filter(p =>\n p.pricing.inputPer1kTokens !== undefined && p.pricing.inputPer1kTokens <= filter.maxCostPer1kTokens!\n );\n }\n\n // Custom filter\n if (filter.filter) {\n providers = providers.filter(filter.filter);\n }\n\n return providers;\n}\n\n/**\n * Get a single provider by ID\n */\nexport function getProviderById(id: string): NormalizedProviderMetadata | undefined {\n for (const providers of providerRegistry.values()) {\n if (providers.has(id)) {\n return providers.get(id);\n }\n }\n return undefined;\n}\n\n/**\n * Get providers by source\n */\nexport function getProvidersBySource(source: string): NormalizedProviderMetadata[] {\n const providers = providerRegistry.get(source);\n return providers ? [...providers.values()] : [];\n}\n\n/**\n * Clear all registered providers (useful for testing)\n */\nexport function clearProviderRegistry(): void {\n providerRegistry.clear();\n}\n\n// Default normalizer that handles LLM, Datalab, Reducto, and Unsiloed metadata formats\nfunction defaultNormalizer(id: string, data: unknown, source: string): NormalizedProviderMetadata {\n const d = data as Record<string, any>;\n\n if (source === 'llm') {\n return normalizeLLMProvider(id, d);\n } else if (source === 'datalab') {\n return normalizeDatalabProvider(id, d);\n } else if (source === 'reducto') {\n return normalizeReductoProvider(id, d);\n } else if (source === 'unsiloed') {\n return normalizeUnsiloedProvider(id, d);\n } else if (source === 'mistral') {\n return normalizeMistralProvider(id, d);\n }\n\n // Generic fallback\n const defaultOutputFormats: OutputFormatSupport = { text: true, markdown: false, html: false, json: false };\n const defaultFeatures: NormalizedFeatures = {\n maxPages: false,\n pageRange: false,\n languageHints: false,\n processingModes: false,\n agenticMode: false,\n customPrompts: false,\n imageExtraction: false,\n pageMarkers: false,\n citations: false,\n chunking: false,\n segmentation: false,\n stripExistingOCR: false,\n formatLines: false,\n forceOCR: false,\n tableOutputFormats: false,\n tableMerging: false,\n confidence: false,\n boundingBoxes: false,\n imageBoundingBoxes: false,\n schemaValidation: false,\n handwrittenText: false,\n headerFooterExtraction: false,\n // Extended features\n embedOptimized: false,\n passwordProtected: false,\n contentFiltering: false,\n ocrMode: false,\n webhookCallback: false,\n mediaResolution: false,\n changeTracking: false,\n hyperlinkExtraction: false,\n chartUnderstanding: false,\n imageCaptions: false,\n signatureExtraction: false,\n commentExtraction: false,\n highlightExtraction: false,\n figureSummaries: false,\n outputFormats: defaultOutputFormats,\n };\n\n return {\n id,\n name: d.name ?? id,\n source,\n type: d.type ?? 'LLM',\n capabilities: {\n supportsImages: d.capabilities?.supportsImages ?? false,\n supportsPDFs: d.capabilities?.supportsPDFs ?? false,\n supportsDocuments: d.capabilities?.supportsDocuments ?? false,\n supportsReasoning: d.capabilities?.supportsReasoning ?? false,\n supportsStructuredOutput: d.capabilities?.supportsStructuredOutput ?? false,\n supportsPrompts: false,\n supportsCitations: false,\n supportsChunking: false,\n supportsImageExtraction: false,\n supportsPageMarkers: false,\n supportsLanguageHints: false,\n supportsProcessingModes: false,\n supportsSegmentation: false,\n outputFormats: defaultOutputFormats,\n },\n features: defaultFeatures,\n inputRequirements: {\n inputType: d.inputRequirements?.inputType ?? 'any',\n acceptedMethods: d.inputRequirements?.acceptedMethods ?? d.inputFormats?.inputMethods ?? ['base64'],\n },\n compatibleNodes: {\n parse: d.compatibleNodes?.parse ?? false,\n extract: d.compatibleNodes?.extract ?? false,\n categorize: d.compatibleNodes?.categorize ?? false,\n qualify: d.compatibleNodes?.qualify ?? false,\n split: d.compatibleNodes?.split ?? false,\n },\n inputFormats: {\n imageMimeTypes: [],\n documentMimeTypes: [],\n inputMethods: ['base64'],\n },\n pricing: {\n model: 'per-token',\n currency: 'USD',\n },\n raw: data,\n };\n}\n\nfunction normalizeLLMProvider(id: string, d: Record<string, any>): NormalizedProviderMetadata {\n // LLM providers can output any format via prompting\n const outputFormats: OutputFormatSupport = {\n text: true,\n markdown: true,\n html: true,\n json: d.capabilities?.supportsStructuredOutput ?? true,\n };\n\n // Extract vendor from id or default to id\n const vendor = d.vendor ?? id;\n\n // LLM features - all LLMs support prompts and flexible output\n // LLMs don't have native pageRange support - they receive full document\n // maxPages can be 'derived' if SDK pre-processes pages before sending\n const features: NormalizedFeatures = {\n maxPages: 'derived' as FeatureStatus, // SDK can limit via pre-processing\n pageRange: false, // No native API support - LLMs receive full text\n languageHints: false, // Not applicable to LLMs\n processingModes: false, // Not applicable to LLMs\n agenticMode: false, // Not applicable to LLMs\n customPrompts: true, // All LLMs support prompts\n imageExtraction: false, // LLMs don't extract images\n pageMarkers: false, // LLMs don't add page markers\n citations: vendor === 'anthropic' ? true : false, // Anthropic has Citations API\n chunking: false, // LLMs don't do chunking\n segmentation: false, // LLMs don't do segmentation\n stripExistingOCR: false,\n formatLines: false,\n forceOCR: false,\n tableOutputFormats: false,\n tableMerging: false,\n confidence: false, // LLMs don't provide confidence scores\n boundingBoxes: false, // LLMs don't provide bounding boxes\n imageBoundingBoxes: false, // LLMs don't provide image bounding boxes (Gemini 2.0+ can via specific prompting, but not a simple toggle)\n schemaValidation: d.capabilities?.supportsStructuredOutput ?? false, // Some LLMs support schema validation\n handwrittenText: false, // Not specific to LLMs\n headerFooterExtraction: false, // LLMs don't extract header/footer separately\n // Extended features\n embedOptimized: false,\n passwordProtected: false,\n contentFiltering: false,\n ocrMode: false,\n webhookCallback: false,\n mediaResolution: vendor === 'google' ? true : false, // Google Gemini has mediaResolution\n changeTracking: false,\n hyperlinkExtraction: false,\n chartUnderstanding: false,\n imageCaptions: false,\n signatureExtraction: false,\n commentExtraction: false,\n highlightExtraction: false,\n figureSummaries: false,\n outputFormats,\n };\n\n return {\n id,\n name: d.name ?? id,\n source: 'llm',\n type: 'LLM',\n // NEW: 3-layer identity\n identity: {\n provider: vendor,\n model: d.defaultModel ?? id,\n method: 'native' as const,\n },\n capabilities: {\n supportsImages: d.capabilities?.supportsImages ?? false,\n supportsPDFs: d.capabilities?.supportsPDFs ?? false,\n supportsDocuments: false, // LLM providers don't support Office docs directly\n supportsReasoning: d.capabilities?.supportsReasoning ?? false,\n supportsStructuredOutput: d.capabilities?.supportsStructuredOutput ?? false,\n // NEW capabilities\n supportsPrompts: true,\n supportsCitations: vendor === 'anthropic', // Anthropic has Citations API\n supportsChunking: false,\n supportsImageExtraction: false,\n supportsPageMarkers: false,\n supportsLanguageHints: false,\n supportsProcessingModes: false,\n supportsSegmentation: false,\n outputFormats,\n },\n features,\n // LLM providers with vision can work with either raw documents or parsed text\n inputRequirements: {\n inputType: d.inputRequirements?.inputType ?? 'any',\n acceptedMethods: d.inputRequirements?.acceptedMethods ?? d.inputFormats?.images?.methods ?? ['base64', 'url'],\n },\n compatibleNodes: {\n parse: d.compatibleNodes?.parse ?? false,\n extract: d.compatibleNodes?.extract ?? false,\n categorize: d.compatibleNodes?.categorize ?? false,\n qualify: d.compatibleNodes?.qualify ?? false,\n split: d.compatibleNodes?.split ?? false,\n },\n inputFormats: {\n imageMimeTypes: d.inputFormats?.images?.mimeTypes ?? [],\n documentMimeTypes: ['application/pdf'], // PDFs only for LLM\n inputMethods: d.inputFormats?.images?.methods ?? ['base64'],\n maxImageSize: d.inputFormats?.images?.maxSize,\n maxPdfSize: d.inputFormats?.pdfs?.maxSize,\n maxPages: d.inputFormats?.pdfs?.maxPages,\n },\n pricing: {\n model: 'per-token',\n inputPer1kTokens: d.pricing?.inputPer1k,\n outputPer1kTokens: d.pricing?.outputPer1k,\n currency: 'USD',\n notes: d.pricing?.notes,\n },\n rateLimits: {\n requestsPerMinute: d.limits?.requestsPerMinute,\n },\n raw: d,\n };\n}\n\nfunction normalizeDatalabProvider(id: string, d: Record<string, any>): NormalizedProviderMetadata {\n const opts = d.supportedOptions ?? {};\n const isVLM = d.type === 'VLM';\n const isMarkerOCR = id === 'marker-ocr' || id.includes('marker-ocr');\n const isMarkerVLM = id === 'marker-vlm' || id.includes('marker-vlm');\n\n // Extract model from the provider/model format or use id\n const model = d.model ?? id;\n\n // Output formats based on provider type and outputFormat.features\n const outputFormats: OutputFormatSupport = {\n text: true,\n markdown: d.outputFormat?.features?.markdown ?? false,\n html: false,\n json: d.outputFormat?.features?.structuredJSON ?? isVLM,\n };\n\n // Map Datalab supportedOptions to normalized features\n // Mark deprecated features based on Datalab API docs\n const features: NormalizedFeatures = {\n maxPages: opts.maxPages ?? false,\n pageRange: opts.pageRange ?? false,\n languageHints: opts.langs ? 'deprecated' as FeatureStatus : false, // API ignores, handled automatically\n processingModes: opts.mode ?? false,\n agenticMode: false, // Datalab doesn't have agentic mode\n customPrompts: opts.blockCorrectionPrompt ? 'deprecated' as FeatureStatus : false, // Not currently supported\n imageExtraction: opts.extractImages ?? false,\n pageMarkers: opts.paginate ?? false, // maps from 'paginate'\n citations: isMarkerVLM ? true : false, // Marker VLM has citations\n chunking: false, // Datalab doesn't have chunking\n segmentation: opts.segmentation ?? false,\n stripExistingOCR: opts.stripExistingOCR ? 'deprecated' as FeatureStatus : false, // Managed automatically\n formatLines: opts.formatLines ? 'deprecated' as FeatureStatus : false, // Handled automatically\n forceOCR: 'deprecated' as FeatureStatus, // DEPRECATED: force_ocr param has no effect per API docs\n tableOutputFormats: false,\n tableMerging: false,\n confidence: false, // Datalab doesn't provide confidence scores\n boundingBoxes: d.outputFormat?.features?.boundingBoxes ?? true, // Datalab Surya provides text bboxes\n imageBoundingBoxes: isMarkerOCR || isMarkerVLM ? true : false, // Marker extracts images with bboxes\n schemaValidation: isVLM, // VLM providers support schema validation\n handwrittenText: true, // Datalab handles handwritten text\n headerFooterExtraction: false, // Datalab has issues with header/footer extraction\n // Extended features\n embedOptimized: false,\n passwordProtected: false,\n contentFiltering: false,\n ocrMode: false,\n webhookCallback: true, // Datalab supports webhook callbacks\n mediaResolution: false,\n changeTracking: true, // Datalab marker_extras supports track_changes\n hyperlinkExtraction: isMarkerOCR || isMarkerVLM, // Datalab extras=extract_links\n chartUnderstanding: isMarkerOCR || isMarkerVLM, // Datalab extras=chart_understanding\n imageCaptions: isMarkerOCR || isMarkerVLM, // Datalab disable_image_captions param\n signatureExtraction: false,\n commentExtraction: false,\n highlightExtraction: false,\n figureSummaries: false,\n outputFormats,\n };\n\n return {\n id,\n name: d.name ?? id,\n source: 'datalab',\n type: d.type ?? 'OCR',\n // NEW: 3-layer identity\n identity: {\n provider: 'datalab',\n model,\n method: 'native' as const, // Default to native, can be overridden when self-hosted\n },\n capabilities: {\n supportsImages: d.capabilities?.supportsImages ?? true,\n supportsPDFs: d.capabilities?.supportsPDFs ?? true,\n supportsDocuments: d.capabilities?.supportsDocuments ?? true,\n supportsReasoning: false, // Datalab doesn't do reasoning\n supportsStructuredOutput: isVLM,\n // NEW capabilities from supportedOptions\n supportsPrompts: opts.blockCorrectionPrompt ?? false,\n supportsCitations: opts.citations ?? false,\n supportsChunking: false,\n supportsImageExtraction: opts.extractImages ?? false,\n supportsPageMarkers: opts.paginate ?? false,\n supportsLanguageHints: opts.langs ?? false,\n supportsProcessingModes: opts.mode ?? false,\n supportsSegmentation: opts.segmentation ?? false,\n outputFormats,\n },\n features,\n // Datalab providers always need raw document input\n inputRequirements: {\n inputType: d.inputRequirements?.inputType ?? 'raw-document',\n acceptedMethods: d.inputRequirements?.acceptedMethods ?? d.inputFormats?.inputMethods ?? ['base64', 'url'],\n },\n compatibleNodes: {\n parse: d.compatibleNodes?.parse ?? false,\n extract: d.compatibleNodes?.extract ?? false,\n categorize: d.compatibleNodes?.categorize ?? false,\n qualify: d.compatibleNodes?.qualify ?? false,\n split: d.compatibleNodes?.split ?? false,\n },\n inputFormats: {\n imageMimeTypes: (d.inputFormats?.mimeTypes ?? []).filter((m: string) => m.startsWith('image/')),\n documentMimeTypes: (d.inputFormats?.mimeTypes ?? []).filter((m: string) => !m.startsWith('image/')),\n inputMethods: d.inputFormats?.inputMethods ?? ['base64'],\n maxFileSize: d.inputFormats?.maxFileSize,\n maxPages: d.inputFormats?.maxPages,\n },\n pricing: {\n model: 'per-page',\n perPage: d.pricing?.perPage,\n currency: 'USD',\n notes: d.pricing?.notes,\n },\n rateLimits: {\n docsPerMinute: d.apiConfig?.rateLimit?.docsPerMinute,\n },\n raw: d,\n };\n}\n\nfunction normalizeReductoProvider(id: string, d: Record<string, any>): NormalizedProviderMetadata {\n const opts = d.supportedOptions ?? {};\n const isVLM = d.type === 'VLM';\n const isExtract = d.compatibleNodes?.extract === true;\n const isParse = d.compatibleNodes?.parse === true;\n\n // Extract model from metadata or default to 'v1'\n const model = d.model ?? 'v1';\n\n // Output formats based on provider type\n const outputFormats: OutputFormatSupport = {\n text: d.outputFormat?.features?.textLines ?? true,\n markdown: d.outputFormat?.features?.markdown ?? d.compatibleNodes?.parse ?? false,\n html: opts.tableOutputFormat ?? false, // Reducto can output HTML tables\n json: d.outputFormat?.features?.structuredJSON ?? isExtract,\n };\n\n // Map Reducto supportedOptions to normalized features\n // Reducto doesn't have native maxPages, only pageRange - mark maxPages as derived\n const features: NormalizedFeatures = {\n maxPages: (opts.pageRange ?? false) ? 'derived' as FeatureStatus : false, // SDK derives from pageRange (1-indexed)\n pageRange: opts.pageRange ?? false,\n languageHints: false, // Reducto doesn't support language hints\n processingModes: false, // Reducto uses agentic instead\n agenticMode: opts.mode ?? false, // maps from 'mode' (agentic)\n customPrompts: opts.additionalPrompt ?? false, // maps from 'additionalPrompt'\n imageExtraction: opts.extractImages ?? false, // maps from 'returnImages'\n pageMarkers: true, // Reducto has addPageMarkers\n citations: opts.citations ?? false,\n chunking: opts.chunking ?? false,\n segmentation: opts.segmentation ?? false, // Via Split endpoint\n stripExistingOCR: false,\n formatLines: false,\n forceOCR: false,\n tableOutputFormats: opts.tableOutputFormat ?? false,\n tableMerging: d.compatibleNodes?.parse ?? false, // Parse has mergeTables\n confidence: opts.confidence ?? d.outputFormat?.features?.confidence ?? false, // Reducto Parse has confidence\n boundingBoxes: d.outputFormat?.features?.boundingBoxes ?? isParse, // Reducto Parse has text bounding boxes\n imageBoundingBoxes: isParse ? true : false, // Reducto Parse has figure bounding boxes\n schemaValidation: d.outputFormat?.features?.schemaValidation ?? isExtract, // Extract has schema validation\n handwrittenText: false, // Reducto doesn't specifically advertise handwriting\n headerFooterExtraction: true, // Reducto has Header/Footer block types\n // Extended features\n embedOptimized: isParse, // Reducto Parse supports retrieval.embedding_optimized: true\n passwordProtected: true, // Reducto handles encrypted PDFs\n contentFiltering: true, // Reducto can filter block types\n ocrMode: opts.ocrSystem ?? false, // Reducto has ocr_system selection\n webhookCallback: true, // Reducto supports webhook callbacks\n mediaResolution: false,\n changeTracking: true, // Reducto tracks changes in Word docs\n hyperlinkExtraction: true, // Reducto extracts hyperlinks via formatting.include\n chartUnderstanding: isParse, // Reducto enhance.agentic[].advanced_chart_agent for figures\n imageCaptions: false, // Not available in Reducto\n signatureExtraction: false, // NOT supported - formatting.include only accepts: change_tracking, highlight, comments, hyperlinks\n commentExtraction: isParse || isExtract, // Reducto formatting.include: [\"comments\"]\n highlightExtraction: isParse || isExtract, // Reducto formatting.include: [\"highlight\"]\n figureSummaries: isParse, // Reducto enhance.summarize_figures\n outputFormats,\n };\n\n return {\n id,\n name: d.name ?? id,\n source: 'reducto',\n type: d.type ?? 'OCR',\n // NEW: 3-layer identity\n identity: {\n provider: 'reducto',\n model,\n method: 'native' as const,\n },\n capabilities: {\n supportsImages: d.capabilities?.supportsImages ?? true,\n supportsPDFs: d.capabilities?.supportsPDFs ?? true,\n supportsDocuments: d.capabilities?.supportsDocuments ?? true,\n supportsReasoning: false, // Reducto doesn't do reasoning\n supportsStructuredOutput: isVLM || isExtract,\n // NEW capabilities from supportedOptions\n supportsPrompts: opts.additionalPrompt ?? false,\n supportsCitations: opts.citations ?? false,\n supportsChunking: opts.chunking ?? false,\n supportsImageExtraction: opts.extractImages ?? false,\n supportsPageMarkers: true,\n supportsLanguageHints: false,\n supportsProcessingModes: opts.mode ?? false, // agentic mode\n supportsSegmentation: opts.segmentation ?? false,\n outputFormats,\n },\n features,\n // Reducto providers always need raw document input\n inputRequirements: {\n inputType: d.inputRequirements?.inputType ?? 'raw-document',\n acceptedMethods: d.inputRequirements?.acceptedMethods ?? d.inputFormats?.inputMethods ?? ['base64', 'url'],\n },\n compatibleNodes: {\n parse: d.compatibleNodes?.parse ?? false,\n extract: d.compatibleNodes?.extract ?? false,\n categorize: d.compatibleNodes?.categorize ?? false,\n qualify: d.compatibleNodes?.qualify ?? false,\n split: d.compatibleNodes?.split ?? false,\n },\n inputFormats: {\n imageMimeTypes: (d.inputFormats?.mimeTypes ?? []).filter((m: string) => m.startsWith('image/')),\n documentMimeTypes: (d.inputFormats?.mimeTypes ?? []).filter((m: string) => !m.startsWith('image/')),\n inputMethods: d.inputFormats?.inputMethods ?? ['base64'],\n maxFileSize: d.inputFormats?.maxFileSize,\n maxPages: d.inputFormats?.maxPages,\n },\n pricing: {\n model: 'per-page',\n perPage: d.pricing?.standard ? d.pricing.standard * (d.pricing.usdPerCredit ?? 0.004) : d.pricing?.perPage,\n currency: 'USD',\n notes: d.pricing?.notes,\n },\n rateLimits: {\n docsPerMinute: d.apiConfig?.rateLimit?.docsPerMinute,\n },\n raw: d,\n };\n}\n\nfunction normalizeUnsiloedProvider(id: string, d: Record<string, any>): NormalizedProviderMetadata {\n const isVLM = d.type === 'VLM';\n const isExtract = d.compatibleNodes?.extract === true;\n const isParse = d.compatibleNodes?.parse === true;\n const isSplit = d.compatibleNodes?.split === true;\n const isCategorize = d.compatibleNodes?.categorize === true;\n\n // Extract model from metadata or default to 'v1'\n const model = d.model ?? 'v1';\n\n // Output formats based on provider type and outputFormat.features\n const outputFormats: OutputFormatSupport = {\n text: d.outputFormat?.features?.textLines ?? isParse,\n markdown: d.outputFormat?.features?.markdown ?? isParse,\n html: false, // Unsiloed doesn't output HTML\n json: d.outputFormat?.features?.structuredJSON ?? (isVLM || isExtract),\n };\n\n // Unsiloed features - inferred from outputFormat.features and capabilities\n // Note: Unsiloed doesn't have a formal supportedOptions like Datalab/Reducto\n const features: NormalizedFeatures = {\n maxPages: false, // Unsiloed doesn't have max pages option\n pageRange: false, // Unsiloed doesn't have page range option\n languageHints: false, // Unsiloed doesn't support language hints\n processingModes: false, // Unsiloed doesn't have fast/balanced/high_accuracy modes like Datalab\n agenticMode: false, // Unsiloed doesn't have agentic mode\n customPrompts: false, // Unsiloed doesn't support custom prompts\n imageExtraction: false, // Unsiloed doesn't extract images\n pageMarkers: false, // Unsiloed doesn't add page markers\n citations: d.outputFormat?.features?.citations ?? isExtract, // Extract has citations\n chunking: d.outputFormat?.features?.semanticChunking ?? isParse, // Parse has semantic chunking\n segmentation: isSplit, // Split provider does segmentation\n stripExistingOCR: false,\n formatLines: false,\n forceOCR: false,\n tableOutputFormats: false,\n tableMerging: false,\n confidence: d.outputFormat?.features?.confidence ?? false, // Unsiloed may provide confidence\n boundingBoxes: d.outputFormat?.features?.boundingBoxes ?? isParse, // Unsiloed Parse has bounding boxes\n imageBoundingBoxes: false, // Unsiloed doesn't return image-specific bboxes\n schemaValidation: isExtract, // Extract supports schema validation\n handwrittenText: d.capabilities?.specialFeatures?.includes('handwritten text') ?? false, // Parse supports handwriting\n headerFooterExtraction: false, // Unsiloed doesn't extract header/footer separately\n // Extended features\n embedOptimized: false,\n passwordProtected: false,\n contentFiltering: isParse, // Parse supports keep_segment_types: [\"table\", \"picture\", \"formula\", \"text\"]\n ocrMode: isParse, // Parse endpoint supports ocr_mode: 'auto_ocr' | 'full_ocr'\n webhookCallback: false, // Unsiloed is synchronous\n mediaResolution: false,\n changeTracking: false,\n hyperlinkExtraction: false,\n chartUnderstanding: false, // Not available in Unsiloed\n imageCaptions: false, // Not available in Unsiloed\n signatureExtraction: false, // Not available in Unsiloed\n commentExtraction: false, // Not available in Unsiloed\n highlightExtraction: false, // Not available in Unsiloed\n figureSummaries: false, // Not available in Unsiloed\n outputFormats,\n };\n\n return {\n id,\n name: d.name ?? id,\n source: 'unsiloed',\n type: d.type ?? 'OCR',\n // NEW: 3-layer identity\n identity: {\n provider: 'unsiloed',\n model,\n method: 'native' as const,\n },\n capabilities: {\n supportsImages: d.capabilities?.supportsImages ?? true,\n supportsPDFs: d.capabilities?.supportsPDFs ?? true,\n supportsDocuments: d.capabilities?.supportsDocuments ?? false,\n supportsReasoning: false, // Unsiloed doesn't do reasoning\n supportsStructuredOutput: isVLM || isExtract,\n // NEW capabilities\n supportsPrompts: false, // Unsiloed doesn't support custom prompts\n supportsCitations: d.outputFormat?.features?.citations ?? isExtract,\n supportsChunking: d.outputFormat?.features?.semanticChunking ?? isParse,\n supportsImageExtraction: false,\n supportsPageMarkers: false,\n supportsLanguageHints: false,\n supportsProcessingModes: false, // Unsiloed doesn't have fast/balanced/high_accuracy modes\n supportsSegmentation: isSplit || isCategorize,\n outputFormats,\n },\n features,\n // Unsiloed providers always need raw document input\n inputRequirements: {\n inputType: d.inputRequirements?.inputType ?? 'raw-document',\n acceptedMethods: d.inputRequirements?.acceptedMethods ?? d.inputFormats?.inputMethods ?? ['base64', 'url'],\n },\n compatibleNodes: {\n parse: d.compatibleNodes?.parse ?? false,\n extract: d.compatibleNodes?.extract ?? false,\n categorize: d.compatibleNodes?.categorize ?? false,\n qualify: d.compatibleNodes?.qualify ?? false,\n split: d.compatibleNodes?.split ?? false,\n },\n inputFormats: {\n imageMimeTypes: (d.inputFormats?.mimeTypes ?? []).filter((m: string) => m.startsWith('image/')),\n documentMimeTypes: (d.inputFormats?.mimeTypes ?? []).filter((m: string) => !m.startsWith('image/')),\n inputMethods: d.inputFormats?.inputMethods ?? ['base64'],\n maxFileSize: d.inputFormats?.maxFileSize,\n maxPages: d.inputFormats?.maxPages,\n },\n pricing: {\n model: 'per-page',\n perPage: d.pricing?.standardUSD ?? d.pricing?.perPage,\n currency: 'USD',\n notes: d.pricing?.notes,\n },\n rateLimits: {\n docsPerMinute: d.apiConfig?.rateLimit?.docsPerMinute,\n },\n raw: d,\n };\n}\n\n// Convenience functions\n\n/**\n * Get providers that support a specific MIME type\n */\nexport function getProvidersForMimeType(mimeType: string): NormalizedProviderMetadata[] {\n return queryProviders({ mimeType });\n}\n\n/**\n * Get the cheapest provider for a specific capability\n */\nexport function getCheapestProviderFor(\n capability: 'ocr' | 'extraction' | 'parse'\n): NormalizedProviderMetadata | undefined {\n let providers: NormalizedProviderMetadata[];\n\n switch (capability) {\n case 'ocr':\n case 'parse':\n providers = queryProviders({ compatibleWith: ['parse'] });\n break;\n case 'extraction':\n providers = queryProviders({ compatibleWith: ['extract'] });\n break;\n }\n\n // Sort by cost (per-page first, then per-token)\n return providers.sort((a, b) => {\n const costA = a.pricing.perPage ?? (a.pricing.inputPer1kTokens ?? Infinity);\n const costB = b.pricing.perPage ?? (b.pricing.inputPer1kTokens ?? Infinity);\n return costA - costB;\n })[0];\n}\n\n/**\n * Get providers with the largest file size support\n */\nexport function getProvidersForLargeFiles(minSizeMB: number = 100): NormalizedProviderMetadata[] {\n return queryProviders({ minFileSize: minSizeMB });\n}\n\n// ============================================================================\n// Model-Level Metadata Types\n// ============================================================================\n\n/**\n * Type alias for capabilities object (for model override typing)\n */\nexport type NormalizedCapabilities = NormalizedProviderMetadata['capabilities'];\n\n/**\n * Type alias for node compatibility object\n */\nexport type NodeCompatibility = NormalizedProviderMetadata['compatibleNodes'];\n\n/**\n * Type alias for pricing configuration\n */\nexport type NormalizedPricing = NormalizedProviderMetadata['pricing'];\n\n/**\n * Node type names for querying\n */\nexport type NodeTypeName = 'parse' | 'extract' | 'categorize' | 'qualify' | 'split';\n\n/**\n * Model-level limits that may differ from provider defaults\n */\nexport type ModelLimits = {\n maxContextTokens?: number;\n maxOutputTokens?: number;\n maxFileSize?: number; // MB\n maxPages?: number;\n};\n\n/**\n * Model-level metadata that can override provider defaults.\n * Unspecified fields inherit from the provider.\n */\nexport type ModelMetadata = {\n /** Model ID as used in API calls */\n id: string;\n\n /** Human-readable name (optional, defaults to id) */\n name?: string;\n\n /** OpenRouter model ID (e.g., 'openai/gpt-4.1') */\n openRouterId?: string;\n\n // =========================================\n // Capability Overrides (inherit if unset)\n // =========================================\n\n /** Override provider capabilities */\n capabilities?: Partial<NormalizedCapabilities>;\n\n /** Override provider input requirements */\n inputRequirements?: Partial<InputRequirements>;\n\n /** Override provider node compatibility */\n compatibleNodes?: Partial<NodeCompatibility>;\n\n // =========================================\n // Model-Specific Values\n // =========================================\n\n /** Model-specific pricing */\n pricing?: {\n inputPer1kTokens?: number;\n outputPer1kTokens?: number;\n perPage?: number;\n };\n\n /** Model-specific limits */\n limits?: ModelLimits;\n};\n\n/**\n * Provider metadata extended with model array\n */\nexport type ProviderMetadataWithModels = NormalizedProviderMetadata & {\n /** Per-model metadata with override capabilities */\n models?: ModelMetadata[];\n};\n\n/**\n * Fully resolved model metadata (all inheritance applied)\n */\nexport type ResolvedModelMetadata = {\n modelId: string;\n modelName: string;\n openRouterId?: string;\n providerId: string;\n providerName: string;\n providerSource: string;\n capabilities: NormalizedCapabilities;\n features: NormalizedFeatures;\n inputRequirements: InputRequirements;\n compatibleNodes: NodeCompatibility;\n pricing: NormalizedPricing;\n limits?: ModelLimits;\n};\n\n/**\n * Filter options for model queries\n */\nexport type ModelQueryFilter = {\n /** Filter by provider ID */\n providerId?: string | string[];\n\n /** Filter by provider source */\n source?: string | string[];\n\n /** Filter by capabilities */\n supports?: {\n images?: boolean;\n pdfs?: boolean;\n documents?: boolean;\n reasoning?: boolean;\n structuredOutput?: boolean;\n // Extended capability filters (same as ProviderQueryFilter)\n prompts?: boolean;\n citations?: boolean;\n chunking?: boolean;\n imageExtraction?: boolean;\n pageMarkers?: boolean;\n languageHints?: boolean;\n processingModes?: boolean;\n segmentation?: boolean;\n };\n\n /** Filter by specific features (all must be supported) */\n hasFeatures?: FeatureName[];\n\n /** Filter by output format support */\n outputFormat?: 'text' | 'markdown' | 'html' | 'json';\n\n /** Filter by input requirements */\n inputRequirements?: {\n inputType?: ProviderInputType | ProviderInputType[];\n };\n\n /** Filter by node compatibility */\n compatibleWith?: NodeTypeName[];\n\n /** Filter by context window (minimum) */\n minContextTokens?: number;\n\n /** Custom filter function */\n filter?: (model: ResolvedModelMetadata) => boolean;\n};\n\n// Registry for provider metadata with models\nconst modelRegistry = new Map<string, ProviderMetadataWithModels>();\n\n/**\n * Register provider metadata with model information\n *\n * @param providerId - Provider identifier\n * @param metadata - Provider metadata with models array\n */\nexport function registerProviderWithModels(\n providerId: string,\n metadata: ProviderMetadataWithModels\n): void {\n modelRegistry.set(providerId, metadata);\n}\n\n/**\n * Resolve model metadata by applying inheritance from provider.\n * Returns fully resolved metadata for a specific model.\n *\n * @param providerId - Provider ID (e.g., 'openai', 'anthropic')\n * @param modelId - Model ID (e.g., 'gpt-4.1', 'claude-sonnet-4.5'). If not provided, returns provider defaults.\n * @returns Resolved model metadata or undefined if not found\n *\n * @example\n * ```typescript\n * const gpt4 = resolveModelMetadata('openai', 'gpt-4.1');\n * console.log(gpt4?.capabilities.supportsReasoning); // false\n *\n * const o3 = resolveModelMetadata('openai', 'o3');\n * console.log(o3?.capabilities.supportsReasoning); // true\n * ```\n */\nexport function resolveModelMetadata(\n providerId: string,\n modelId?: string\n): ResolvedModelMetadata | undefined {\n // Try model registry first (has detailed model info)\n const providerWithModels = modelRegistry.get(providerId);\n if (providerWithModels) {\n return resolveFromProviderWithModels(providerWithModels, modelId);\n }\n\n // Fall back to basic provider registry\n const provider = getProviderById(providerId);\n if (!provider) return undefined;\n\n // Return provider-level metadata (no model-specific overrides)\n return {\n modelId: modelId ?? providerId,\n modelName: modelId ?? provider.name,\n providerId: provider.id,\n providerName: provider.name,\n providerSource: provider.source,\n capabilities: { ...provider.capabilities },\n features: { ...provider.features },\n inputRequirements: { ...provider.inputRequirements },\n compatibleNodes: { ...provider.compatibleNodes },\n pricing: { ...provider.pricing },\n };\n}\n\n/**\n * Resolve model from provider with models array (internal helper)\n */\nfunction resolveFromProviderWithModels(\n provider: ProviderMetadataWithModels,\n modelId?: string\n): ResolvedModelMetadata {\n // Find model in models array\n const model = modelId\n ? provider.models?.find(m => m.id === modelId)\n : undefined;\n\n // Build resolved metadata with inheritance\n return {\n modelId: model?.id ?? modelId ?? provider.id,\n modelName: model?.name ?? model?.id ?? modelId ?? provider.name,\n openRouterId: model?.openRouterId,\n providerId: provider.id,\n providerName: provider.name,\n providerSource: provider.source,\n\n // Merge capabilities (model overrides provider)\n capabilities: {\n supportsImages: model?.capabilities?.supportsImages ?? provider.capabilities.supportsImages,\n supportsPDFs: model?.capabilities?.supportsPDFs ?? provider.capabilities.supportsPDFs,\n supportsDocuments: model?.capabilities?.supportsDocuments ?? provider.capabilities.supportsDocuments,\n supportsReasoning: model?.capabilities?.supportsReasoning ?? provider.capabilities.supportsReasoning,\n supportsStructuredOutput: model?.capabilities?.supportsStructuredOutput ?? provider.capabilities.supportsStructuredOutput,\n // NEW capabilities\n supportsPrompts: model?.capabilities?.supportsPrompts ?? provider.capabilities.supportsPrompts,\n supportsCitations: model?.capabilities?.supportsCitations ?? provider.capabilities.supportsCitations,\n supportsChunking: model?.capabilities?.supportsChunking ?? provider.capabilities.supportsChunking,\n supportsImageExtraction: model?.capabilities?.supportsImageExtraction ?? provider.capabilities.supportsImageExtraction,\n supportsPageMarkers: model?.capabilities?.supportsPageMarkers ?? provider.capabilities.supportsPageMarkers,\n supportsLanguageHints: model?.capabilities?.supportsLanguageHints ?? provider.capabilities.supportsLanguageHints,\n supportsProcessingModes: model?.capabilities?.supportsProcessingModes ?? provider.capabilities.supportsProcessingModes,\n supportsSegmentation: model?.capabilities?.supportsSegmentation ?? provider.capabilities.supportsSegmentation,\n outputFormats: model?.capabilities?.outputFormats ?? provider.capabilities.outputFormats,\n },\n\n // Merge input requirements\n inputRequirements: {\n inputType: model?.inputRequirements?.inputType ?? provider.inputRequirements.inputType,\n acceptedMethods: model?.inputRequirements?.acceptedMethods ?? provider.inputRequirements.acceptedMethods,\n },\n\n // Merge node compatibility\n compatibleNodes: {\n parse: model?.compatibleNodes?.parse ?? provider.compatibleNodes.parse,\n extract: model?.compatibleNodes?.extract ?? provider.compatibleNodes.extract,\n categorize: model?.compatibleNodes?.categorize ?? provider.compatibleNodes.categorize,\n qualify: model?.compatibleNodes?.qualify ?? provider.compatibleNodes.qualify,\n split: model?.compatibleNodes?.split ?? provider.compatibleNodes.split,\n },\n\n // Features (inherited from provider - models don't override features)\n features: { ...provider.features },\n\n // Merge pricing\n pricing: {\n model: provider.pricing.model,\n inputPer1kTokens: model?.pricing?.inputPer1kTokens ?? provider.pricing.inputPer1kTokens,\n outputPer1kTokens: model?.pricing?.outputPer1kTokens ?? provider.pricing.outputPer1kTokens,\n perPage: model?.pricing?.perPage ?? provider.pricing.perPage,\n currency: provider.pricing.currency,\n notes: provider.pricing.notes,\n },\n\n // Model limits\n limits: model?.limits,\n };\n}\n\n/**\n * Query models with filters.\n * Returns all models that match the filter criteria.\n *\n * @param filter - Query filters\n * @returns Array of matching resolved model metadata\n *\n * @example\n * ```typescript\n * // Get all reasoning models\n * const reasoningModels = queryModels({ supports: { reasoning: true } });\n *\n * // Get models with large context windows\n * const largeContextModels = queryModels({ minContextTokens: 100000 });\n *\n * // Get OpenAI models compatible with extract()\n * const openaiExtract = queryModels({\n * providerId: 'openai',\n * compatibleWith: ['extract']\n * });\n * ```\n */\nexport function queryModels(filter: ModelQueryFilter = {}): ResolvedModelMetadata[] {\n const results: ResolvedModelMetadata[] = [];\n\n // Collect all models from model registry\n for (const [providerId, provider] of modelRegistry) {\n // Check provider-level filters first\n if (filter.providerId) {\n const providerIds = Array.isArray(filter.providerId) ? filter.providerId : [filter.providerId];\n if (!providerIds.includes(providerId)) continue;\n }\n\n if (filter.source) {\n const sources = Array.isArray(filter.source) ? filter.source : [filter.source];\n if (!sources.includes(provider.source)) continue;\n }\n\n // Resolve each model\n const models = provider.models ?? [{ id: provider.id }];\n for (const model of models) {\n const resolved = resolveFromProviderWithModels(provider, model.id);\n if (matchesModelFilter(resolved, filter)) {\n results.push(resolved);\n }\n }\n }\n\n // Also include providers from basic registry that don't have detailed models\n for (const provider of getAllProviders()) {\n // Skip if already in model registry\n if (modelRegistry.has(provider.id)) continue;\n\n // Check provider-level filters\n if (filter.providerId) {\n const providerIds = Array.isArray(filter.providerId) ? filter.providerId : [filter.providerId];\n if (!providerIds.includes(provider.id)) continue;\n }\n\n if (filter.source) {\n const sources = Array.isArray(filter.source) ? filter.source : [filter.source];\n if (!sources.includes(provider.source)) continue;\n }\n\n const resolved = resolveModelMetadata(provider.id);\n if (resolved && matchesModelFilter(resolved, filter)) {\n results.push(resolved);\n }\n }\n\n return results;\n}\n\n/**\n * Check if resolved model matches filter criteria (internal helper)\n */\nfunction matchesModelFilter(model: ResolvedModelMetadata, filter: ModelQueryFilter): boolean {\n // Check capabilities\n if (filter.supports) {\n if (filter.supports.images !== undefined && model.capabilities.supportsImages !== filter.supports.images) {\n return false;\n }\n if (filter.supports.pdfs !== undefined && model.capabilities.supportsPDFs !== filter.supports.pdfs) {\n return false;\n }\n if (filter.supports.documents !== undefined && model.capabilities.supportsDocuments !== filter.supports.documents) {\n return false;\n }\n if (filter.supports.reasoning !== undefined && model.capabilities.supportsReasoning !== filter.supports.reasoning) {\n return false;\n }\n if (filter.supports.structuredOutput !== undefined && model.capabilities.supportsStructuredOutput !== filter.supports.structuredOutput) {\n return false;\n }\n // Extended capability filters\n if (filter.supports.prompts !== undefined && model.capabilities.supportsPrompts !== filter.supports.prompts) {\n return false;\n }\n if (filter.supports.citations !== undefined && model.capabilities.supportsCitations !== filter.supports.citations) {\n return false;\n }\n if (filter.supports.chunking !== undefined && model.capabilities.supportsChunking !== filter.supports.chunking) {\n return false;\n }\n if (filter.supports.imageExtraction !== undefined && model.capabilities.supportsImageExtraction !== filter.supports.imageExtraction) {\n return false;\n }\n if (filter.supports.pageMarkers !== undefined && model.capabilities.supportsPageMarkers !== filter.supports.pageMarkers) {\n return false;\n }\n if (filter.supports.languageHints !== undefined && model.capabilities.supportsLanguageHints !== filter.supports.languageHints) {\n return false;\n }\n if (filter.supports.processingModes !== undefined && model.capabilities.supportsProcessingModes !== filter.supports.processingModes) {\n return false;\n }\n if (filter.supports.segmentation !== undefined && model.capabilities.supportsSegmentation !== filter.supports.segmentation) {\n return false;\n }\n }\n\n // Check specific features (all must be supported)\n // Uses isFeatureEnabled() to treat 'deprecated' and 'derived' as truthy\n if (filter.hasFeatures && filter.hasFeatures.length > 0) {\n for (const feature of filter.hasFeatures) {\n if (!isFeatureEnabled(model.features[feature])) {\n return false;\n }\n }\n }\n\n // Check output format support\n if (filter.outputFormat) {\n if (model.capabilities.outputFormats[filter.outputFormat] !== true) {\n return false;\n }\n }\n\n // Check input requirements\n if (filter.inputRequirements?.inputType !== undefined) {\n const inputTypes = Array.isArray(filter.inputRequirements.inputType)\n ? filter.inputRequirements.inputType\n : [filter.inputRequirements.inputType];\n if (!inputTypes.includes(model.inputRequirements.inputType)) {\n return false;\n }\n }\n\n // Check node compatibility\n if (filter.compatibleWith && filter.compatibleWith.length > 0) {\n for (const node of filter.compatibleWith) {\n if (!model.compatibleNodes[node]) {\n return false;\n }\n }\n }\n\n // Check context tokens\n if (filter.minContextTokens !== undefined) {\n const contextTokens = model.limits?.maxContextTokens ?? 0;\n if (contextTokens < filter.minContextTokens) {\n return false;\n }\n }\n\n // Custom filter\n if (filter.filter && !filter.filter(model)) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Get all models compatible with a specific node type.\n *\n * @param nodeType - Node type to check compatibility\n * @returns Array of resolved model metadata\n *\n * @example\n * ```typescript\n * // Get all models that can be used with extract()\n * const extractModels = getModelsForNode('extract');\n *\n * // Get all models that can be used with parse()\n * const parseModels = getModelsForNode('parse');\n * ```\n */\nexport function getModelsForNode(nodeType: NodeTypeName): ResolvedModelMetadata[] {\n return queryModels({ compatibleWith: [nodeType] });\n}\n\n/**\n * Get all registered models (resolved)\n */\nexport function getAllModels(): ResolvedModelMetadata[] {\n return queryModels({});\n}\n\n/**\n * Clear model registry (useful for testing)\n */\nexport function clearModelRegistry(): void {\n modelRegistry.clear();\n}\n\n// ============================================================================\n// Identity-Based Query Functions\n// ============================================================================\n\n/**\n * Query providers by 3-layer identity (provider/model/method)\n *\n * @example\n * ```typescript\n * // Get all Datalab providers\n * const datalabProviders = queryByIdentity({ provider: 'datalab' });\n *\n * // Get specific model\n * const surya = queryByIdentity({ provider: 'datalab', model: 'surya' });\n *\n * // Get all self-hosted capable providers\n * const selfHosted = queryByIdentity({ method: 'self-hosted' });\n * ```\n */\nexport function queryByIdentity(filter: {\n provider?: ProviderVendor | string;\n model?: string;\n method?: AccessMethod;\n}): NormalizedProviderMetadata[] {\n return queryProviders({\n provider: filter.provider,\n model: filter.model,\n method: filter.method,\n });\n}\n\n/**\n * Get all models available for a specific provider vendor\n *\n * @example\n * ```typescript\n * const datalabModels = getModelsForProvider('datalab');\n * // => ['surya', 'marker-ocr', 'marker-vlm']\n * ```\n */\nexport function getModelsForProvider(provider: ProviderVendor | string): string[] {\n const providers = queryProviders({ provider });\n const models = new Set<string>();\n for (const p of providers) {\n if (p.identity?.model) {\n models.add(p.identity.model);\n }\n }\n return [...models];\n}\n\n/**\n * Get available access methods for a provider/model combination\n *\n * @example\n * ```typescript\n * const methods = getMethodsForModel('datalab', 'surya');\n * // => ['native', 'self-hosted']\n * ```\n */\nexport function getMethodsForModel(\n provider: ProviderVendor | string,\n model: string\n): AccessMethod[] {\n const providers = queryProviders({ provider, model });\n const methods = new Set<AccessMethod>();\n for (const p of providers) {\n if (p.identity?.method) {\n methods.add(p.identity.method);\n }\n }\n return [...methods];\n}\n\n/**\n * Get all unique provider vendors in the registry\n *\n * @example\n * ```typescript\n * const vendors = getAllProviderVendors();\n * // => ['datalab', 'reducto', 'unsiloed', 'openai', 'anthropic', ...]\n * ```\n */\nexport function getAllProviderVendors(): string[] {\n const providers = getAllProviders();\n const vendors = new Set<string>();\n for (const p of providers) {\n if (p.identity?.provider) {\n vendors.add(p.identity.provider);\n }\n }\n return [...vendors];\n}\n\n// ============================================================================\n// Derived Feature Transformation Utilities\n// ============================================================================\n\n/**\n * Page indexing convention by provider source.\n * Used when converting maxPages to pageRange.\n */\nconst PAGE_INDEXING: Record<string, PageIndexing> = {\n datalab: '0-indexed',\n reducto: '1-indexed',\n mistral: '0-indexed',\n unsiloed: '1-indexed', // Default assumption\n llm: '1-indexed', // N/A but default\n};\n\n/**\n * Get the page indexing convention for a provider.\n *\n * @param provider - Provider metadata or source string\n * @returns Page indexing convention ('0-indexed' or '1-indexed')\n */\nexport function getPageIndexing(provider: NormalizedProviderMetadata | string): PageIndexing {\n const source = typeof provider === 'string' ? provider : provider.source;\n return PAGE_INDEXING[source] ?? '1-indexed';\n}\n\n/**\n * Options that can be transformed for derived features.\n */\nexport type DerivedFeatureOptions = {\n maxPages?: number;\n pageRange?: string;\n};\n\n/**\n * Result of derived feature transformation.\n */\nexport type TransformedOptions = {\n /** The transformed page_range parameter (provider-specific format) */\n page_range?: string;\n /** Array format for providers that support it (e.g., Mistral) */\n pages?: number[];\n /** Original options minus the derived ones */\n remainingOptions: Record<string, unknown>;\n};\n\n/**\n * Transform maxPages to provider-specific pageRange format.\n *\n * This utility handles the conversion when a provider has `maxPages: 'derived'`,\n * meaning the SDK provides maxPages functionality via the underlying pageRange API.\n *\n * @param options - User-provided options including maxPages\n * @param provider - Provider metadata to determine format\n * @returns Transformed options with provider-specific pageRange\n *\n * @example\n * ```typescript\n * // User wants first 5 pages from Reducto (1-indexed)\n * const result = transformDerivedFeatures({ maxPages: 5 }, reductoProvider);\n * // => { page_range: '1-5', remainingOptions: {} }\n *\n * // User wants first 5 pages from Datalab (0-indexed)\n * const result = transformDerivedFeatures({ maxPages: 5 }, datalabProvider);\n * // => { page_range: '0-4', remainingOptions: {} }\n *\n * // User wants first 5 pages from Mistral (0-indexed, array format)\n * const result = transformDerivedFeatures({ maxPages: 5 }, mistralProvider);\n * // => { page_range: '0-4', pages: [0,1,2,3,4], remainingOptions: {} }\n * ```\n */\nexport function transformDerivedFeatures(\n options: DerivedFeatureOptions & Record<string, unknown>,\n provider: NormalizedProviderMetadata\n): TransformedOptions {\n const { maxPages, pageRange, ...remainingOptions } = options;\n const result: TransformedOptions = { remainingOptions };\n\n // If user provided explicit pageRange, pass it through\n if (pageRange !== undefined) {\n result.page_range = pageRange;\n return result;\n }\n\n // If maxPages provided and provider has derived maxPages support\n if (maxPages !== undefined && provider.features.maxPages === 'derived') {\n const indexing = getPageIndexing(provider);\n\n if (indexing === '0-indexed') {\n // 0-indexed: first N pages = 0 to N-1\n result.page_range = `0-${maxPages - 1}`;\n\n // Mistral also supports array format\n if (provider.source === 'mistral') {\n result.pages = Array.from({ length: maxPages }, (_, i) => i);\n }\n } else {\n // 1-indexed: first N pages = 1 to N\n result.page_range = `1-${maxPages}`;\n }\n } else if (maxPages !== undefined && isFeatureEnabled(provider.features.maxPages)) {\n // Provider natively supports maxPages, pass it through in remainingOptions\n result.remainingOptions.maxPages = maxPages;\n }\n\n return result;\n}\n\n/**\n * Check if a provider requires derived feature transformation for maxPages.\n *\n * @param provider - Provider metadata\n * @returns true if maxPages needs to be transformed to pageRange\n */\nexport function requiresMaxPagesTransformation(provider: NormalizedProviderMetadata): boolean {\n return provider.features.maxPages === 'derived';\n}\n\n// ============================================================================\n// Mistral Provider Normalizer\n// ============================================================================\n\nfunction normalizeMistralProvider(id: string, d: Record<string, any>): NormalizedProviderMetadata {\n const opts = d.supportedOptions ?? {};\n const isVLM = d.type === 'VLM';\n const isOCR = d.type === 'OCR';\n\n // Extract model from metadata\n const model = d.model ?? id;\n\n // Output formats based on provider type\n const outputFormats: OutputFormatSupport = {\n text: true,\n markdown: d.outputFormat?.features?.markdown ?? isOCR,\n html: d.outputFormat?.features?.htmlTables ?? isOCR, // OCR 3 can output HTML tables\n json: d.outputFormat?.features?.structuredJSON ?? isVLM,\n };\n\n // Map Mistral supportedOptions to normalized features\n // Mistral VLM: bbox_annotation supports 1000 pages, document_annotation limited to 8 pages\n const features: NormalizedFeatures = {\n maxPages: d.inputFormats?.maxPages !== undefined,\n pageRange: true, // Mistral supports pages param: \"0-5\" or [0,2,5] (0-indexed)\n languageHints: false, // Mistral doesn't support language hints\n processingModes: false, // Mistral doesn't have processing modes\n agenticMode: false, // Mistral doesn't have agentic mode\n customPrompts: false, // Mistral OCR 3 doesn't support custom prompts\n imageExtraction: opts.includeImageBase64 ?? false, // Can include embedded images\n pageMarkers: false, // Mistral doesn't add page markers\n citations: false, // Mistral doesn't provide citations\n chunking: false, // Mistral doesn't do chunking\n segmentation: false, // Mistral doesn't do segmentation\n stripExistingOCR: false,\n formatLines: false,\n forceOCR: true, // OCR 3 always does OCR\n tableOutputFormats: opts.tableFormat ?? isOCR, // html or markdown table format\n tableMerging: false,\n confidence: false, // Mistral doesn't provide confidence scores\n boundingBoxes: false, // Mistral does NOT provide text-level bounding boxes\n imageBoundingBoxes: true, // Mistral provides image/figure bounding boxes only\n schemaValidation: d.outputFormat?.features?.schemaValidation ?? isVLM, // VLM supports schema\n handwrittenText: d.outputFormat?.features?.handwrittenText ?? true, // Excellent handwriting support\n headerFooterExtraction: opts.extractHeader ?? opts.extractFooter ?? false, // extract_header/extract_footer\n // Extended features\n embedOptimized: false,\n passwordProtected: false,\n contentFiltering: false,\n ocrMode: false,\n webhookCallback: false, // Mistral is synchronous\n mediaResolution: false,\n changeTracking: false,\n hyperlinkExtraction: true, // Response pages[].hyperlinks[] auto-extracted\n chartUnderstanding: false, // Not available as separate feature in Mistral\n imageCaptions: false, // Not available in Mistral\n signatureExtraction: false, // Not available in Mistral\n commentExtraction: false, // Not available in Mistral\n highlightExtraction: false, // Not available in Mistral\n figureSummaries: false, // Not available in Mistral\n outputFormats,\n };\n\n return {\n id: d.id ?? id,\n name: d.name ?? id,\n source: 'mistral',\n type: d.type ?? 'OCR',\n // 3-layer identity\n identity: {\n provider: 'mistral',\n model,\n method: 'native' as const,\n },\n capabilities: {\n supportsImages: d.capabilities?.supportsImages ?? true,\n supportsPDFs: d.capabilities?.supportsPDFs ?? true,\n supportsDocuments: d.capabilities?.supportsDocuments ?? true, // Supports DOCX, PPTX, TXT, EPUB, RTF, ODT, etc. (NOT XLSX)\n supportsReasoning: false, // OCR 3 doesn't do reasoning\n supportsStructuredOutput: d.capabilities?.supportsStructuredOutput ?? isVLM,\n // Extended capabilities\n supportsPrompts: false,\n supportsCitations: false,\n supportsChunking: false,\n supportsImageExtraction: opts.includeImageBase64 ?? false,\n supportsPageMarkers: false,\n supportsLanguageHints: false,\n supportsProcessingModes: false,\n supportsSegmentation: false,\n outputFormats,\n },\n features,\n // Mistral providers always need raw document input\n inputRequirements: {\n inputType: d.inputRequirements?.inputType ?? 'raw-document',\n acceptedMethods: d.inputRequirements?.acceptedMethods ?? ['base64', 'url'],\n },\n compatibleNodes: {\n parse: d.compatibleNodes?.parse ?? isOCR,\n extract: d.compatibleNodes?.extract ?? isVLM,\n categorize: d.compatibleNodes?.categorize ?? false,\n qualify: d.compatibleNodes?.qualify ?? false,\n split: d.compatibleNodes?.split ?? false,\n },\n inputFormats: {\n imageMimeTypes: (d.inputFormats?.mimeTypes ?? []).filter((m: string) => m.startsWith('image/')),\n documentMimeTypes: (d.inputFormats?.mimeTypes ?? []).filter((m: string) => !m.startsWith('image/')),\n inputMethods: d.inputFormats?.inputMethods ?? ['base64', 'url'],\n maxFileSize: d.inputFormats?.maxFileSize ?? 50, // 50MB limit\n maxPages: d.inputFormats?.maxPages ?? 1000,\n },\n pricing: {\n model: 'per-page',\n perPage: d.pricing?.perPage ?? 0.002, // $2/1000 pages\n currency: 'USD',\n notes: d.pricing?.notes ?? '$2 per 1000 pages',\n },\n rateLimits: {\n docsPerMinute: d.apiConfig?.rateLimit?.docsPerMinute,\n },\n raw: d,\n };\n}\n","/**\n * @doclo/core - Retry Utilities\n *\n * Shared retry infrastructure for LLM and OCR providers.\n * Includes exponential backoff, circuit breaker pattern, and error classification.\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Configuration for retry behavior\n */\nexport interface RetryConfig {\n /** Maximum number of retry attempts (default: 2) */\n maxRetries?: number;\n /** Base delay in milliseconds between retries (default: 1000) */\n retryDelay?: number;\n /** Enable exponential backoff (default: true) */\n useExponentialBackoff?: boolean;\n /** Maximum delay cap in milliseconds (default: 30000) */\n maxDelay?: number;\n}\n\n/**\n * Configuration for circuit breaker behavior\n */\nexport interface CircuitBreakerConfig {\n /** Number of consecutive failures before opening circuit (default: 3) */\n threshold?: number;\n /** Time in milliseconds before trying again after circuit opens (default: 30000) */\n resetTimeout?: number;\n}\n\n/**\n * Internal state for a circuit breaker\n */\nexport interface CircuitBreakerState {\n consecutiveFailures: number;\n lastFailureTime?: number;\n isOpen: boolean;\n}\n\n/**\n * Circuit breaker interface\n */\nexport interface CircuitBreaker {\n /** Check if circuit is currently open (should skip requests) */\n isOpen(): boolean;\n /** Record a successful request (resets failure count) */\n recordSuccess(): void;\n /** Record a failed request (may open circuit) */\n recordFailure(): void;\n /** Get current state for inspection */\n getState(): CircuitBreakerState;\n}\n\n/**\n * Options for the withRetry wrapper\n */\nexport interface WithRetryOptions<T> extends RetryConfig {\n /** Called before each retry attempt (for logging/observability) */\n onRetry?: (attempt: number, error: Error, delay: number) => void | Promise<void>;\n /** Override to parse Retry-After header from response errors */\n getRetryAfter?: (error: Error) => number | undefined;\n /** Circuit breaker to use (optional) */\n circuitBreaker?: CircuitBreaker;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Default retry configuration */\nexport const DEFAULT_RETRY_CONFIG: Required<RetryConfig> = {\n maxRetries: 2,\n retryDelay: 1000,\n useExponentialBackoff: true,\n maxDelay: 30000,\n};\n\n/** Default circuit breaker configuration */\nexport const DEFAULT_CIRCUIT_BREAKER_CONFIG: Required<CircuitBreakerConfig> = {\n threshold: 3,\n resetTimeout: 30000,\n};\n\n/**\n * HTTP status codes that are retryable\n * - 408: Request Timeout\n * - 429: Too Many Requests (rate limited)\n * - 500: Internal Server Error\n * - 502: Bad Gateway\n * - 503: Service Unavailable\n * - 504: Gateway Timeout\n */\nconst RETRYABLE_STATUS_CODES = ['408', '429', '500', '502', '503', '504'];\n\n/**\n * Error message patterns that indicate retryable errors\n */\nconst RETRYABLE_ERROR_PATTERNS = [\n 'timeout',\n 'rate limit',\n 'overloaded',\n 'econnreset',\n 'etimedout',\n 'enotfound',\n 'econnrefused',\n 'socket hang up',\n 'network error',\n];\n\n// ============================================================================\n// Error Classification\n// ============================================================================\n\n/**\n * Determines if an error is retryable based on its message content.\n *\n * Retryable errors include:\n * - HTTP 408, 429, 500, 502, 503, 504\n * - Timeout errors\n * - Rate limit errors\n * - Network errors (ECONNRESET, ETIMEDOUT, etc.)\n *\n * Non-retryable errors include:\n * - HTTP 400, 401, 403, 404 (client errors)\n * - Business logic failures\n *\n * @param error - The error to classify\n * @returns true if the error is retryable\n */\nexport function isRetryableError(error: Error): boolean {\n const message = error.message.toLowerCase();\n\n // Check for retryable HTTP status codes\n for (const code of RETRYABLE_STATUS_CODES) {\n if (message.includes(code)) {\n return true;\n }\n }\n\n // Check for retryable error patterns\n for (const pattern of RETRYABLE_ERROR_PATTERNS) {\n if (message.includes(pattern)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Extracts HTTP status code from an error message if present.\n *\n * @param error - The error to extract status from\n * @returns The HTTP status code or undefined\n */\nexport function extractStatusCode(error: Error): number | undefined {\n // Common patterns: \"HTTP 429\", \"status: 503\", \"failed: 500\"\n const patterns = [\n /\\b(\\d{3})\\b/, // Just the status code\n /status[:\\s]+(\\d{3})/i,\n /http[:\\s]+(\\d{3})/i,\n /failed[:\\s]+(\\d{3})/i,\n ];\n\n for (const pattern of patterns) {\n const match = error.message.match(pattern);\n if (match && match[1]) {\n const code = parseInt(match[1], 10);\n if (code >= 100 && code < 600) {\n return code;\n }\n }\n }\n\n return undefined;\n}\n\n/**\n * Parses Retry-After header value from error message or response.\n * Supports both seconds (integer) and HTTP-date formats.\n *\n * @param error - Error that may contain Retry-After information\n * @returns Delay in milliseconds, or undefined if not found\n */\nexport function parseRetryAfter(error: Error): number | undefined {\n const message = error.message;\n\n // Look for \"retry-after: X\" or \"Retry-After: X\" patterns\n const match = message.match(/retry-after[:\\s]+(\\d+)/i);\n if (match && match[1]) {\n const seconds = parseInt(match[1], 10);\n if (!isNaN(seconds) && seconds > 0 && seconds < 3600) {\n return seconds * 1000;\n }\n }\n\n return undefined;\n}\n\n// ============================================================================\n// Delay Calculation\n// ============================================================================\n\n/**\n * Calculates the delay before the next retry attempt.\n *\n * With exponential backoff enabled (default):\n * - Attempt 1: baseDelay * 2^0 = 1x baseDelay\n * - Attempt 2: baseDelay * 2^1 = 2x baseDelay\n * - Attempt 3: baseDelay * 2^2 = 4x baseDelay\n * Plus random jitter (0-1000ms) to prevent thundering herd.\n *\n * @param attempt - Current attempt number (1-indexed)\n * @param config - Retry configuration\n * @returns Delay in milliseconds\n */\nexport function calculateRetryDelay(\n attempt: number,\n config: RetryConfig = {}\n): number {\n const {\n retryDelay = DEFAULT_RETRY_CONFIG.retryDelay,\n useExponentialBackoff = DEFAULT_RETRY_CONFIG.useExponentialBackoff,\n maxDelay = DEFAULT_RETRY_CONFIG.maxDelay,\n } = config;\n\n if (!useExponentialBackoff) {\n return retryDelay;\n }\n\n // Exponential backoff: baseDelay * (2 ^ (attempt - 1)) + jitter\n const exponentialDelay = retryDelay * Math.pow(2, attempt - 1);\n const jitter = Math.random() * 1000; // 0-1000ms jitter\n return Math.min(exponentialDelay + jitter, maxDelay);\n}\n\n// ============================================================================\n// Circuit Breaker\n// ============================================================================\n\n// Global registry of circuit breakers by key\nconst circuitBreakerRegistry = new Map<string, CircuitBreaker>();\n\n/**\n * Creates or retrieves a circuit breaker for a given key.\n *\n * Circuit breakers prevent cascading failures by:\n * 1. Tracking consecutive failures per provider/endpoint\n * 2. \"Opening\" the circuit after threshold failures (skipping requests)\n * 3. Allowing a retry after resetTimeout (half-open state)\n * 4. Closing the circuit on success\n *\n * @param key - Unique identifier (e.g., \"datalab:surya\" or \"openai:gpt-4\")\n * @param config - Circuit breaker configuration\n * @returns CircuitBreaker instance\n */\nexport function createCircuitBreaker(\n key: string,\n config: CircuitBreakerConfig = {}\n): CircuitBreaker {\n // Return existing circuit breaker if one exists for this key\n const existing = circuitBreakerRegistry.get(key);\n if (existing) {\n return existing;\n }\n\n const {\n threshold = DEFAULT_CIRCUIT_BREAKER_CONFIG.threshold,\n resetTimeout = DEFAULT_CIRCUIT_BREAKER_CONFIG.resetTimeout,\n } = config;\n\n let state: CircuitBreakerState = {\n consecutiveFailures: 0,\n isOpen: false,\n };\n\n const circuitBreaker: CircuitBreaker = {\n isOpen(): boolean {\n if (!state.isOpen) return false;\n\n // Check if enough time has passed to try again (half-open state)\n if (state.lastFailureTime && Date.now() - state.lastFailureTime > resetTimeout) {\n // Reset to allow a trial request\n state = {\n consecutiveFailures: 0,\n isOpen: false,\n };\n return false;\n }\n\n return true;\n },\n\n recordSuccess(): void {\n state = {\n consecutiveFailures: 0,\n isOpen: false,\n };\n },\n\n recordFailure(): void {\n state.consecutiveFailures++;\n state.lastFailureTime = Date.now();\n\n if (state.consecutiveFailures >= threshold) {\n state.isOpen = true;\n console.warn(`Circuit breaker opened for ${key} after ${state.consecutiveFailures} consecutive failures`);\n }\n },\n\n getState(): CircuitBreakerState {\n return { ...state };\n },\n };\n\n circuitBreakerRegistry.set(key, circuitBreaker);\n return circuitBreaker;\n}\n\n/**\n * Clears all circuit breakers. Useful for testing.\n */\nexport function clearCircuitBreakers(): void {\n circuitBreakerRegistry.clear();\n}\n\n/**\n * Gets the circuit breaker for a key without creating one.\n *\n * @param key - Unique identifier\n * @returns CircuitBreaker or undefined if not found\n */\nexport function getCircuitBreaker(key: string): CircuitBreaker | undefined {\n return circuitBreakerRegistry.get(key);\n}\n\n// ============================================================================\n// Retry Wrapper\n// ============================================================================\n\n/**\n * Wraps an async function with retry logic.\n *\n * @example\n * ```typescript\n * const result = await withRetry(\n * () => fetchWithTimeout(url, options),\n * {\n * maxRetries: 3,\n * retryDelay: 1000,\n * useExponentialBackoff: true,\n * onRetry: (attempt, error, delay) => {\n * console.log(`Retry ${attempt} after ${delay}ms: ${error.message}`);\n * }\n * }\n * );\n * ```\n *\n * @param fn - Async function to retry\n * @param options - Retry options\n * @returns Result of the function\n * @throws Last error if all retries fail\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n options: WithRetryOptions<T> = {}\n): Promise<T> {\n const {\n maxRetries = DEFAULT_RETRY_CONFIG.maxRetries,\n retryDelay = DEFAULT_RETRY_CONFIG.retryDelay,\n useExponentialBackoff = DEFAULT_RETRY_CONFIG.useExponentialBackoff,\n maxDelay = DEFAULT_RETRY_CONFIG.maxDelay,\n onRetry,\n getRetryAfter,\n circuitBreaker,\n } = options;\n\n // Check circuit breaker before first attempt\n if (circuitBreaker?.isOpen()) {\n throw new Error('Circuit breaker is open');\n }\n\n let lastError: Error | null = null;\n\n // maxRetries = 0 means no retries (just one attempt)\n // maxRetries = 2 means up to 3 total attempts (1 initial + 2 retries)\n const totalAttempts = maxRetries + 1;\n\n for (let attempt = 1; attempt <= totalAttempts; attempt++) {\n try {\n const result = await fn();\n\n // Success - record it and return\n circuitBreaker?.recordSuccess();\n return result;\n } catch (error) {\n lastError = error as Error;\n\n // Check if we should retry\n const isLastAttempt = attempt === totalAttempts;\n const canRetry = !isLastAttempt && isRetryableError(lastError);\n\n if (!canRetry) {\n break;\n }\n\n // Calculate delay (may be overridden by Retry-After header)\n let delay = calculateRetryDelay(attempt, { retryDelay, useExponentialBackoff, maxDelay });\n\n // Check for Retry-After header\n const retryAfter = getRetryAfter?.(lastError) ?? parseRetryAfter(lastError);\n if (retryAfter !== undefined && retryAfter > 0) {\n delay = Math.min(retryAfter, maxDelay);\n }\n\n // Call onRetry hook\n if (onRetry) {\n await onRetry(attempt, lastError, delay);\n }\n\n // Wait before next attempt\n await sleep(delay);\n }\n }\n\n // All attempts failed\n circuitBreaker?.recordFailure();\n throw lastError!;\n}\n\n/**\n * Helper to sleep for a given duration\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n"],"mappings":"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","names":["node","validStarters","schema","maxSize","node"]}
|