UNPKG

@api.global/typedserver

Version:

A TypeScript-based project for easy serving of static files with support for live reloading, compression, and typed requests.

656 lines 53.4 kB
import * as plugins from './plugins.js'; import * as interfaces from '../dist_ts_interfaces/index.js'; import { DevToolsController } from './controllers/controller.devtools.js'; import { TypedRequestController } from './controllers/controller.typedrequest.js'; import { BuiltInRoutesController } from './controllers/controller.builtin.js'; export class TypedServer { // instance options; smartServe; smartwatchInstance; serveDirHashSubject = new plugins.smartrx.rxjs.ReplaySubject(1); serveHash = '000000'; typedsocket; typedrouter = new plugins.typedrequest.TypedRouter(); // Sitemap helper sitemapHelper; smartmanifestInstance; // Decorated controllers devToolsController; typedRequestController; builtInRoutesController; // File server for static files fileServer; lastReload = Date.now(); ended = false; constructor(optionsArg) { const standardOptions = { port: 3000, injectReload: false, serveDir: null, watch: false, cors: true, }; this.options = { ...standardOptions, ...optionsArg, }; } /** * Access sitemap URLs (for adding/replacing) */ get sitemap() { return this.sitemapHelper; } /** * Add a custom route handler * Supports Express-style path patterns like '/path/:param' and '/path/*splat' * @param path - The route path pattern * @param method - HTTP method (GET, POST, PUT, DELETE, PATCH, ALL) * @param handler - Async function that receives IRequestContext and returns Response or null */ addRoute(path, method, handler) { // Delegate to smartserve's ControllerRegistry plugins.smartserve.ControllerRegistry.addRoute(path, method, handler); } /** * inits and starts the server */ async start() { // Validate essential configuration before starting if (this.options.injectReload && !this.options.serveDir) { throw new Error('You set to inject the reload script without a serve dir. This is not supported at the moment.'); } const port = typeof this.options.port === 'string' ? parseInt(this.options.port, 10) : this.options.port || 3000; // Initialize optional helpers if (this.options.sitemap) { this.sitemapHelper = new SitemapHelper(this.options.domain); } if (this.options.manifest) { this.smartmanifestInstance = new plugins.smartmanifest.SmartManifest(this.options.manifest); } // Initialize file server for static files if (this.options.serveDir) { this.fileServer = new plugins.smartserve.FileServer({ root: this.options.serveDir, index: ['index.html'], etag: true, }); } // Initialize decorated controllers if (this.options.injectReload) { this.devToolsController = new DevToolsController({ getLastReload: () => this.lastReload, getEnded: () => this.ended, }); } this.typedRequestController = new TypedRequestController(this.typedrouter); this.builtInRoutesController = new BuiltInRoutesController({ domain: this.options.domain, robots: this.options.robots, manifest: this.smartmanifestInstance, sitemap: this.options.sitemap, feed: this.options.feed, appVersion: this.options.appVersion, feedMetadata: this.options.feedMetadata, articleGetterFunction: this.options.articleGetterFunction, blockWaybackMachine: this.options.blockWaybackMachine, getSitemapUrls: () => this.sitemapHelper?.urls || [], }); // Register controllers with SmartServe's ControllerRegistry // Note: @Route decorators auto-register classes at import time. // Controllers with constructor args (like DevToolsController) use default no-op // constructors to handle auto-instantiation gracefully. if (this.options.injectReload) { plugins.smartserve.ControllerRegistry.registerInstance(this.devToolsController); } plugins.smartserve.ControllerRegistry.registerInstance(this.typedRequestController); plugins.smartserve.ControllerRegistry.registerInstance(this.builtInRoutesController); // Compile routes for fast matching plugins.smartserve.ControllerRegistry.compileRoutes(); // Build SmartServe options const smartServeOptions = { port, hostname: '0.0.0.0', compression: this.options.compression, tls: this.options.privateKey && this.options.publicKey ? { key: this.options.privateKey, cert: this.options.publicKey, } : undefined, websocket: { typedRouter: this.typedrouter, onConnectionOpen: (peer) => { peer.tags.add('allClients'); console.log(`WebSocket connected: ${peer.id}`); }, onConnectionClose: (peer) => { console.log(`WebSocket disconnected: ${peer.id}`); }, }, }; this.smartServe = new plugins.smartserve.SmartServe(smartServeOptions); // Set up custom request handler that integrates with ControllerRegistry this.smartServe.setHandler(async (request) => { return this.handleRequest(request); }); // Setup file watching if (this.options.watch && this.options.serveDir) { try { // Use glob pattern to match all files recursively in serveDir const watchGlob = this.options.serveDir.endsWith('/') ? `${this.options.serveDir}**/*` : `${this.options.serveDir}/**/*`; this.smartwatchInstance = new plugins.smartwatch.Smartwatch([watchGlob]); await this.smartwatchInstance.start(); (await this.smartwatchInstance.getObservableFor('change')).subscribe(async () => { await this.createServeDirHash(); this.reload(); }); await this.createServeDirHash(); } catch (error) { console.error('Failed to initialize file watching:', error); } } // Start the server await this.smartServe.start(); console.log(`TypedServer listening on port ${port}`); // Setup TypedSocket using SmartServe integration try { this.typedsocket = plugins.typedsocket.TypedSocket.fromSmartServe(this.smartServe, this.typedrouter); // Setup typedrouter handlers this.typedrouter.addTypedHandler(new plugins.typedrequest.TypedHandler('getLatestServerChangeTime', async () => { return { time: this.lastReload, }; })); // Speedtest handler for service worker dashboard // Client calls this in a loop for the test duration to get accurate time-based measurements this.typedrouter.addTypedHandler(new plugins.typedrequest.TypedHandler('serviceworker_speedtest', async (reqArg) => { const chunkSizeKB = reqArg.chunkSizeKB || 64; const sizeBytes = chunkSizeKB * 1024; let payload; let bytesTransferred = 0; switch (reqArg.type) { case 'download_chunk': // Generate chunk data for download test payload = 'x'.repeat(sizeBytes); bytesTransferred = sizeBytes; break; case 'upload_chunk': // Acknowledge received upload data bytesTransferred = reqArg.payload?.length || 0; break; case 'latency': // Simple ping - minimal data bytesTransferred = 0; break; } return { bytesTransferred, timestamp: Date.now(), payload }; })); } catch (error) { console.error('Failed to initialize TypedSocket:', error); } } /** * Create an IRequestContext from a Request */ createContext(request, params) { const url = new URL(request.url); const method = request.method.toUpperCase(); // Parse query params const query = {}; url.searchParams.forEach((value, key) => { query[key] = value; }); // Cached body parsers (lazy evaluation) let jsonCache; let textCache; let arrayBufferCache; let formDataCache; return { request, params, query, headers: request.headers, path: url.pathname, method, url, runtime: 'node', state: {}, async json() { if (jsonCache === undefined) { jsonCache = await request.clone().json(); } return jsonCache; }, async text() { if (textCache === undefined) { textCache = await request.clone().text(); } return textCache; }, async arrayBuffer() { if (arrayBufferCache === undefined) { arrayBufferCache = await request.clone().arrayBuffer(); } return arrayBufferCache; }, async formData() { if (formDataCache === undefined) { formDataCache = await request.clone().formData(); } return formDataCache; }, }; } /** * Build CSP header string from configuration */ buildCspHeader(csp) { const directives = []; const addDirective = (name, value) => { if (value) { const sources = Array.isArray(value) ? value.join(' ') : value; directives.push(`${name} ${sources}`); } }; addDirective('default-src', csp.defaultSrc); addDirective('script-src', csp.scriptSrc); addDirective('style-src', csp.styleSrc); addDirective('img-src', csp.imgSrc); addDirective('font-src', csp.fontSrc); addDirective('connect-src', csp.connectSrc); addDirective('media-src', csp.mediaSrc); addDirective('frame-src', csp.frameSrc); addDirective('object-src', csp.objectSrc); addDirective('worker-src', csp.workerSrc); addDirective('form-action', csp.formAction); addDirective('frame-ancestors', csp.frameAncestors); addDirective('base-uri', csp.baseUri); if (csp.reportUri) { directives.push(`report-uri ${csp.reportUri}`); } if (csp.reportTo) { directives.push(`report-to ${csp.reportTo}`); } if (csp.upgradeInsecureRequests) { directives.push('upgrade-insecure-requests'); } if (csp.blockAllMixedContent) { directives.push('block-all-mixed-content'); } return directives.join('; '); } /** * Apply all configured headers (CORS, security) to a response */ applyResponseHeaders(response) { const headers = new Headers(response.headers); // CORS headers if (this.options.cors) { headers.set('Access-Control-Allow-Origin', '*'); headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS, PATCH'); headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With'); headers.set('Access-Control-Max-Age', '86400'); } // Security headers const security = this.options.securityHeaders; if (security) { // Content Security Policy if (security.csp) { const cspHeader = this.buildCspHeader(security.csp); if (cspHeader) { headers.set('Content-Security-Policy', cspHeader); } } // X-Frame-Options if (security.xFrameOptions) { headers.set('X-Frame-Options', security.xFrameOptions); } // X-Content-Type-Options if (security.xContentTypeOptions) { headers.set('X-Content-Type-Options', 'nosniff'); } // X-XSS-Protection if (security.xXssProtection) { const value = typeof security.xXssProtection === 'string' ? security.xXssProtection : '1; mode=block'; headers.set('X-XSS-Protection', value); } // Referrer-Policy if (security.referrerPolicy) { headers.set('Referrer-Policy', security.referrerPolicy); } // Strict-Transport-Security (HSTS) if (security.hstsMaxAge !== undefined) { let hsts = `max-age=${security.hstsMaxAge}`; if (security.hstsIncludeSubDomains) { hsts += '; includeSubDomains'; } if (security.hstsPreload) { hsts += '; preload'; } headers.set('Strict-Transport-Security', hsts); } // Permissions-Policy if (security.permissionsPolicy) { const policies = Object.entries(security.permissionsPolicy) .map(([feature, allowlist]) => `${feature}=(${allowlist.join(' ')})`) .join(', '); if (policies) { headers.set('Permissions-Policy', policies); } } // Cross-Origin-Opener-Policy if (security.crossOriginOpenerPolicy) { headers.set('Cross-Origin-Opener-Policy', security.crossOriginOpenerPolicy); } // Cross-Origin-Embedder-Policy if (security.crossOriginEmbedderPolicy) { headers.set('Cross-Origin-Embedder-Policy', security.crossOriginEmbedderPolicy); } // Cross-Origin-Resource-Policy if (security.crossOriginResourcePolicy) { headers.set('Cross-Origin-Resource-Policy', security.crossOriginResourcePolicy); } } return new Response(response.body, { status: response.status, statusText: response.statusText, headers }); } /** * Main request handler - routes to appropriate sub-handlers */ async handleRequest(request) { const url = new URL(request.url); const path = url.pathname; const method = request.method.toUpperCase(); // Handle OPTIONS preflight for CORS if (method === 'OPTIONS' && this.options.cors) { return this.applyResponseHeaders(new Response(null, { status: 204 })); } // Process the request and wrap response with all configured headers const response = await this.handleRequestInternal(request, path, method); return this.applyResponseHeaders(response); } /** * Internal request handler - routes to appropriate sub-handlers */ async handleRequestInternal(request, path, method) { // First, try to match via ControllerRegistry (decorated routes) const match = plugins.smartserve.ControllerRegistry.matchRoute(path, method); if (match) { try { const context = this.createContext(request, match.params); const result = await match.route.handler(context); // Handle Response or convert to Response if (result instanceof Response) { return result; } return new Response(JSON.stringify(result), { status: 200, headers: { 'Content-Type': 'application/json' }, }); } catch (error) { if (error instanceof plugins.smartserve.RouteNotFoundError) { // Route explicitly threw "not found", continue to other handlers } else { console.error('Controller error:', error); return new Response('Internal Server Error', { status: 500 }); } } } // HTML injection for reload (if enabled) if (this.options.injectReload && this.options.serveDir) { const response = await this.handleHtmlWithInjection(request); if (response) return response; } // Try static file serving if (this.fileServer && (method === 'GET' || method === 'HEAD')) { try { const staticResponse = await this.fileServer.serve(request); if (staticResponse) { return staticResponse; } } catch (error) { // Fall through to 404 } } // Default answer for root if (path === '/' && method === 'GET' && this.options.defaultAnswer) { const html = await this.options.defaultAnswer(); return new Response(html, { status: 200, headers: { 'Content-Type': 'text/html' }, }); } // SPA fallback - serve index.html for non-file routes if (this.options.spaFallback && this.options.serveDir && method === 'GET' && !path.includes('.')) { try { const indexPath = plugins.path.join(this.options.serveDir, 'index.html'); let html = await plugins.fsInstance.file(indexPath).encoding('utf8').read(); // Inject reload script if enabled if (this.options.injectReload && html.includes('<head>')) { const injection = `<head> <!-- injected by @apiglobal/typedserver start --> <script async defer type="module" src="/typedserver/devtools"></script> <script> globalThis.typedserver = { lastReload: ${this.lastReload}, versionInfo: ${JSON.stringify({}, null, 2)}, } </script> <!-- injected by @apiglobal/typedserver stop --> `; html = html.replace('<head>', injection); } return new Response(html, { status: 200, headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache, no-store, must-revalidate', appHash: this.serveHash, }, }); } catch { // Fall through to 404 } } // Not found return new Response('Not Found', { status: 404 }); } /** * Handle HTML files with reload script injection */ async handleHtmlWithInjection(request) { const url = new URL(request.url); const requestPath = url.pathname; // Check if this is a request for an HTML file or root if (requestPath === '/' || requestPath.endsWith('.html') || !requestPath.includes('.')) { try { let filePath = requestPath === '/' ? 'index.html' : requestPath.slice(1); if (!filePath.endsWith('.html') && !filePath.includes('.')) { filePath = plugins.path.join(filePath, 'index.html'); } const fullPath = plugins.path.join(this.options.serveDir, filePath); // Security check if (!fullPath.startsWith(this.options.serveDir)) { return new Response('Forbidden', { status: 403 }); } let fileContent = (await plugins.fsInstance .file(fullPath) .encoding('utf8') .read()); // Inject reload script if (fileContent.includes('<head>')) { const injection = `<head> <!-- injected by @apiglobal/typedserver start --> <script async defer type="module" src="/typedserver/devtools"></script> <script> globalThis.typedserver = { lastReload: ${this.lastReload}, versionInfo: ${JSON.stringify({}, null, 2)}, } </script> <!-- injected by @apiglobal/typedserver stop --> `; fileContent = fileContent.replace('<head>', injection); console.log('injected typedserver script.'); } return new Response(fileContent, { status: 200, headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache, no-store, must-revalidate', Pragma: 'no-cache', Expires: '0', appHash: this.serveHash, }, }); } catch (error) { // Fall through to default handling } } return null; } /** * reloads the page */ async reload() { this.lastReload = Date.now(); if (!this.typedsocket) { console.warn('TypedSocket not initialized, skipping client notifications'); return; } // Push cache invalidation to service workers first try { const swConnections = await this.typedsocket.findAllTargetConnectionsByTag('serviceworker'); for (const connection of swConnections) { const pushCacheInvalidate = this.typedsocket.createTypedRequest('serviceworker_cacheInvalidate', connection); pushCacheInvalidate.fire({ reason: 'File change detected', timestamp: this.lastReload, }).catch(err => { console.warn('Failed to push cache invalidation to service worker:', err); }); } if (swConnections.length > 0) { console.log(`Pushed cache invalidation to ${swConnections.length} service worker(s)`); } } catch (error) { console.warn('Failed to notify service workers:', error); } // Notify frontend clients try { const connections = await this.typedsocket.findAllTargetConnectionsByTag('typedserver_frontend'); for (const connection of connections) { const pushTime = this.typedsocket.createTypedRequest('pushLatestServerChangeTime', connection); pushTime.fire({ time: this.lastReload, }).catch(err => { console.warn('Failed to push latest server change time to client:', err); }); } } catch (error) { console.error('Failed to notify clients about reload:', error); } } /** * Stops the server and cleans up resources */ async stop() { this.ended = true; const stopWithErrorHandling = async (stopFn, componentName) => { try { await stopFn(); } catch (err) { console.error(`Error stopping ${componentName}:`, err); } }; const tasks = []; // Stop SmartServe if (this.smartServe) { tasks.push(stopWithErrorHandling(() => this.smartServe.stop(), 'SmartServe')); } // Stop TypedSocket (in SmartServe mode, this is a no-op but good for cleanup) if (this.typedsocket) { tasks.push(stopWithErrorHandling(() => this.typedsocket.stop(), 'TypedSocket')); } // Stop file watcher if (this.smartwatchInstance) { tasks.push(stopWithErrorHandling(() => this.smartwatchInstance.stop(), 'file watcher')); } await Promise.all(tasks); } /** * Calculates a hash of the served directory for cache busting */ async createServeDirHash() { try { const serveDirHash = await plugins.fsInstance .directory(this.options.serveDir) .recursive() .treeHash(); this.serveHash = serveDirHash.slice(0, 12); console.log('Current ServeDir hash: ' + this.serveHash); this.serveDirHashSubject.next(this.serveHash); } catch (error) { console.error('Failed to create serve directory hash:', error); const fallbackHash = Date.now().toString(16).slice(-6); this.serveHash = fallbackHash; console.log('Using fallback hash: ' + fallbackHash); this.serveDirHashSubject.next(fallbackHash); } } } // ============================================================================ // Helper Classes // ============================================================================ /** * Sitemap helper class */ class SitemapHelper { smartSitemap = new plugins.smartsitemap.SmartSitemap(); urls = []; constructor(domain) { if (domain) { this.urls.push({ url: `https://${domain}/`, timestamp: Date.now(), frequency: 'daily', }); } } async createSitemap() { return this.smartSitemap.createSitemapFromUrlInfoArray(this.urls); } async createSitemapNews(articles) { return this.smartSitemap.createSitemapNewsFromArticleArray(articles); } replaceUrls(urlsArg) { this.urls = urlsArg; } addUrls(urlsArg) { this.urls = this.urls.concat(urlsArg); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy50eXBlZHNlcnZlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMudHlwZWRzZXJ2ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxLQUFLLFVBQVUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUM3RCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUMxRSxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSwwQ0FBMEMsQ0FBQztBQUNsRixPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxxQ0FBcUMsQ0FBQztBQXFKOUUsTUFBTSxPQUFPLFdBQVc7SUFDdEIsV0FBVztJQUNKLE9BQU8sQ0FBaUI7SUFDeEIsVUFBVSxDQUFnQztJQUMxQyxrQkFBa0IsQ0FBZ0M7SUFDbEQsbUJBQW1CLEdBQUcsSUFBSSxPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxhQUFhLENBQVMsQ0FBQyxDQUFDLENBQUM7SUFDeEUsU0FBUyxHQUFXLFFBQVEsQ0FBQztJQUM3QixXQUFXLENBQWtDO0lBQzdDLFdBQVcsR0FBRyxJQUFJLE9BQU8sQ0FBQyxZQUFZLENBQUMsV0FBVyxFQUFFLENBQUM7SUFFNUQsaUJBQWlCO0lBQ1QsYUFBYSxDQUFnQjtJQUM3QixxQkFBcUIsQ0FBc0M7SUFFbkUsd0JBQXdCO0lBQ2hCLGtCQUFrQixDQUFxQjtJQUN2QyxzQkFBc0IsQ0FBeUI7SUFDL0MsdUJBQXVCLENBQTBCO0lBRXpELCtCQUErQjtJQUN2QixVQUFVLENBQWdDO0lBRTNDLFVBQVUsR0FBVyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDaEMsS0FBSyxHQUFHLEtBQUssQ0FBQztJQUVyQixZQUFZLFVBQTBCO1FBQ3BDLE1BQU0sZUFBZSxHQUFtQjtZQUN0QyxJQUFJLEVBQUUsSUFBSTtZQUNWLFlBQVksRUFBRSxLQUFLO1lBQ25CLFFB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