import { IncomingMessage, ServerResponse } from 'node:http'; import { Connect } from 'vite'; import { W as WritenexError } from '../errors-C0iYiDTv.js'; import { W as WritenexConfig } from '../config-CWvboaLE.js'; import { D as DiscoveredCollection, a as ContentSummary } from '../content-upwjbOn5.js'; import { D as DiscoveredImage } from '../image-FP7w5ZIs.js'; /** * @fileoverview Vite middleware for Writenex routes * * This module provides the main middleware handler that intercepts requests * to Writenex routes (/_writenex/*) and delegates to appropriate handlers. * * ## Route Structure: * - `/_writenex` - Editor UI (HTML page with React app) * - `/_writenex/api/*` - API endpoints for CRUD operations * - `/_writenex/assets/*` - Static assets (JS, CSS) * * @module @writenex/astro/server/middleware */ /** * Middleware context passed to handlers */ interface MiddlewareContext { /** Base path for Writenex routes */ basePath: string; /** Project root directory */ projectRoot: string; /** Resolved Writenex configuration */ config: Required; /** Astro trailingSlash setting for preview URLs */ trailingSlash: "always" | "never" | "ignore"; } /** * Create the Writenex middleware handler * * @param context - Middleware context with configuration * @returns Connect middleware function * * @example * ```typescript * const middleware = createMiddleware({ * basePath: '/_writenex', * projectRoot: '/path/to/project', * config: resolvedConfig, * }); * * server.middlewares.use(middleware); * ``` */ declare function createMiddleware(context: MiddlewareContext): Connect.NextHandleFunction; /** * Parse URL query parameters * * @param url - The URL string to parse * @returns Object with query parameters */ declare function parseQueryParams(url: string): Record; /** * Parse request body as JSON * * @param req - The incoming request * @returns Parsed JSON body or null */ declare function parseJsonBody(req: IncomingMessage): Promise; /** * Send JSON response * * @param res - The server response * @param data - Data to send as JSON * @param statusCode - HTTP status code (default: 200) */ declare function sendJson(res: ServerResponse, data: unknown, statusCode?: number): void; /** * Send error response * * @param res - The server response * @param message - Error message * @param statusCode - HTTP status code (default: 400) */ declare function sendError(res: ServerResponse, message: string, statusCode?: number): void; /** * Send WritenexError response * * Automatically uses the error's HTTP status code and formats * the response using the error's toJSON method. * * @param res - The server response * @param error - WritenexError instance */ declare function sendWritenexError(res: ServerResponse, error: WritenexError): void; /** * @fileoverview Static asset serving for Writenex editor * * This module handles serving the editor UI HTML and static assets * (JavaScript, CSS) for the Writenex editor interface. * * ## Asset Strategy: * - In development: Serve from source with Vite transform * - In production: Serve pre-bundled assets from dist/client * * @module @writenex/astro/server/assets */ /** * Serve the editor HTML page * * This generates the HTML shell that loads the React editor application. * The actual React components will be loaded via the bundled client assets. * * @param _req - The incoming request * @param res - The server response * @param context - Middleware context */ declare function serveEditorHtml(_req: IncomingMessage, res: ServerResponse, context: MiddlewareContext): Promise; /** * Serve static assets (JS, CSS, etc.) * * @param _req - The incoming request * @param res - The server response * @param assetPath - Path to the asset (relative to assets directory) * @param _context - Middleware context */ declare function serveAsset(_req: IncomingMessage, res: ServerResponse, assetPath: string, _context: MiddlewareContext): Promise; /** * Get the path to bundled client assets * * @returns Path to the client dist directory */ declare function getClientDistPath(): string; /** * Check if client assets are bundled * * @returns True if bundled assets exist */ declare function hasClientBundle(): boolean; /** * @fileoverview Server-side caching for Writenex * * Provides in-memory caching for collection discovery and content summaries * to improve performance by avoiding repeated filesystem operations. * * ## Features: * - TTL-based cache expiration * - Per-collection content caching * - Image discovery caching * - Manual cache invalidation * - Integration with file watcher for automatic invalidation * * @module @writenex/astro/server/cache */ /** * Server-side cache for collection and content data * * This cache stores: * - Collection discovery results (list of collections with metadata) * - Content summaries per collection (list of content items) * - Discovered images per content item * * Cache invalidation happens: * - Automatically when TTL expires * - Manually via invalidate methods * - Via file watcher integration */ declare class ServerCache { /** Cache for collection discovery results */ private collectionsCache; /** Cache for content summaries, keyed by collection name */ private contentCache; /** Cache for discovered images, keyed by "collection:contentId" */ private imagesCache; /** Time-to-live for cache entries in milliseconds */ private ttl; /** Whether file watcher is integrated (allows longer TTL) */ private hasWatcher; constructor(options?: { ttl?: number; hasWatcher?: boolean; }); /** * Enable file watcher integration * * When watcher is enabled, cache can use longer TTL since * invalidation happens via watcher events. */ enableWatcher(): void; /** * Check if a cache entry is still valid */ private isValid; /** * Get cached collections if valid * * @returns Cached collections or null if expired/not cached */ getCollections(): DiscoveredCollection[] | null; /** * Set collections cache * * @param collections - Collections to cache */ setCollections(collections: DiscoveredCollection[]): void; /** * Invalidate collections cache */ invalidateCollections(): void; /** * Get cached content summaries for a collection if valid * * @param collection - Collection name * @returns Cached content summaries or null if expired/not cached */ getContent(collection: string): ContentSummary[] | null; /** * Set content cache for a collection * * @param collection - Collection name * @param items - Content summaries to cache */ setContent(collection: string, items: ContentSummary[]): void; /** * Invalidate content cache for a specific collection * * @param collection - Collection name to invalidate */ invalidateContent(collection: string): void; /** * Invalidate all content caches */ invalidateAllContent(): void; /** * Generate cache key for image cache * * @param collection - Collection name * @param contentId - Content ID * @returns Cache key string */ private getImagesCacheKey; /** * Get cached images for a content item if valid * * @param collection - Collection name * @param contentId - Content ID * @returns Cached images or null if expired/not cached */ getImages(collection: string, contentId: string): DiscoveredImage[] | null; /** * Set images cache for a content item * * @param collection - Collection name * @param contentId - Content ID * @param images - Discovered images to cache */ setImages(collection: string, contentId: string, images: DiscoveredImage[]): void; /** * Invalidate image cache for a specific content item * * @param collection - Collection name * @param contentId - Content ID */ invalidateImages(collection: string, contentId: string): void; /** * Invalidate all image caches for a collection * * @param collection - Collection name */ invalidateCollectionImages(collection: string): void; /** * Invalidate all image caches */ invalidateAllImages(): void; /** * Invalidate all caches * * Called when a major change occurs that affects everything. */ invalidateAll(): void; /** * Handle file change event from watcher * * Intelligently invalidates only the affected caches. * * @param type - Type of file change (add, change, unlink) * @param collection - Collection that was affected * @param contentId - Optional content ID for targeted image cache invalidation */ handleFileChange(type: "add" | "change" | "unlink", collection: string, contentId?: string): void; /** * Get cache statistics for debugging * * @returns Object with cache stats */ getStats(): { collectionsValid: boolean; contentCollections: string[]; cachedImages: string[]; ttl: number; hasWatcher: boolean; }; } /** * Get or create the global cache instance * * @param options - Cache options (only used on first call) * @returns The global cache instance */ declare function getCache(options?: { ttl?: number; hasWatcher?: boolean; }): ServerCache; /** * Reset the global cache instance * * Useful for testing or when configuration changes. */ declare function resetCache(): void; /** * @fileoverview API route handlers for Writenex * * This module provides the API router that handles CRUD operations * for content collections. * * ## API Endpoints: * - GET /api/collections - List all collections * - GET /api/content/:collection - List content in collection * - GET /api/content/:collection/:id - Get single content item * - POST /api/content/:collection - Create new content * - PUT /api/content/:collection/:id - Update content * - DELETE /api/content/:collection/:id - Delete content * - GET /api/images/:collection/:contentId - Discover images for content * - GET /api/images/:collection/:contentId/* - Serve image file * - POST /api/images - Upload image * - GET /api/versions/:collection/:id - List versions * - GET /api/versions/:collection/:id/:versionId - Get version * - POST /api/versions/:collection/:id - Create manual version * - POST /api/versions/:collection/:id/:versionId/restore - Restore version * - GET /api/versions/:collection/:id/:versionId/diff - Get diff data * - DELETE /api/versions/:collection/:id/:versionId - Delete version * - DELETE /api/versions/:collection/:id - Clear all versions * * @module @writenex/astro/server/routes */ /** * Create the API router * * @param context - Middleware context * @returns Router function that handles API requests */ declare function createApiRouter(context: MiddlewareContext): (req: IncomingMessage, res: ServerResponse, path: string) => Promise; export { type MiddlewareContext, ServerCache, createApiRouter, createMiddleware, getCache, getClientDistPath, hasClientBundle, parseJsonBody, parseQueryParams, resetCache, sendError, sendJson, sendWritenexError, serveAsset, serveEditorHtml };