theokit 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-NSKFOAFX.js +49 -0
- package/dist/build-NSKFOAFX.js.map +1 -0
- package/dist/chunk-ASGEGAWL.js +480 -0
- package/dist/chunk-ASGEGAWL.js.map +1 -0
- package/dist/chunk-ATSTRYYT.js +894 -0
- package/dist/chunk-ATSTRYYT.js.map +1 -0
- package/dist/chunk-KPU44T6G.js +71 -0
- package/dist/chunk-KPU44T6G.js.map +1 -0
- package/dist/chunk-MMZZBPMX.js +335 -0
- package/dist/chunk-MMZZBPMX.js.map +1 -0
- package/dist/chunk-N5YH2UDG.js +85 -0
- package/dist/chunk-N5YH2UDG.js.map +1 -0
- package/dist/chunk-SAVVU5LG.js +66 -0
- package/dist/chunk-SAVVU5LG.js.map +1 -0
- package/dist/chunk-TXMUCDJT.js +43 -0
- package/dist/chunk-TXMUCDJT.js.map +1 -0
- package/dist/chunk-U3OJFWK3.js +162 -0
- package/dist/chunk-U3OJFWK3.js.map +1 -0
- package/dist/cli/index.js +79 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/client/index.d.ts +37 -0
- package/dist/client/index.js +57 -0
- package/dist/client/index.js.map +1 -0
- package/dist/cloudflare-CVGN7FOU.js +88 -0
- package/dist/cloudflare-CVGN7FOU.js.map +1 -0
- package/dist/dev-7UJK3M2O.js +47 -0
- package/dist/dev-7UJK3M2O.js.map +1 -0
- package/dist/docker-M253W54T.js +101 -0
- package/dist/docker-M253W54T.js.map +1 -0
- package/dist/generate-AA7ZE42F.js +116 -0
- package/dist/generate-AA7ZE42F.js.map +1 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.js +171 -0
- package/dist/index.js.map +1 -0
- package/dist/rate-limit-C6hHXIj1.d.ts +13 -0
- package/dist/routes-YP357VAC.js +60 -0
- package/dist/routes-YP357VAC.js.map +1 -0
- package/dist/server/index.d.ts +94 -0
- package/dist/server/index.js +148 -0
- package/dist/server/index.js.map +1 -0
- package/dist/start-BIS3RCFQ.js +243 -0
- package/dist/start-BIS3RCFQ.js.map +1 -0
- package/dist/vercel-747SR2FB.js +80 -0
- package/dist/vercel-747SR2FB.js.map +1 -0
- package/dist/vite-plugin/index.d.ts +12 -0
- package/dist/vite-plugin/index.js +8 -0
- package/dist/vite-plugin/index.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/vite-plugin/index.ts","../src/router/scan.ts","../src/router/types.ts","../src/router/generate.ts","../src/router/entry.ts","../src/router/entry-server.ts","../src/vite-plugin/api-middleware.ts","../src/server/scan.ts","../src/server/match.ts","../src/server/middleware-runner.ts","../src/server/execute.ts","../src/server/module-loader.ts","../src/server/logger.ts","../src/vite-plugin/action-middleware.ts","../src/server/action-scan.ts","../src/server/csrf.ts","../src/server/action-execute.ts","../src/server/ws-scan.ts"],"sourcesContent":["import type { Plugin } from 'vite'\nimport { resolve, basename, dirname } from 'node:path'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { scanRoutes } from '../router/scan.js'\nimport { generateRouteManifest } from '../router/generate.js'\nimport { generateEntryClient } from '../router/entry.js'\nimport { generateEntryServer } from '../router/entry-server.js'\nimport { isRouteFile } from '../router/types.js'\nimport { createApiMiddleware } from './api-middleware.js'\nimport { createActionMiddleware } from './action-middleware.js'\nimport { scanWebSocketRoutes } from '../server/ws-scan.js'\nimport type { RateLimitConfig } from '../server/rate-limit.js'\n\nconst VIRTUAL_ENTRY_ID = '/@theo/entry-client'\nconst RESOLVED_ENTRY_ID = '\\0@theo/entry-client'\nconst VIRTUAL_MANIFEST_ID = '/@theo/route-manifest'\nconst RESOLVED_MANIFEST_ID = '\\0@theo/route-manifest'\nconst VIRTUAL_ENTRY_SERVER_ID = '/@theo/entry-server'\nconst RESOLVED_ENTRY_SERVER_ID = '\\0@theo/entry-server'\n\nexport interface TheoPluginOptions {\n root?: string\n rateLimit?: RateLimitConfig\n ssr?: boolean\n}\n\nexport function theoPlugin(rootOrOptions?: string | TheoPluginOptions): Plugin {\n const options = typeof rootOrOptions === 'string' ? { root: rootOrOptions } : (rootOrOptions ?? {})\n const projectRoot = options.root ?? process.cwd()\n const appDir = resolve(projectRoot, 'app')\n const ssrEnabled = options.ssr ?? false\n\n // Resolve paths for SSR module loading\n const currentDir = dirname(fileURLToPath(import.meta.url))\n const theoSrcDir = resolve(currentDir, '..')\n\n return {\n name: 'theo',\n\n config() {\n // Detect whether we're running from source (.ts) or compiled dist (.js)\n const ext = existsSync(resolve(theoSrcDir, 'index.ts')) ? '.ts' : '.js'\n return {\n envPrefix: 'THEO_PUBLIC_',\n resolve: {\n alias: [\n { find: 'theokit/server', replacement: resolve(theoSrcDir, `server/index${ext}`) },\n { find: 'theokit', replacement: resolve(theoSrcDir, `index${ext}`) },\n ],\n },\n }\n },\n\n resolveId(id: string) {\n if (id === VIRTUAL_ENTRY_ID) return RESOLVED_ENTRY_ID\n if (id === VIRTUAL_MANIFEST_ID) return RESOLVED_MANIFEST_ID\n if (id === VIRTUAL_ENTRY_SERVER_ID) return RESOLVED_ENTRY_SERVER_ID\n },\n\n load(id: string) {\n if (id === RESOLVED_ENTRY_ID) {\n return generateEntryClient(ssrEnabled)\n }\n if (id === RESOLVED_MANIFEST_ID) {\n const tree = scanRoutes(appDir)\n return generateRouteManifest(tree)\n }\n if (id === RESOLVED_ENTRY_SERVER_ID) {\n return generateEntryServer()\n }\n },\n\n configureServer(server) {\n // Server middleware (action before API — more specific prefix first)\n const serverDir = resolve(projectRoot, 'server')\n server.middlewares.use(createActionMiddleware(server, serverDir))\n server.middlewares.use(createApiMiddleware(server, serverDir, options.rateLimit))\n\n // SSR dev middleware\n if (ssrEnabled) {\n server.middlewares.use(async (req, res, next) => {\n const url = req.url ?? '/'\n // Skip API, static, and HMR requests\n if (url.startsWith('/api/') || url.startsWith('/@') || url.startsWith('/node_modules/') || url.includes('.')) {\n return next()\n }\n\n try {\n const indexPath = resolve(projectRoot, 'index.html')\n let template = readFileSync(indexPath, 'utf-8')\n template = await server.transformIndexHtml(url, template)\n\n const mod = await server.ssrLoadModule(VIRTUAL_ENTRY_SERVER_ID)\n const result = await mod.render(url)\n\n if (result && typeof result === 'object' && 'redirect' in result) {\n res.writeHead(302, { Location: (result.redirect as Response).headers.get('location') ?? '/' })\n res.end()\n return\n }\n\n // render() returns HTML string — inject into template\n const ssrHtml = result as string\n const rootDivMatch = template.match(/<div id=[\"']root[\"'][^>]*>/)\n if (!rootDivMatch) {\n res.writeHead(200, { 'Content-Type': 'text/html' })\n res.end(template)\n return\n }\n\n const splitIdx = template.indexOf(rootDivMatch[0]) + rootDivMatch[0].length\n const html = template.slice(0, splitIdx) + ssrHtml + template.slice(splitIdx)\n\n res.writeHead(200, { 'Content-Type': 'text/html' })\n res.end(html)\n } catch (err) {\n server.ssrFixStacktrace(err as Error)\n console.error('[SSR Dev Error]', err)\n // Fallback to CSR\n return next()\n }\n })\n }\n\n // Frontend HMR watcher\n function handleRouteChange(filePath: string) {\n if (!isRouteFile(basename(filePath))) return\n if (!filePath.startsWith(appDir)) return\n\n const mod = server.moduleGraph.getModuleById(RESOLVED_MANIFEST_ID)\n if (mod) {\n server.moduleGraph.invalidateModule(mod)\n server.ws.send({ type: 'full-reload' })\n }\n }\n\n server.watcher.on('add', handleRouteChange)\n server.watcher.on('unlink', handleRouteChange)\n\n // WebSocket upgrade handler (dev mode)\n const wsRoutes = scanWebSocketRoutes(resolve(projectRoot, 'server'))\n if (wsRoutes.length > 0 && server.httpServer) {\n import('ws').then(({ WebSocketServer }) => {\n const wss = new WebSocketServer({ noServer: true })\n\n server.httpServer!.on('upgrade', async (request, socket, head) => {\n const url = request.url ?? '/'\n if (!url.startsWith('/ws/')) return // Let Vite handle HMR etc.\n\n const wsPath = url.split('?')[0]\n const match = wsRoutes.find(r => r.wsPath === wsPath)\n if (!match) { socket.destroy(); return }\n\n try {\n const mod = await server.ssrLoadModule(match.filePath)\n const handler = mod.default ?? mod\n\n wss.handleUpgrade(request, socket, head, (ws) => {\n handler.onOpen?.(ws, request)\n ws.on('message', (data: Buffer) => handler.onMessage?.(ws, data.toString()))\n ws.on('close', (code: number, reason: Buffer) => handler.onClose?.(ws, code, reason))\n ws.on('error', (err: Error) => handler.onError?.(ws, err))\n })\n } catch {\n socket.destroy()\n }\n })\n }).catch(() => {\n console.warn('[Theo] WebSocket routes found but \"ws\" package not installed. Run: npm install ws')\n })\n }\n },\n }\n}\n","import { readdirSync, statSync, existsSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\nimport type { RouteNode, RouteFileName } from './types.js'\nimport { ROUTE_FILE_NAMES, ROUTE_FILE_EXTENSIONS } from './types.js'\n\nfunction toNodeKey(name: RouteFileName): 'page' | 'layout' | 'error' | 'loading' | 'notFound' {\n if (name === 'not-found') return 'notFound'\n return name as 'page' | 'layout' | 'error' | 'loading'\n}\n\nfunction setRouteFile(node: RouteNode, key: 'page' | 'layout' | 'error' | 'loading' | 'notFound', value: string): void {\n switch (key) {\n case 'page': node.page = value; break\n case 'layout': node.layout = value; break\n case 'error': node.error = value; break\n case 'loading': node.loading = value; break\n case 'notFound': node.notFound = value; break\n }\n}\n\nfunction scanDir(dir: string, segment: string, routePath: string): RouteNode {\n const node: RouteNode = { segment, path: routePath, children: [] }\n\n const entries = readdirSync(dir, { withFileTypes: true })\n\n // Check route files with extension priority (.tsx > .ts > .jsx > .js)\n for (const name of ROUTE_FILE_NAMES) {\n const key = toNodeKey(name)\n if (node[key] !== undefined) continue\n for (const ext of ROUTE_FILE_EXTENSIONS) {\n const filename = `${name}${ext}`\n if (existsSync(join(dir, filename))) {\n setRouteFile(node, key, resolve(dir, filename))\n break\n }\n }\n }\n\n // Recurse into subdirectories\n for (const entry of entries) {\n if (!entry.isDirectory()) continue\n if (entry.name.startsWith('_') || entry.name.startsWith('.')) continue\n\n const childPath =\n routePath === '/' ? `/${entry.name}` : `${routePath}/${entry.name}`\n const child = scanDir(join(dir, entry.name), entry.name, childPath)\n\n // Prune empty nodes\n const hasRouteFile =\n child.page ||\n child.layout ||\n child.error ||\n child.loading ||\n child.notFound\n if (hasRouteFile || child.children.length > 0) {\n node.children.push(child)\n }\n }\n\n return node\n}\n\nexport function scanRoutes(appDir: string): RouteNode {\n if (!existsSync(appDir)) {\n throw new Error(`App directory does not exist: ${appDir}`)\n }\n if (!statSync(appDir).isDirectory()) {\n throw new Error(`App path is not a directory: ${appDir}`)\n }\n return scanDir(appDir, '', '/')\n}\n","export interface RouteNode {\n segment: string\n path: string\n page?: string\n layout?: string\n error?: string\n loading?: string\n notFound?: string\n children: RouteNode[]\n}\n\nexport const ROUTE_FILE_NAMES = [\n 'page',\n 'layout',\n 'error',\n 'loading',\n 'not-found',\n] as const\n\nexport type RouteFileName = (typeof ROUTE_FILE_NAMES)[number]\n\nexport const ROUTE_FILE_EXTENSIONS = ['.tsx', '.ts', '.jsx', '.js'] as const\n\nconst ROUTE_FILE_REGEX =\n /^(page|layout|error|loading|not-found)\\.(tsx|ts|jsx|js)$/\n\nexport function isRouteFile(filename: string): boolean {\n return ROUTE_FILE_REGEX.test(filename)\n}\n","import type { RouteNode } from './types.js'\n\nfunction normalizePath(p: string): string {\n return p.replace(/\\\\/g, '/')\n}\n\nfunction safeVarName(segment: string, prefix: string): string {\n const safe = segment.replace(/[^a-zA-Z0-9]/g, '_') || 'root'\n return `${prefix}_${safe}`\n}\n\ninterface ImportEntry {\n varName: string\n importPath: string\n}\n\nexport function generateRouteManifest(tree: RouteNode): string {\n const imports: ImportEntry[] = []\n let hasLayout = false\n\n function collectImports(node: RouteNode): void {\n const seg = node.segment || 'root'\n if (node.page) {\n imports.push({\n varName: safeVarName(seg, 'Page'),\n importPath: normalizePath(node.page),\n })\n }\n if (node.layout) {\n hasLayout = true\n imports.push({\n varName: safeVarName(seg, 'Layout'),\n importPath: normalizePath(node.layout),\n })\n }\n if (node.error) {\n imports.push({\n varName: safeVarName(seg, 'Error'),\n importPath: normalizePath(node.error),\n })\n }\n if (node.loading) {\n imports.push({\n varName: safeVarName(seg, 'Loading'),\n importPath: normalizePath(node.loading),\n })\n }\n if (node.notFound) {\n imports.push({\n varName: safeVarName(seg, 'NotFound'),\n importPath: normalizePath(node.notFound),\n })\n }\n for (const child of node.children) {\n collectImports(child)\n }\n }\n\n collectImports(tree)\n\n // Build import lines\n const lines: string[] = [\n `import React, { Suspense, lazy } from 'react'`,\n ]\n\n if (hasLayout) {\n lines.push(`import { Outlet } from 'react-router'`)\n }\n\n lines.push('')\n\n for (const imp of imports) {\n lines.push(\n `const ${imp.varName} = lazy(() => import('${imp.importPath}'))`,\n )\n }\n\n lines.push('')\n\n // Generate route config\n function genRouteConfig(node: RouteNode, isRoot: boolean): string {\n const seg = node.segment || 'root'\n const childConfigs: string[] = []\n\n // Index route for this node's page\n if (node.page) {\n const pageVar = safeVarName(seg, 'Page')\n let pageElement = `React.createElement(${pageVar})`\n if (node.loading) {\n const loadVar = safeVarName(seg, 'Loading')\n pageElement = `React.createElement(Suspense, { fallback: React.createElement(${loadVar}) }, ${pageElement})`\n }\n childConfigs.push(`{ index: true, element: ${pageElement} }`)\n }\n\n // Child routes\n for (const child of node.children) {\n childConfigs.push(genRouteConfig(child, false))\n }\n\n // Not-found wildcard (only at this level)\n if (node.notFound) {\n const nfVar = safeVarName(seg, 'NotFound')\n childConfigs.push(\n `{ path: '*', element: React.createElement(${nfVar}) }`,\n )\n }\n\n // Wrap children in error boundary (pathless wrapper) if error exists\n let childrenArray = `[${childConfigs.join(', ')}]`\n if (node.error) {\n const errVar = safeVarName(seg, 'Error')\n childrenArray = `[{ errorElement: React.createElement(${errVar}), children: ${childrenArray} }]`\n }\n\n // Build route object\n if (node.layout) {\n const layoutVar = safeVarName(seg, 'Layout')\n const pathPart = isRoot\n ? `path: '/'`\n : `path: '${node.segment}'`\n return `{ ${pathPart}, element: React.createElement(${layoutVar}), children: ${childrenArray} }`\n }\n\n // No layout — if root, wrap in path '/'\n if (isRoot) {\n if (childConfigs.length === 0 && !node.page) {\n return `{ path: '/', children: [] }`\n }\n // Root without layout: children are direct routes\n return `{ path: '/', children: ${childrenArray} }`\n }\n\n // Child segment without layout — just a route\n if (node.page && node.children.length === 0 && !node.error && !node.notFound) {\n const pageVar = safeVarName(seg, 'Page')\n let pageElement = `React.createElement(${pageVar})`\n if (node.loading) {\n const loadVar = safeVarName(seg, 'Loading')\n pageElement = `React.createElement(Suspense, { fallback: React.createElement(${loadVar}) }, ${pageElement})`\n }\n return `{ path: '${node.segment}', element: ${pageElement} }`\n }\n\n // Child with children but no layout\n return `{ path: '${node.segment}', children: ${childrenArray} }`\n }\n\n const routeConfig = genRouteConfig(tree, true)\n lines.push(`export const routes = [${routeConfig}]`)\n\n return lines.join('\\n')\n}\n","export function generateEntryClient(ssr?: boolean): string {\n const rootMethod = ssr ? 'hydrateRoot' : 'createRoot'\n const renderCall = ssr\n ? ` ${rootMethod}(el,\\n React.createElement(Suspense, { fallback: null },\\n React.createElement(RouterProvider, { router })\\n )\\n )`\n : ` ${rootMethod}(el).render(\\n React.createElement(Suspense, { fallback: null },\\n React.createElement(RouterProvider, { router })\\n )\\n )`\n\n return [\n `import React, { Suspense } from 'react'`,\n `import { ${rootMethod} } from 'react-dom/client'`,\n `import { createBrowserRouter, RouterProvider } from 'react-router'`,\n `import { routes } from '/@theo/route-manifest'`,\n ``,\n `const router = createBrowserRouter(routes)`,\n `const el = document.getElementById('root')`,\n `if (el) {`,\n renderCall,\n `}`,\n ].join('\\n')\n}\n","export function generateEntryServer(): string {\n return [\n `import React from 'react'`,\n `import { renderToPipeableStream } from 'react-dom/server'`,\n `import { createStaticHandler, createStaticRouter, StaticRouterProvider } from 'react-router'`,\n `import { PassThrough } from 'node:stream'`,\n `import { routes } from '/@theo/route-manifest'`,\n ``,\n `export async function render(url) {`,\n ` const handler = createStaticHandler(routes)`,\n ` const request = new Request('http://localhost' + url)`,\n ` const context = await handler.query(request)`,\n ``,\n ` if (context instanceof Response) {`,\n ` return { redirect: context }`,\n ` }`,\n ``,\n ` const router = createStaticRouter(handler.dataRoutes, context)`,\n ` const app = React.createElement(StaticRouterProvider, { router, context })`,\n ``,\n ` return new Promise((resolve, reject) => {`,\n ` let html = ''`,\n ` const passthrough = new PassThrough()`,\n ` passthrough.on('data', (chunk) => { html += chunk.toString() })`,\n ` passthrough.on('end', () => { resolve(html) })`,\n ` passthrough.on('error', reject)`,\n ``,\n ` const { pipe } = renderToPipeableStream(app, {`,\n ` onAllReady() { pipe(passthrough) },`,\n ` onShellError(err) { reject(err) },`,\n ` onError(err) { console.error('[SSR Error]', err) },`,\n ` })`,\n ` })`,\n `}`,\n ].join('\\n')\n}\n","import type { ViteDevServer, Connect } from 'vite'\nimport { randomUUID } from 'node:crypto'\nimport { scanServerRoutes } from '../server/scan.js'\nimport { matchRoute } from '../server/match.js'\nimport { executeRoute, sendError } from '../server/execute.js'\nimport { createViteLoader } from '../server/module-loader.js'\nimport { logRequest } from '../server/logger.js'\nimport { createRateLimiter } from '../server/rate-limit.js'\nimport type { RateLimitConfig } from '../server/rate-limit.js'\n\nexport function createApiMiddleware(\n vite: ViteDevServer,\n serverDir: string,\n rateLimitConfig?: RateLimitConfig,\n): Connect.NextHandleFunction {\n const loadModule = createViteLoader(vite)\n const rateLimiter = rateLimitConfig ? createRateLimiter(rateLimitConfig) : null\n\n return async (req, res, next) => {\n const url = req.url ?? ''\n if (!url.startsWith('/api/')) {\n return next()\n }\n\n const requestId = randomUUID()\n const start = Date.now()\n res.setHeader('x-request-id', requestId)\n\n // Rate limit check\n if (rateLimiter) {\n const check = rateLimiter(req)\n for (const [k, v] of Object.entries(check.headers)) res.setHeader(k, v)\n if (check.limited) {\n sendError(res, 'RATE_LIMITED', 'Too many requests', 429, undefined, requestId)\n logRequest({ method: req.method ?? 'GET', url, status: 429, duration: Date.now() - start, requestId })\n return\n }\n }\n\n const routes = scanServerRoutes(serverDir)\n const match = matchRoute(url, routes)\n\n if (!match) {\n sendError(res, 'NOT_FOUND', 'API route not found', 404, undefined, requestId)\n logRequest({ method: req.method ?? 'GET', url, status: 404, duration: Date.now() - start, requestId })\n return\n }\n\n const method = (req.method ?? 'GET').toUpperCase()\n await executeRoute(match.route, method, match.params, req, res, loadModule, serverDir, requestId)\n logRequest({ method, url, status: res.statusCode, duration: Date.now() - start, requestId })\n }\n}\n","import { readdirSync, existsSync, statSync } from 'node:fs'\nimport { join, resolve, relative, extname, basename } from 'node:path'\nimport { compilePattern, type ServerRouteNode } from './match.js'\n\nconst ROUTE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx']\n\nfunction fileToRoutePath(filePath: string, routesDir: string): string {\n let rel = relative(routesDir, filePath)\n // Strip extension\n const ext = extname(rel)\n rel = rel.slice(0, -ext.length)\n // Normalize separators\n rel = rel.replace(/\\\\/g, '/')\n // Strip index suffix\n if (rel.endsWith('/index')) {\n rel = rel.slice(0, -6)\n } else if (rel === 'index') {\n rel = ''\n }\n // Replace [param] with :param\n rel = rel.replace(/\\[([^\\]]+)\\]/g, ':$1')\n return `/api/${rel}`\n}\n\nfunction scanDir(dir: string, routesDir: string, results: ServerRouteNode[]): void {\n const entries = readdirSync(dir, { withFileTypes: true })\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name)\n if (entry.isDirectory()) {\n if (!entry.name.startsWith('_') && !entry.name.startsWith('.')) {\n scanDir(fullPath, routesDir, results)\n }\n } else if (entry.isFile()) {\n const ext = extname(entry.name)\n if (!ROUTE_EXTENSIONS.includes(ext)) continue\n\n const routePath = fileToRoutePath(fullPath, routesDir)\n const { pattern, paramNames } = compilePattern(routePath)\n results.push({\n filePath: resolve(fullPath),\n routePath,\n paramNames,\n pattern,\n })\n }\n }\n}\n\nexport function scanServerRoutes(serverDir: string): ServerRouteNode[] {\n const routesDir = join(serverDir, 'routes')\n if (!existsSync(routesDir) || !statSync(routesDir).isDirectory()) {\n return []\n }\n\n const results: ServerRouteNode[] = []\n scanDir(routesDir, routesDir, results)\n\n // Sort: static routes before dynamic (routes without params first)\n results.sort((a, b) => {\n if (a.paramNames.length === 0 && b.paramNames.length > 0) return -1\n if (a.paramNames.length > 0 && b.paramNames.length === 0) return 1\n return a.routePath.localeCompare(b.routePath)\n })\n\n return results\n}\n","export interface ServerRouteNode {\n filePath: string\n routePath: string\n paramNames: string[]\n pattern: RegExp\n}\n\nexport function compilePattern(routePath: string): {\n pattern: RegExp\n paramNames: string[]\n} {\n const paramNames: string[] = []\n const regexStr = routePath.replace(/:([^/]+)/g, (_, name) => {\n paramNames.push(name)\n return '([^/]+)'\n })\n return { pattern: new RegExp(`^${regexStr}$`), paramNames }\n}\n\nexport function matchRoute(\n url: string,\n routes: ServerRouteNode[],\n): { route: ServerRouteNode; params: Record<string, string> } | null {\n // Strip query string and trailing slash\n let path = url.split('?')[0]\n if (path.length > 1 && path.endsWith('/')) {\n path = path.slice(0, -1)\n }\n\n for (const route of routes) {\n const match = route.pattern.exec(path)\n if (match) {\n const params: Record<string, string> = {}\n route.paramNames.forEach((name, i) => {\n params[name] = match[i + 1]\n })\n return { route, params }\n }\n }\n return null\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { LoadModule } from './module-loader.js'\nimport { existsSync } from 'node:fs'\nimport { join } from 'node:path'\n\nexport interface MiddlewareResult {\n ctx: unknown\n aborted: boolean\n}\n\nexport async function runMiddlewareAndContext(\n req: IncomingMessage,\n res: ServerResponse,\n loadModule: LoadModule,\n serverDir: string,\n): Promise<MiddlewareResult> {\n // 1. Run middleware (if exists)\n const middlewarePath = join(serverDir, 'middleware.ts')\n if (existsSync(middlewarePath)) {\n const mod = await loadModule(middlewarePath)\n const mw = mod.default\n if (typeof mw === 'function') {\n let nextCalled = false\n await mw(req, res, async () => {\n nextCalled = true\n })\n if (!nextCalled || res.writableEnded) {\n return { ctx: {}, aborted: true }\n }\n }\n }\n\n // 2. Create context (if exists)\n let ctx: unknown = {}\n const contextPath = join(serverDir, 'context.ts')\n if (existsSync(contextPath)) {\n const mod = await loadModule(contextPath)\n if (typeof mod.createContext === 'function') {\n ctx = await (mod.createContext as Function)({ request: req, response: res })\n }\n }\n\n return { ctx, aborted: false }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { ServerRouteNode } from './match.js'\nimport type { LoadModule } from './module-loader.js'\nimport { runMiddlewareAndContext } from './middleware-runner.js'\nimport { AuthRequiredError } from './auth.js'\n\nconst METHODS_WITH_BODY = ['POST', 'PUT', 'PATCH']\n\nexport function sendJson(\n res: ServerResponse,\n data: unknown,\n status = 200,\n): void {\n const body = JSON.stringify(data)\n res.writeHead(status, {\n 'Content-Type': 'application/json',\n 'Content-Length': Buffer.byteLength(body),\n })\n res.end(body)\n}\n\nexport function sendError(\n res: ServerResponse,\n code: string,\n message: string,\n status: number,\n issues?: unknown[],\n requestId?: string,\n): void {\n const errorMessage =\n code === 'INTERNAL_ERROR' && process.env.NODE_ENV === 'production'\n ? 'Internal server error'\n : message\n\n if (code === 'INTERNAL_ERROR') {\n console.error(`[${requestId ?? 'no-id'}] ${message}`)\n }\n\n sendJson(\n res,\n {\n error: {\n code,\n message: errorMessage,\n ...(requestId ? { requestId } : {}),\n ...(issues ? { issues } : {}),\n },\n },\n status,\n )\n}\n\nexport function parseBody(req: IncomingMessage): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const method = req.method?.toUpperCase() ?? 'GET'\n if (!METHODS_WITH_BODY.includes(method)) {\n return resolve(undefined)\n }\n\n const contentType = req.headers['content-type'] ?? ''\n const chunks: Buffer[] = []\n\n req.on('data', (chunk: Buffer) => chunks.push(chunk))\n req.on('end', () => {\n const raw = Buffer.concat(chunks).toString()\n if (!raw) return resolve(undefined)\n\n if (!contentType.includes('application/json')) {\n return reject(new Error('Expected Content-Type: application/json'))\n }\n\n try {\n resolve(JSON.parse(raw))\n } catch {\n reject(new Error('Invalid JSON body'))\n }\n })\n req.on('error', reject)\n })\n}\n\nexport async function executeRoute(\n route: ServerRouteNode,\n method: string,\n params: Record<string, string>,\n req: IncomingMessage,\n res: ServerResponse,\n loadModule: LoadModule,\n serverDir?: string,\n requestId?: string,\n): Promise<void> {\n try {\n // Run middleware + context pipeline\n let ctx: unknown = {}\n if (serverDir) {\n const result = await runMiddlewareAndContext(req, res, loadModule, serverDir)\n if (result.aborted) return\n ctx = result.ctx\n }\n\n const mod = await loadModule(route.filePath)\n const routeConfig = mod[method]\n\n if (!routeConfig) {\n sendError(res, 'METHOD_NOT_ALLOWED', `Method ${method} not allowed`, 405, undefined, requestId)\n return\n }\n\n const handler = typeof routeConfig === 'function' ? routeConfig : (routeConfig as Record<string, unknown>).handler\n if (typeof handler !== 'function') {\n sendError(res, 'INTERNAL_ERROR', 'Route handler is not a function', 500, undefined, requestId)\n return\n }\n\n // Parse query\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`)\n const query: Record<string, string> = Object.fromEntries(url.searchParams)\n\n // Parse body\n let body: unknown\n try {\n body = await parseBody(req)\n } catch (err) {\n sendError(res, 'VALIDATION_ERROR', (err as Error).message, 400, undefined, requestId)\n return\n }\n\n // Zod validation\n const rc = routeConfig as Record<string, unknown>\n if (rc.query && typeof (rc.query as { safeParse: Function }).safeParse === 'function') {\n const result = (rc.query as { safeParse: Function }).safeParse(query)\n if (!result.success) {\n sendError(res, 'VALIDATION_ERROR', 'Invalid query parameters', 400, result.error.issues, requestId)\n return\n }\n Object.assign(query, result.data)\n }\n\n if (rc.body && typeof (rc.body as { safeParse: Function }).safeParse === 'function') {\n const result = (rc.body as { safeParse: Function }).safeParse(body)\n if (!result.success) {\n sendError(res, 'VALIDATION_ERROR', 'Invalid request body', 400, result.error.issues, requestId)\n return\n }\n body = result.data\n }\n\n if (rc.params && typeof (rc.params as { safeParse: Function }).safeParse === 'function') {\n const result = (rc.params as { safeParse: Function }).safeParse(params)\n if (!result.success) {\n sendError(res, 'VALIDATION_ERROR', 'Invalid route parameters', 400, result.error.issues, requestId)\n return\n }\n Object.assign(params, result.data)\n }\n\n // Execute handler\n const handlerResult = await handler({ query, body, params, request: req, ctx })\n\n // Handle result\n if (handlerResult === undefined || handlerResult === null) {\n sendJson(res, null, (rc.status as number) ?? 204)\n return\n }\n\n if (handlerResult instanceof Response) {\n res.writeHead(handlerResult.status, Object.fromEntries(handlerResult.headers))\n\n if (handlerResult.body) {\n const reader = handlerResult.body.getReader()\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n res.write(value)\n }\n } catch {\n // Stream error after headers sent — just close the response\n }\n }\n\n res.end()\n return\n }\n\n sendJson(res, handlerResult, (rc.status as number) ?? 200)\n } catch (err) {\n if (err instanceof AuthRequiredError) {\n sendError(res, err.code, err.message, err.status, undefined, requestId)\n return\n }\n sendError(res, 'INTERNAL_ERROR', (err as Error).message ?? 'Internal server error', 500, undefined, requestId)\n }\n}\n","import type { ViteDevServer } from 'vite'\nimport { pathToFileURL } from 'node:url'\n\nexport type LoadModule = (path: string) => Promise<Record<string, unknown>>\n\nexport function createViteLoader(vite: ViteDevServer): LoadModule {\n return (path) => vite.ssrLoadModule(path) as Promise<Record<string, unknown>>\n}\n\nexport function createProductionLoader(): LoadModule {\n return async (path) => {\n const url = pathToFileURL(path).href\n return import(url) as Promise<Record<string, unknown>>\n }\n}\n","export interface RequestLog {\n level: string\n method: string\n url: string\n status: number\n duration: number\n requestId: string\n timestamp: string\n}\n\nexport type LoggerFn = (log: RequestLog) => void\n\nconst defaultLogger: LoggerFn = (log) => {\n console.log(JSON.stringify(log))\n}\n\nexport function logRequest(\n info: Omit<RequestLog, 'level' | 'timestamp'>,\n customLogger?: LoggerFn,\n): void {\n const log: RequestLog = {\n level: 'info',\n ...info,\n timestamp: new Date().toISOString(),\n }\n const logger = customLogger ?? defaultLogger\n logger(log)\n}\n","import type { ViteDevServer, Connect } from 'vite'\nimport { randomUUID } from 'node:crypto'\nimport { scanServerActions } from '../server/action-scan.js'\nimport { executeAction } from '../server/action-execute.js'\nimport { sendError } from '../server/execute.js'\nimport { createViteLoader } from '../server/module-loader.js'\nimport { logRequest } from '../server/logger.js'\n\nconst PREFIX = '/api/__actions/'\n\nexport function createActionMiddleware(\n vite: ViteDevServer,\n serverDir: string,\n): Connect.NextHandleFunction {\n const loadModule = createViteLoader(vite)\n return async (req, res, next) => {\n const url = req.url ?? ''\n if (!url.startsWith(PREFIX)) {\n return next()\n }\n\n const requestId = randomUUID()\n const start = Date.now()\n res.setHeader('x-request-id', requestId)\n\n const pathAfterPrefix = url.slice(PREFIX.length).split('?')[0]\n const segments = pathAfterPrefix.split('/').filter(Boolean)\n\n if (segments.length < 2) {\n sendError(res, 'BAD_REQUEST', 'Action URL must be /api/__actions/{file}/{exportName}', 400, undefined, requestId)\n logRequest({ method: req.method ?? 'POST', url, status: 400, duration: Date.now() - start, requestId })\n return\n }\n\n const exportName = segments[segments.length - 1]\n const actionPath = segments.slice(0, -1).join('/')\n\n const actions = scanServerActions(serverDir)\n const action = actions.find((a) => a.actionPath === actionPath)\n\n if (!action) {\n sendError(res, 'NOT_FOUND', `Action file \"${actionPath}\" not found`, 404, undefined, requestId)\n logRequest({ method: req.method ?? 'POST', url, status: 404, duration: Date.now() - start, requestId })\n return\n }\n\n await executeAction(action.filePath, exportName, req, res, loadModule, serverDir, requestId)\n logRequest({ method: req.method ?? 'POST', url, status: res.statusCode, duration: Date.now() - start, requestId })\n }\n}\n","import { readdirSync, existsSync, statSync } from 'node:fs'\nimport { join, resolve, relative, extname } from 'node:path'\n\nexport interface ActionNode {\n filePath: string\n actionPath: string\n}\n\nconst ACTION_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx']\n\nfunction scanDir(dir: string, actionsDir: string, results: ActionNode[]): void {\n const entries = readdirSync(dir, { withFileTypes: true })\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name)\n if (entry.isDirectory()) {\n if (!entry.name.startsWith('_') && !entry.name.startsWith('.')) {\n scanDir(fullPath, actionsDir, results)\n }\n } else if (entry.isFile()) {\n const ext = extname(entry.name)\n if (!ACTION_EXTENSIONS.includes(ext)) continue\n\n let rel = relative(actionsDir, fullPath)\n rel = rel.replace(/\\\\/g, '/')\n rel = rel.slice(0, -ext.length)\n\n results.push({\n filePath: resolve(fullPath),\n actionPath: rel,\n })\n }\n }\n}\n\nexport function scanServerActions(serverDir: string): ActionNode[] {\n const actionsDir = join(serverDir, 'actions')\n if (!existsSync(actionsDir) || !statSync(actionsDir).isDirectory()) {\n return []\n }\n\n const results: ActionNode[] = []\n scanDir(actionsDir, actionsDir, results)\n return results\n}\n","import type { IncomingMessage } from 'node:http'\n\nexport function validateCsrf(\n req: IncomingMessage,\n): { valid: true } | { valid: false; reason: string } {\n // 1. Custom header must be present (primary defense)\n if (req.headers['x-theo-action'] !== '1') {\n return { valid: false, reason: 'Missing X-Theo-Action header' }\n }\n\n // 2. Origin matching (secondary defense)\n const origin = req.headers['origin']\n if (!origin) {\n // Browsers omit Origin for same-origin requests — treat as valid\n return { valid: true }\n }\n\n const host = req.headers['host']\n if (!host) {\n return { valid: true }\n }\n\n try {\n const originHost = new URL(origin).host\n if (originHost !== host) {\n return { valid: false, reason: `Origin ${origin} does not match host ${host}` }\n }\n } catch {\n return { valid: false, reason: `Invalid origin: ${origin}` }\n }\n\n return { valid: true }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { LoadModule } from './module-loader.js'\nimport { validateCsrf } from './csrf.js'\nimport { parseBody, sendJson, sendError } from './execute.js'\nimport { runMiddlewareAndContext } from './middleware-runner.js'\nimport { AuthRequiredError } from './auth.js'\n\nexport async function executeAction(\n filePath: string,\n exportName: string,\n req: IncomingMessage,\n res: ServerResponse,\n loadModule: LoadModule,\n serverDir?: string,\n requestId?: string,\n): Promise<void> {\n try {\n // 1. Only POST\n const method = (req.method ?? 'GET').toUpperCase()\n if (method !== 'POST') {\n sendError(res, 'METHOD_NOT_ALLOWED', 'Actions only accept POST', 405, undefined, requestId)\n return\n }\n\n // 2. CSRF validation\n const csrf = validateCsrf(req)\n if (!csrf.valid) {\n sendError(res, 'FORBIDDEN', csrf.reason, 403, undefined, requestId)\n return\n }\n\n // 3. Run middleware + context pipeline\n let ctx: unknown = {}\n if (serverDir) {\n const result = await runMiddlewareAndContext(req, res, loadModule, serverDir)\n if (result.aborted) return\n ctx = result.ctx\n }\n\n // 4. Load module\n const mod = await loadModule(filePath)\n\n // 5. Find export\n const actionConfig = mod[exportName] as Record<string, unknown> | undefined\n if (!actionConfig || typeof actionConfig.handler !== 'function' || !actionConfig.input) {\n sendError(res, 'NOT_FOUND', `Action \"${exportName}\" not found`, 404, undefined, requestId)\n return\n }\n\n // 6. Parse body\n let body: unknown\n try {\n body = await parseBody(req)\n } catch (err) {\n sendError(res, 'VALIDATION_ERROR', (err as Error).message, 400, undefined, requestId)\n return\n }\n\n // 7. Validate input with Zod\n const input = actionConfig.input as { safeParse: (v: unknown) => { success: boolean; data?: unknown; error?: { issues: unknown[] } } }\n const result = input.safeParse(body)\n if (!result.success) {\n sendError(res, 'VALIDATION_ERROR', 'Invalid action input', 400, result.error?.issues, requestId)\n return\n }\n\n // 8. Execute handler\n const handlerResult = await (actionConfig.handler as Function)({ input: result.data, ctx })\n\n // 9. Send response\n if (handlerResult === undefined || handlerResult === null) {\n sendJson(res, null, 204)\n return\n }\n\n sendJson(res, handlerResult, 200)\n } catch (err) {\n if (err instanceof AuthRequiredError) {\n sendError(res, err.code, err.message, err.status, undefined, requestId)\n return\n }\n sendError(res, 'INTERNAL_ERROR', (err as Error).message ?? 'Internal server error', 500, undefined, requestId)\n }\n}\n","import { readdirSync, existsSync, statSync } from 'node:fs'\nimport { join, resolve, relative, extname } from 'node:path'\n\nconst WS_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx']\n\nexport interface WebSocketRouteNode {\n filePath: string\n wsPath: string\n}\n\nfunction scanDir(dir: string, wsDir: string, results: WebSocketRouteNode[]): void {\n const entries = readdirSync(dir, { withFileTypes: true })\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name)\n if (entry.isDirectory()) {\n if (!entry.name.startsWith('_') && !entry.name.startsWith('.')) {\n scanDir(fullPath, wsDir, results)\n }\n } else if (entry.isFile()) {\n const ext = extname(entry.name)\n if (!WS_EXTENSIONS.includes(ext)) continue\n\n let rel = relative(wsDir, fullPath)\n rel = rel.replace(/\\\\/g, '/')\n rel = rel.slice(0, -ext.length)\n if (rel.endsWith('/index')) rel = rel.slice(0, -6)\n else if (rel === 'index') rel = ''\n\n results.push({\n filePath: resolve(fullPath),\n wsPath: `/ws/${rel}`,\n })\n }\n }\n}\n\nexport function scanWebSocketRoutes(serverDir: string): WebSocketRouteNode[] {\n const wsDir = join(serverDir, 'ws')\n if (!existsSync(wsDir) || !statSync(wsDir).isDirectory()) {\n return []\n }\n\n const results: WebSocketRouteNode[] = []\n scanDir(wsDir, wsDir, results)\n return results\n}\n"],"mappings":"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","names":["resolve","basename","existsSync","readdirSync","existsSync","statSync","join","resolve","scanDir","readdirSync","join","resolve","existsSync","statSync","existsSync","join","resolve","randomUUID","readdirSync","existsSync","statSync","join","resolve","relative","extname","scanDir","result","randomUUID","readdirSync","existsSync","statSync","join","resolve","relative","extname","scanDir","resolve","existsSync","basename"]}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/core/validate-structure.ts
|
|
4
|
+
import { existsSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
|
|
7
|
+
// src/core/errors.ts
|
|
8
|
+
var TheoProjectError = class extends Error {
|
|
9
|
+
errors;
|
|
10
|
+
rootDir;
|
|
11
|
+
constructor(errors, rootDir) {
|
|
12
|
+
const errorLines = errors.map((e) => ` - ${e}`).join("\n");
|
|
13
|
+
super(
|
|
14
|
+
`Invalid Theo project structure
|
|
15
|
+
|
|
16
|
+
Root: ${rootDir}
|
|
17
|
+
|
|
18
|
+
` + (errorLines ? ` Errors:
|
|
19
|
+
${errorLines}
|
|
20
|
+
` : "")
|
|
21
|
+
);
|
|
22
|
+
this.name = "TheoProjectError";
|
|
23
|
+
this.errors = errors;
|
|
24
|
+
this.rootDir = rootDir;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// src/core/validate-structure.ts
|
|
29
|
+
var REQUIRED_DIRS = [
|
|
30
|
+
{
|
|
31
|
+
path: "app",
|
|
32
|
+
errorMessage: "Missing required directory: app/"
|
|
33
|
+
}
|
|
34
|
+
];
|
|
35
|
+
var REQUIRED_FILES = [
|
|
36
|
+
{
|
|
37
|
+
path: "theo.config.ts",
|
|
38
|
+
errorMessage: "Missing required file: theo.config.ts"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
path: "package.json",
|
|
42
|
+
errorMessage: "Missing required file: package.json"
|
|
43
|
+
}
|
|
44
|
+
];
|
|
45
|
+
function validateProjectStructure(rootDir) {
|
|
46
|
+
if (!existsSync(rootDir)) {
|
|
47
|
+
throw new TheoProjectError(
|
|
48
|
+
[`Project directory does not exist: ${rootDir}`],
|
|
49
|
+
rootDir
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
const errors = [];
|
|
53
|
+
for (const rule of REQUIRED_DIRS) {
|
|
54
|
+
if (!existsSync(join(rootDir, rule.path))) {
|
|
55
|
+
errors.push(rule.errorMessage);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
for (const rule of REQUIRED_FILES) {
|
|
59
|
+
if (!existsSync(join(rootDir, rule.path))) {
|
|
60
|
+
errors.push(rule.errorMessage);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (errors.length > 0) {
|
|
64
|
+
throw new TheoProjectError(errors, rootDir);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export {
|
|
69
|
+
validateProjectStructure
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=chunk-KPU44T6G.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/validate-structure.ts","../src/core/errors.ts"],"sourcesContent":["import { existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { TheoProjectError } from './errors.js'\n\ninterface ValidationRule {\n path: string\n errorMessage: string\n}\n\nconst REQUIRED_DIRS: ValidationRule[] = [\n {\n path: 'app',\n errorMessage: 'Missing required directory: app/',\n },\n]\n\nconst REQUIRED_FILES: ValidationRule[] = [\n {\n path: 'theo.config.ts',\n errorMessage: 'Missing required file: theo.config.ts',\n },\n {\n path: 'package.json',\n errorMessage: 'Missing required file: package.json',\n },\n]\n\nexport function validateProjectStructure(rootDir: string): void {\n if (!existsSync(rootDir)) {\n throw new TheoProjectError(\n [`Project directory does not exist: ${rootDir}`],\n rootDir,\n )\n }\n\n const errors: string[] = []\n\n for (const rule of REQUIRED_DIRS) {\n if (!existsSync(join(rootDir, rule.path))) {\n errors.push(rule.errorMessage)\n }\n }\n\n for (const rule of REQUIRED_FILES) {\n if (!existsSync(join(rootDir, rule.path))) {\n errors.push(rule.errorMessage)\n }\n }\n\n if (errors.length > 0) {\n throw new TheoProjectError(errors, rootDir)\n }\n}\n","export class TheoProjectError extends Error {\n public readonly errors: string[]\n public readonly rootDir: string\n\n constructor(errors: string[], rootDir: string) {\n const errorLines = errors.map((e) => ` - ${e}`).join('\\n')\n\n super(\n `Invalid Theo project structure\\n\\n` +\n ` Root: ${rootDir}\\n\\n` +\n (errorLines ? ` Errors:\\n${errorLines}\\n` : ''),\n )\n\n this.name = 'TheoProjectError'\n this.errors = errors\n this.rootDir = rootDir\n }\n}\n"],"mappings":";;;AAAA,SAAS,kBAAkB;AAC3B,SAAS,YAAY;;;ACDd,IAAM,mBAAN,cAA+B,MAAM;AAAA,EAC1B;AAAA,EACA;AAAA,EAEhB,YAAY,QAAkB,SAAiB;AAC7C,UAAM,aAAa,OAAO,IAAI,CAAC,MAAM,OAAO,CAAC,EAAE,EAAE,KAAK,IAAI;AAE1D;AAAA,MACE;AAAA;AAAA,UACa,OAAO;AAAA;AAAA,KACjB,aAAa;AAAA,EAAc,UAAU;AAAA,IAAO;AAAA,IACjD;AAEA,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,UAAU;AAAA,EACjB;AACF;;;ADRA,IAAM,gBAAkC;AAAA,EACtC;AAAA,IACE,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AACF;AAEA,IAAM,iBAAmC;AAAA,EACvC;AAAA,IACE,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AACF;AAEO,SAAS,yBAAyB,SAAuB;AAC9D,MAAI,CAAC,WAAW,OAAO,GAAG;AACxB,UAAM,IAAI;AAAA,MACR,CAAC,qCAAqC,OAAO,EAAE;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAmB,CAAC;AAE1B,aAAW,QAAQ,eAAe;AAChC,QAAI,CAAC,WAAW,KAAK,SAAS,KAAK,IAAI,CAAC,GAAG;AACzC,aAAO,KAAK,KAAK,YAAY;AAAA,IAC/B;AAAA,EACF;AAEA,aAAW,QAAQ,gBAAgB;AACjC,QAAI,CAAC,WAAW,KAAK,SAAS,KAAK,IAAI,CAAC,GAAG;AACzC,aAAO,KAAK,KAAK,YAAY;AAAA,IAC/B;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,GAAG;AACrB,UAAM,IAAI,iBAAiB,QAAQ,OAAO;AAAA,EAC5C;AACF;","names":[]}
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/server/middleware-runner.ts
|
|
4
|
+
import { existsSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
async function runMiddlewareAndContext(req, res, loadModule, serverDir) {
|
|
7
|
+
const middlewarePath = join(serverDir, "middleware.ts");
|
|
8
|
+
if (existsSync(middlewarePath)) {
|
|
9
|
+
const mod = await loadModule(middlewarePath);
|
|
10
|
+
const mw = mod.default;
|
|
11
|
+
if (typeof mw === "function") {
|
|
12
|
+
let nextCalled = false;
|
|
13
|
+
await mw(req, res, async () => {
|
|
14
|
+
nextCalled = true;
|
|
15
|
+
});
|
|
16
|
+
if (!nextCalled || res.writableEnded) {
|
|
17
|
+
return { ctx: {}, aborted: true };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
let ctx = {};
|
|
22
|
+
const contextPath = join(serverDir, "context.ts");
|
|
23
|
+
if (existsSync(contextPath)) {
|
|
24
|
+
const mod = await loadModule(contextPath);
|
|
25
|
+
if (typeof mod.createContext === "function") {
|
|
26
|
+
ctx = await mod.createContext({ request: req, response: res });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return { ctx, aborted: false };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/server/auth.ts
|
|
33
|
+
var AuthRequiredError = class extends Error {
|
|
34
|
+
code = "AUTH_REQUIRED";
|
|
35
|
+
status = 401;
|
|
36
|
+
constructor(message = "Authentication required") {
|
|
37
|
+
super(message);
|
|
38
|
+
this.name = "AuthRequiredError";
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// src/server/execute.ts
|
|
43
|
+
var METHODS_WITH_BODY = ["POST", "PUT", "PATCH"];
|
|
44
|
+
function sendJson(res, data, status = 200) {
|
|
45
|
+
const body = JSON.stringify(data);
|
|
46
|
+
res.writeHead(status, {
|
|
47
|
+
"Content-Type": "application/json",
|
|
48
|
+
"Content-Length": Buffer.byteLength(body)
|
|
49
|
+
});
|
|
50
|
+
res.end(body);
|
|
51
|
+
}
|
|
52
|
+
function sendError(res, code, message, status, issues, requestId) {
|
|
53
|
+
const errorMessage = code === "INTERNAL_ERROR" && process.env.NODE_ENV === "production" ? "Internal server error" : message;
|
|
54
|
+
if (code === "INTERNAL_ERROR") {
|
|
55
|
+
console.error(`[${requestId ?? "no-id"}] ${message}`);
|
|
56
|
+
}
|
|
57
|
+
sendJson(
|
|
58
|
+
res,
|
|
59
|
+
{
|
|
60
|
+
error: {
|
|
61
|
+
code,
|
|
62
|
+
message: errorMessage,
|
|
63
|
+
...requestId ? { requestId } : {},
|
|
64
|
+
...issues ? { issues } : {}
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
status
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
function parseBody(req) {
|
|
71
|
+
return new Promise((resolve, reject) => {
|
|
72
|
+
const method = req.method?.toUpperCase() ?? "GET";
|
|
73
|
+
if (!METHODS_WITH_BODY.includes(method)) {
|
|
74
|
+
return resolve(void 0);
|
|
75
|
+
}
|
|
76
|
+
const contentType = req.headers["content-type"] ?? "";
|
|
77
|
+
const chunks = [];
|
|
78
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
79
|
+
req.on("end", () => {
|
|
80
|
+
const raw = Buffer.concat(chunks).toString();
|
|
81
|
+
if (!raw) return resolve(void 0);
|
|
82
|
+
if (!contentType.includes("application/json")) {
|
|
83
|
+
return reject(new Error("Expected Content-Type: application/json"));
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
resolve(JSON.parse(raw));
|
|
87
|
+
} catch {
|
|
88
|
+
reject(new Error("Invalid JSON body"));
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
req.on("error", reject);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
async function executeRoute(route, method, params, req, res, loadModule, serverDir, requestId) {
|
|
95
|
+
try {
|
|
96
|
+
let ctx = {};
|
|
97
|
+
if (serverDir) {
|
|
98
|
+
const result = await runMiddlewareAndContext(req, res, loadModule, serverDir);
|
|
99
|
+
if (result.aborted) return;
|
|
100
|
+
ctx = result.ctx;
|
|
101
|
+
}
|
|
102
|
+
const mod = await loadModule(route.filePath);
|
|
103
|
+
const routeConfig = mod[method];
|
|
104
|
+
if (!routeConfig) {
|
|
105
|
+
sendError(res, "METHOD_NOT_ALLOWED", `Method ${method} not allowed`, 405, void 0, requestId);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const handler = typeof routeConfig === "function" ? routeConfig : routeConfig.handler;
|
|
109
|
+
if (typeof handler !== "function") {
|
|
110
|
+
sendError(res, "INTERNAL_ERROR", "Route handler is not a function", 500, void 0, requestId);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
114
|
+
const query = Object.fromEntries(url.searchParams);
|
|
115
|
+
let body;
|
|
116
|
+
try {
|
|
117
|
+
body = await parseBody(req);
|
|
118
|
+
} catch (err) {
|
|
119
|
+
sendError(res, "VALIDATION_ERROR", err.message, 400, void 0, requestId);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const rc = routeConfig;
|
|
123
|
+
if (rc.query && typeof rc.query.safeParse === "function") {
|
|
124
|
+
const result = rc.query.safeParse(query);
|
|
125
|
+
if (!result.success) {
|
|
126
|
+
sendError(res, "VALIDATION_ERROR", "Invalid query parameters", 400, result.error.issues, requestId);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
Object.assign(query, result.data);
|
|
130
|
+
}
|
|
131
|
+
if (rc.body && typeof rc.body.safeParse === "function") {
|
|
132
|
+
const result = rc.body.safeParse(body);
|
|
133
|
+
if (!result.success) {
|
|
134
|
+
sendError(res, "VALIDATION_ERROR", "Invalid request body", 400, result.error.issues, requestId);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
body = result.data;
|
|
138
|
+
}
|
|
139
|
+
if (rc.params && typeof rc.params.safeParse === "function") {
|
|
140
|
+
const result = rc.params.safeParse(params);
|
|
141
|
+
if (!result.success) {
|
|
142
|
+
sendError(res, "VALIDATION_ERROR", "Invalid route parameters", 400, result.error.issues, requestId);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
Object.assign(params, result.data);
|
|
146
|
+
}
|
|
147
|
+
const handlerResult = await handler({ query, body, params, request: req, ctx });
|
|
148
|
+
if (handlerResult === void 0 || handlerResult === null) {
|
|
149
|
+
sendJson(res, null, rc.status ?? 204);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (handlerResult instanceof Response) {
|
|
153
|
+
res.writeHead(handlerResult.status, Object.fromEntries(handlerResult.headers));
|
|
154
|
+
if (handlerResult.body) {
|
|
155
|
+
const reader = handlerResult.body.getReader();
|
|
156
|
+
try {
|
|
157
|
+
while (true) {
|
|
158
|
+
const { done, value } = await reader.read();
|
|
159
|
+
if (done) break;
|
|
160
|
+
res.write(value);
|
|
161
|
+
}
|
|
162
|
+
} catch {
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
res.end();
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
sendJson(res, handlerResult, rc.status ?? 200);
|
|
169
|
+
} catch (err) {
|
|
170
|
+
if (err instanceof AuthRequiredError) {
|
|
171
|
+
sendError(res, err.code, err.message, err.status, void 0, requestId);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
sendError(res, "INTERNAL_ERROR", err.message ?? "Internal server error", 500, void 0, requestId);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// src/server/module-loader.ts
|
|
179
|
+
import { pathToFileURL } from "url";
|
|
180
|
+
function createViteLoader(vite) {
|
|
181
|
+
return (path) => vite.ssrLoadModule(path);
|
|
182
|
+
}
|
|
183
|
+
function createProductionLoader() {
|
|
184
|
+
return async (path) => {
|
|
185
|
+
const url = pathToFileURL(path).href;
|
|
186
|
+
return import(url);
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// src/server/logger.ts
|
|
191
|
+
var defaultLogger = (log) => {
|
|
192
|
+
console.log(JSON.stringify(log));
|
|
193
|
+
};
|
|
194
|
+
function logRequest(info, customLogger) {
|
|
195
|
+
const log = {
|
|
196
|
+
level: "info",
|
|
197
|
+
...info,
|
|
198
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
199
|
+
};
|
|
200
|
+
const logger = customLogger ?? defaultLogger;
|
|
201
|
+
logger(log);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// src/server/rate-limit.ts
|
|
205
|
+
function createRateLimiter(config) {
|
|
206
|
+
const store = /* @__PURE__ */ new Map();
|
|
207
|
+
let checkCount = 0;
|
|
208
|
+
return function checkRateLimit(req) {
|
|
209
|
+
const key = req.socket?.remoteAddress ?? "unknown";
|
|
210
|
+
const now = Date.now();
|
|
211
|
+
if (++checkCount % 1e3 === 0) {
|
|
212
|
+
for (const [k, v] of store) {
|
|
213
|
+
if (v.resetAt < now) store.delete(k);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const entry = store.get(key);
|
|
217
|
+
if (!entry || now > entry.resetAt) {
|
|
218
|
+
store.set(key, { count: 1, resetAt: now + config.windowMs });
|
|
219
|
+
return {
|
|
220
|
+
limited: false,
|
|
221
|
+
headers: {
|
|
222
|
+
"X-RateLimit-Limit": String(config.max),
|
|
223
|
+
"X-RateLimit-Remaining": String(config.max - 1)
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
entry.count++;
|
|
228
|
+
if (entry.count > config.max) {
|
|
229
|
+
const retryAfter = Math.ceil((entry.resetAt - now) / 1e3);
|
|
230
|
+
return {
|
|
231
|
+
limited: true,
|
|
232
|
+
headers: {
|
|
233
|
+
"X-RateLimit-Limit": String(config.max),
|
|
234
|
+
"X-RateLimit-Remaining": "0",
|
|
235
|
+
"Retry-After": String(retryAfter)
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
limited: false,
|
|
241
|
+
headers: {
|
|
242
|
+
"X-RateLimit-Limit": String(config.max),
|
|
243
|
+
"X-RateLimit-Remaining": String(config.max - entry.count)
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// src/server/csrf.ts
|
|
250
|
+
function validateCsrf(req) {
|
|
251
|
+
if (req.headers["x-theo-action"] !== "1") {
|
|
252
|
+
return { valid: false, reason: "Missing X-Theo-Action header" };
|
|
253
|
+
}
|
|
254
|
+
const origin = req.headers["origin"];
|
|
255
|
+
if (!origin) {
|
|
256
|
+
return { valid: true };
|
|
257
|
+
}
|
|
258
|
+
const host = req.headers["host"];
|
|
259
|
+
if (!host) {
|
|
260
|
+
return { valid: true };
|
|
261
|
+
}
|
|
262
|
+
try {
|
|
263
|
+
const originHost = new URL(origin).host;
|
|
264
|
+
if (originHost !== host) {
|
|
265
|
+
return { valid: false, reason: `Origin ${origin} does not match host ${host}` };
|
|
266
|
+
}
|
|
267
|
+
} catch {
|
|
268
|
+
return { valid: false, reason: `Invalid origin: ${origin}` };
|
|
269
|
+
}
|
|
270
|
+
return { valid: true };
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// src/server/action-execute.ts
|
|
274
|
+
async function executeAction(filePath, exportName, req, res, loadModule, serverDir, requestId) {
|
|
275
|
+
try {
|
|
276
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
277
|
+
if (method !== "POST") {
|
|
278
|
+
sendError(res, "METHOD_NOT_ALLOWED", "Actions only accept POST", 405, void 0, requestId);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const csrf = validateCsrf(req);
|
|
282
|
+
if (!csrf.valid) {
|
|
283
|
+
sendError(res, "FORBIDDEN", csrf.reason, 403, void 0, requestId);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
let ctx = {};
|
|
287
|
+
if (serverDir) {
|
|
288
|
+
const result2 = await runMiddlewareAndContext(req, res, loadModule, serverDir);
|
|
289
|
+
if (result2.aborted) return;
|
|
290
|
+
ctx = result2.ctx;
|
|
291
|
+
}
|
|
292
|
+
const mod = await loadModule(filePath);
|
|
293
|
+
const actionConfig = mod[exportName];
|
|
294
|
+
if (!actionConfig || typeof actionConfig.handler !== "function" || !actionConfig.input) {
|
|
295
|
+
sendError(res, "NOT_FOUND", `Action "${exportName}" not found`, 404, void 0, requestId);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
let body;
|
|
299
|
+
try {
|
|
300
|
+
body = await parseBody(req);
|
|
301
|
+
} catch (err) {
|
|
302
|
+
sendError(res, "VALIDATION_ERROR", err.message, 400, void 0, requestId);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
const input = actionConfig.input;
|
|
306
|
+
const result = input.safeParse(body);
|
|
307
|
+
if (!result.success) {
|
|
308
|
+
sendError(res, "VALIDATION_ERROR", "Invalid action input", 400, result.error?.issues, requestId);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const handlerResult = await actionConfig.handler({ input: result.data, ctx });
|
|
312
|
+
if (handlerResult === void 0 || handlerResult === null) {
|
|
313
|
+
sendJson(res, null, 204);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
sendJson(res, handlerResult, 200);
|
|
317
|
+
} catch (err) {
|
|
318
|
+
if (err instanceof AuthRequiredError) {
|
|
319
|
+
sendError(res, err.code, err.message, err.status, void 0, requestId);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
sendError(res, "INTERNAL_ERROR", err.message ?? "Internal server error", 500, void 0, requestId);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export {
|
|
327
|
+
sendError,
|
|
328
|
+
executeRoute,
|
|
329
|
+
createViteLoader,
|
|
330
|
+
createProductionLoader,
|
|
331
|
+
logRequest,
|
|
332
|
+
createRateLimiter,
|
|
333
|
+
executeAction
|
|
334
|
+
};
|
|
335
|
+
//# sourceMappingURL=chunk-MMZZBPMX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/middleware-runner.ts","../src/server/auth.ts","../src/server/execute.ts","../src/server/module-loader.ts","../src/server/logger.ts","../src/server/rate-limit.ts","../src/server/csrf.ts","../src/server/action-execute.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { LoadModule } from './module-loader.js'\nimport { existsSync } from 'node:fs'\nimport { join } from 'node:path'\n\nexport interface MiddlewareResult {\n ctx: unknown\n aborted: boolean\n}\n\nexport async function runMiddlewareAndContext(\n req: IncomingMessage,\n res: ServerResponse,\n loadModule: LoadModule,\n serverDir: string,\n): Promise<MiddlewareResult> {\n // 1. Run middleware (if exists)\n const middlewarePath = join(serverDir, 'middleware.ts')\n if (existsSync(middlewarePath)) {\n const mod = await loadModule(middlewarePath)\n const mw = mod.default\n if (typeof mw === 'function') {\n let nextCalled = false\n await mw(req, res, async () => {\n nextCalled = true\n })\n if (!nextCalled || res.writableEnded) {\n return { ctx: {}, aborted: true }\n }\n }\n }\n\n // 2. Create context (if exists)\n let ctx: unknown = {}\n const contextPath = join(serverDir, 'context.ts')\n if (existsSync(contextPath)) {\n const mod = await loadModule(contextPath)\n if (typeof mod.createContext === 'function') {\n ctx = await (mod.createContext as Function)({ request: req, response: res })\n }\n }\n\n return { ctx, aborted: false }\n}\n","export class AuthRequiredError extends Error {\n code = 'AUTH_REQUIRED' as const\n status = 401\n\n constructor(message = 'Authentication required') {\n super(message)\n this.name = 'AuthRequiredError'\n }\n}\n\nexport function requireAuth<T>(session: T | null | undefined): asserts session is T {\n if (session === null || session === undefined) {\n throw new AuthRequiredError()\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { ServerRouteNode } from './match.js'\nimport type { LoadModule } from './module-loader.js'\nimport { runMiddlewareAndContext } from './middleware-runner.js'\nimport { AuthRequiredError } from './auth.js'\n\nconst METHODS_WITH_BODY = ['POST', 'PUT', 'PATCH']\n\nexport function sendJson(\n res: ServerResponse,\n data: unknown,\n status = 200,\n): void {\n const body = JSON.stringify(data)\n res.writeHead(status, {\n 'Content-Type': 'application/json',\n 'Content-Length': Buffer.byteLength(body),\n })\n res.end(body)\n}\n\nexport function sendError(\n res: ServerResponse,\n code: string,\n message: string,\n status: number,\n issues?: unknown[],\n requestId?: string,\n): void {\n const errorMessage =\n code === 'INTERNAL_ERROR' && process.env.NODE_ENV === 'production'\n ? 'Internal server error'\n : message\n\n if (code === 'INTERNAL_ERROR') {\n console.error(`[${requestId ?? 'no-id'}] ${message}`)\n }\n\n sendJson(\n res,\n {\n error: {\n code,\n message: errorMessage,\n ...(requestId ? { requestId } : {}),\n ...(issues ? { issues } : {}),\n },\n },\n status,\n )\n}\n\nexport function parseBody(req: IncomingMessage): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const method = req.method?.toUpperCase() ?? 'GET'\n if (!METHODS_WITH_BODY.includes(method)) {\n return resolve(undefined)\n }\n\n const contentType = req.headers['content-type'] ?? ''\n const chunks: Buffer[] = []\n\n req.on('data', (chunk: Buffer) => chunks.push(chunk))\n req.on('end', () => {\n const raw = Buffer.concat(chunks).toString()\n if (!raw) return resolve(undefined)\n\n if (!contentType.includes('application/json')) {\n return reject(new Error('Expected Content-Type: application/json'))\n }\n\n try {\n resolve(JSON.parse(raw))\n } catch {\n reject(new Error('Invalid JSON body'))\n }\n })\n req.on('error', reject)\n })\n}\n\nexport async function executeRoute(\n route: ServerRouteNode,\n method: string,\n params: Record<string, string>,\n req: IncomingMessage,\n res: ServerResponse,\n loadModule: LoadModule,\n serverDir?: string,\n requestId?: string,\n): Promise<void> {\n try {\n // Run middleware + context pipeline\n let ctx: unknown = {}\n if (serverDir) {\n const result = await runMiddlewareAndContext(req, res, loadModule, serverDir)\n if (result.aborted) return\n ctx = result.ctx\n }\n\n const mod = await loadModule(route.filePath)\n const routeConfig = mod[method]\n\n if (!routeConfig) {\n sendError(res, 'METHOD_NOT_ALLOWED', `Method ${method} not allowed`, 405, undefined, requestId)\n return\n }\n\n const handler = typeof routeConfig === 'function' ? routeConfig : (routeConfig as Record<string, unknown>).handler\n if (typeof handler !== 'function') {\n sendError(res, 'INTERNAL_ERROR', 'Route handler is not a function', 500, undefined, requestId)\n return\n }\n\n // Parse query\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`)\n const query: Record<string, string> = Object.fromEntries(url.searchParams)\n\n // Parse body\n let body: unknown\n try {\n body = await parseBody(req)\n } catch (err) {\n sendError(res, 'VALIDATION_ERROR', (err as Error).message, 400, undefined, requestId)\n return\n }\n\n // Zod validation\n const rc = routeConfig as Record<string, unknown>\n if (rc.query && typeof (rc.query as { safeParse: Function }).safeParse === 'function') {\n const result = (rc.query as { safeParse: Function }).safeParse(query)\n if (!result.success) {\n sendError(res, 'VALIDATION_ERROR', 'Invalid query parameters', 400, result.error.issues, requestId)\n return\n }\n Object.assign(query, result.data)\n }\n\n if (rc.body && typeof (rc.body as { safeParse: Function }).safeParse === 'function') {\n const result = (rc.body as { safeParse: Function }).safeParse(body)\n if (!result.success) {\n sendError(res, 'VALIDATION_ERROR', 'Invalid request body', 400, result.error.issues, requestId)\n return\n }\n body = result.data\n }\n\n if (rc.params && typeof (rc.params as { safeParse: Function }).safeParse === 'function') {\n const result = (rc.params as { safeParse: Function }).safeParse(params)\n if (!result.success) {\n sendError(res, 'VALIDATION_ERROR', 'Invalid route parameters', 400, result.error.issues, requestId)\n return\n }\n Object.assign(params, result.data)\n }\n\n // Execute handler\n const handlerResult = await handler({ query, body, params, request: req, ctx })\n\n // Handle result\n if (handlerResult === undefined || handlerResult === null) {\n sendJson(res, null, (rc.status as number) ?? 204)\n return\n }\n\n if (handlerResult instanceof Response) {\n res.writeHead(handlerResult.status, Object.fromEntries(handlerResult.headers))\n\n if (handlerResult.body) {\n const reader = handlerResult.body.getReader()\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n res.write(value)\n }\n } catch {\n // Stream error after headers sent — just close the response\n }\n }\n\n res.end()\n return\n }\n\n sendJson(res, handlerResult, (rc.status as number) ?? 200)\n } catch (err) {\n if (err instanceof AuthRequiredError) {\n sendError(res, err.code, err.message, err.status, undefined, requestId)\n return\n }\n sendError(res, 'INTERNAL_ERROR', (err as Error).message ?? 'Internal server error', 500, undefined, requestId)\n }\n}\n","import type { ViteDevServer } from 'vite'\nimport { pathToFileURL } from 'node:url'\n\nexport type LoadModule = (path: string) => Promise<Record<string, unknown>>\n\nexport function createViteLoader(vite: ViteDevServer): LoadModule {\n return (path) => vite.ssrLoadModule(path) as Promise<Record<string, unknown>>\n}\n\nexport function createProductionLoader(): LoadModule {\n return async (path) => {\n const url = pathToFileURL(path).href\n return import(url) as Promise<Record<string, unknown>>\n }\n}\n","export interface RequestLog {\n level: string\n method: string\n url: string\n status: number\n duration: number\n requestId: string\n timestamp: string\n}\n\nexport type LoggerFn = (log: RequestLog) => void\n\nconst defaultLogger: LoggerFn = (log) => {\n console.log(JSON.stringify(log))\n}\n\nexport function logRequest(\n info: Omit<RequestLog, 'level' | 'timestamp'>,\n customLogger?: LoggerFn,\n): void {\n const log: RequestLog = {\n level: 'info',\n ...info,\n timestamp: new Date().toISOString(),\n }\n const logger = customLogger ?? defaultLogger\n logger(log)\n}\n","import type { IncomingMessage } from 'node:http'\n\nexport interface RateLimitConfig {\n windowMs: number\n max: number\n}\n\nexport interface RateLimitResult {\n limited: boolean\n headers: Record<string, string>\n}\n\ninterface StoreEntry {\n count: number\n resetAt: number\n}\n\nexport function createRateLimiter(config: RateLimitConfig) {\n const store = new Map<string, StoreEntry>()\n let checkCount = 0\n\n return function checkRateLimit(req: IncomingMessage): RateLimitResult {\n const key = req.socket?.remoteAddress ?? 'unknown'\n const now = Date.now()\n\n // Periodic cleanup (EC-1): every 1000 checks, remove expired entries\n if (++checkCount % 1000 === 0) {\n for (const [k, v] of store) {\n if (v.resetAt < now) store.delete(k)\n }\n }\n\n const entry = store.get(key)\n\n if (!entry || now > entry.resetAt) {\n store.set(key, { count: 1, resetAt: now + config.windowMs })\n return {\n limited: false,\n headers: {\n 'X-RateLimit-Limit': String(config.max),\n 'X-RateLimit-Remaining': String(config.max - 1),\n },\n }\n }\n\n entry.count++\n\n if (entry.count > config.max) {\n const retryAfter = Math.ceil((entry.resetAt - now) / 1000)\n return {\n limited: true,\n headers: {\n 'X-RateLimit-Limit': String(config.max),\n 'X-RateLimit-Remaining': '0',\n 'Retry-After': String(retryAfter),\n },\n }\n }\n\n return {\n limited: false,\n headers: {\n 'X-RateLimit-Limit': String(config.max),\n 'X-RateLimit-Remaining': String(config.max - entry.count),\n },\n }\n }\n}\n","import type { IncomingMessage } from 'node:http'\n\nexport function validateCsrf(\n req: IncomingMessage,\n): { valid: true } | { valid: false; reason: string } {\n // 1. Custom header must be present (primary defense)\n if (req.headers['x-theo-action'] !== '1') {\n return { valid: false, reason: 'Missing X-Theo-Action header' }\n }\n\n // 2. Origin matching (secondary defense)\n const origin = req.headers['origin']\n if (!origin) {\n // Browsers omit Origin for same-origin requests — treat as valid\n return { valid: true }\n }\n\n const host = req.headers['host']\n if (!host) {\n return { valid: true }\n }\n\n try {\n const originHost = new URL(origin).host\n if (originHost !== host) {\n return { valid: false, reason: `Origin ${origin} does not match host ${host}` }\n }\n } catch {\n return { valid: false, reason: `Invalid origin: ${origin}` }\n }\n\n return { valid: true }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { LoadModule } from './module-loader.js'\nimport { validateCsrf } from './csrf.js'\nimport { parseBody, sendJson, sendError } from './execute.js'\nimport { runMiddlewareAndContext } from './middleware-runner.js'\nimport { AuthRequiredError } from './auth.js'\n\nexport async function executeAction(\n filePath: string,\n exportName: string,\n req: IncomingMessage,\n res: ServerResponse,\n loadModule: LoadModule,\n serverDir?: string,\n requestId?: string,\n): Promise<void> {\n try {\n // 1. Only POST\n const method = (req.method ?? 'GET').toUpperCase()\n if (method !== 'POST') {\n sendError(res, 'METHOD_NOT_ALLOWED', 'Actions only accept POST', 405, undefined, requestId)\n return\n }\n\n // 2. CSRF validation\n const csrf = validateCsrf(req)\n if (!csrf.valid) {\n sendError(res, 'FORBIDDEN', csrf.reason, 403, undefined, requestId)\n return\n }\n\n // 3. Run middleware + context pipeline\n let ctx: unknown = {}\n if (serverDir) {\n const result = await runMiddlewareAndContext(req, res, loadModule, serverDir)\n if (result.aborted) return\n ctx = result.ctx\n }\n\n // 4. Load module\n const mod = await loadModule(filePath)\n\n // 5. Find export\n const actionConfig = mod[exportName] as Record<string, unknown> | undefined\n if (!actionConfig || typeof actionConfig.handler !== 'function' || !actionConfig.input) {\n sendError(res, 'NOT_FOUND', `Action \"${exportName}\" not found`, 404, undefined, requestId)\n return\n }\n\n // 6. Parse body\n let body: unknown\n try {\n body = await parseBody(req)\n } catch (err) {\n sendError(res, 'VALIDATION_ERROR', (err as Error).message, 400, undefined, requestId)\n return\n }\n\n // 7. Validate input with Zod\n const input = actionConfig.input as { safeParse: (v: unknown) => { success: boolean; data?: unknown; error?: { issues: unknown[] } } }\n const result = input.safeParse(body)\n if (!result.success) {\n sendError(res, 'VALIDATION_ERROR', 'Invalid action input', 400, result.error?.issues, requestId)\n return\n }\n\n // 8. Execute handler\n const handlerResult = await (actionConfig.handler as Function)({ input: result.data, ctx })\n\n // 9. Send response\n if (handlerResult === undefined || handlerResult === null) {\n sendJson(res, null, 204)\n return\n }\n\n sendJson(res, handlerResult, 200)\n } catch (err) {\n if (err instanceof AuthRequiredError) {\n sendError(res, err.code, err.message, err.status, undefined, requestId)\n return\n }\n sendError(res, 'INTERNAL_ERROR', (err as Error).message ?? 'Internal server error', 500, undefined, requestId)\n }\n}\n"],"mappings":";;;AAEA,SAAS,kBAAkB;AAC3B,SAAS,YAAY;AAOrB,eAAsB,wBACpB,KACA,KACA,YACA,WAC2B;AAE3B,QAAM,iBAAiB,KAAK,WAAW,eAAe;AACtD,MAAI,WAAW,cAAc,GAAG;AAC9B,UAAM,MAAM,MAAM,WAAW,cAAc;AAC3C,UAAM,KAAK,IAAI;AACf,QAAI,OAAO,OAAO,YAAY;AAC5B,UAAI,aAAa;AACjB,YAAM,GAAG,KAAK,KAAK,YAAY;AAC7B,qBAAa;AAAA,MACf,CAAC;AACD,UAAI,CAAC,cAAc,IAAI,eAAe;AACpC,eAAO,EAAE,KAAK,CAAC,GAAG,SAAS,KAAK;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AAGA,MAAI,MAAe,CAAC;AACpB,QAAM,cAAc,KAAK,WAAW,YAAY;AAChD,MAAI,WAAW,WAAW,GAAG;AAC3B,UAAM,MAAM,MAAM,WAAW,WAAW;AACxC,QAAI,OAAO,IAAI,kBAAkB,YAAY;AAC3C,YAAM,MAAO,IAAI,cAA2B,EAAE,SAAS,KAAK,UAAU,IAAI,CAAC;AAAA,IAC7E;AAAA,EACF;AAEA,SAAO,EAAE,KAAK,SAAS,MAAM;AAC/B;;;AC3CO,IAAM,oBAAN,cAAgC,MAAM;AAAA,EAC3C,OAAO;AAAA,EACP,SAAS;AAAA,EAET,YAAY,UAAU,2BAA2B;AAC/C,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;;;ACFA,IAAM,oBAAoB,CAAC,QAAQ,OAAO,OAAO;AAE1C,SAAS,SACd,KACA,MACA,SAAS,KACH;AACN,QAAM,OAAO,KAAK,UAAU,IAAI;AAChC,MAAI,UAAU,QAAQ;AAAA,IACpB,gBAAgB;AAAA,IAChB,kBAAkB,OAAO,WAAW,IAAI;AAAA,EAC1C,CAAC;AACD,MAAI,IAAI,IAAI;AACd;AAEO,SAAS,UACd,KACA,MACA,SACA,QACA,QACA,WACM;AACN,QAAM,eACJ,SAAS,oBAAoB,QAAQ,IAAI,aAAa,eAClD,0BACA;AAEN,MAAI,SAAS,kBAAkB;AAC7B,YAAQ,MAAM,IAAI,aAAa,OAAO,KAAK,OAAO,EAAE;AAAA,EACtD;AAEA;AAAA,IACE;AAAA,IACA;AAAA,MACE,OAAO;AAAA,QACL;AAAA,QACA,SAAS;AAAA,QACT,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,QACjC,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,MAC7B;AAAA,IACF;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,UAAU,KAAwC;AAChE,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,SAAS,IAAI,QAAQ,YAAY,KAAK;AAC5C,QAAI,CAAC,kBAAkB,SAAS,MAAM,GAAG;AACvC,aAAO,QAAQ,MAAS;AAAA,IAC1B;AAEA,UAAM,cAAc,IAAI,QAAQ,cAAc,KAAK;AACnD,UAAM,SAAmB,CAAC;AAE1B,QAAI,GAAG,QAAQ,CAAC,UAAkB,OAAO,KAAK,KAAK,CAAC;AACpD,QAAI,GAAG,OAAO,MAAM;AAClB,YAAM,MAAM,OAAO,OAAO,MAAM,EAAE,SAAS;AAC3C,UAAI,CAAC,IAAK,QAAO,QAAQ,MAAS;AAElC,UAAI,CAAC,YAAY,SAAS,kBAAkB,GAAG;AAC7C,eAAO,OAAO,IAAI,MAAM,yCAAyC,CAAC;AAAA,MACpE;AAEA,UAAI;AACF,gBAAQ,KAAK,MAAM,GAAG,CAAC;AAAA,MACzB,QAAQ;AACN,eAAO,IAAI,MAAM,mBAAmB,CAAC;AAAA,MACvC;AAAA,IACF,CAAC;AACD,QAAI,GAAG,SAAS,MAAM;AAAA,EACxB,CAAC;AACH;AAEA,eAAsB,aACpB,OACA,QACA,QACA,KACA,KACA,YACA,WACA,WACe;AACf,MAAI;AAEF,QAAI,MAAe,CAAC;AACpB,QAAI,WAAW;AACb,YAAM,SAAS,MAAM,wBAAwB,KAAK,KAAK,YAAY,SAAS;AAC5E,UAAI,OAAO,QAAS;AACpB,YAAM,OAAO;AAAA,IACf;AAEA,UAAM,MAAM,MAAM,WAAW,MAAM,QAAQ;AAC3C,UAAM,cAAc,IAAI,MAAM;AAE9B,QAAI,CAAC,aAAa;AAChB,gBAAU,KAAK,sBAAsB,UAAU,MAAM,gBAAgB,KAAK,QAAW,SAAS;AAC9F;AAAA,IACF;AAEA,UAAM,UAAU,OAAO,gBAAgB,aAAa,cAAe,YAAwC;AAC3G,QAAI,OAAO,YAAY,YAAY;AACjC,gBAAU,KAAK,kBAAkB,mCAAmC,KAAK,QAAW,SAAS;AAC7F;AAAA,IACF;AAGA,UAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,IAAI,QAAQ,QAAQ,WAAW,EAAE;AAC/E,UAAM,QAAgC,OAAO,YAAY,IAAI,YAAY;AAGzE,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,UAAU,GAAG;AAAA,IAC5B,SAAS,KAAK;AACZ,gBAAU,KAAK,oBAAqB,IAAc,SAAS,KAAK,QAAW,SAAS;AACpF;AAAA,IACF;AAGA,UAAM,KAAK;AACX,QAAI,GAAG,SAAS,OAAQ,GAAG,MAAkC,cAAc,YAAY;AACrF,YAAM,SAAU,GAAG,MAAkC,UAAU,KAAK;AACpE,UAAI,CAAC,OAAO,SAAS;AACnB,kBAAU,KAAK,oBAAoB,4BAA4B,KAAK,OAAO,MAAM,QAAQ,SAAS;AAClG;AAAA,MACF;AACA,aAAO,OAAO,OAAO,OAAO,IAAI;AAAA,IAClC;AAEA,QAAI,GAAG,QAAQ,OAAQ,GAAG,KAAiC,cAAc,YAAY;AACnF,YAAM,SAAU,GAAG,KAAiC,UAAU,IAAI;AAClE,UAAI,CAAC,OAAO,SAAS;AACnB,kBAAU,KAAK,oBAAoB,wBAAwB,KAAK,OAAO,MAAM,QAAQ,SAAS;AAC9F;AAAA,MACF;AACA,aAAO,OAAO;AAAA,IAChB;AAEA,QAAI,GAAG,UAAU,OAAQ,GAAG,OAAmC,cAAc,YAAY;AACvF,YAAM,SAAU,GAAG,OAAmC,UAAU,MAAM;AACtE,UAAI,CAAC,OAAO,SAAS;AACnB,kBAAU,KAAK,oBAAoB,4BAA4B,KAAK,OAAO,MAAM,QAAQ,SAAS;AAClG;AAAA,MACF;AACA,aAAO,OAAO,QAAQ,OAAO,IAAI;AAAA,IACnC;AAGA,UAAM,gBAAgB,MAAM,QAAQ,EAAE,OAAO,MAAM,QAAQ,SAAS,KAAK,IAAI,CAAC;AAG9E,QAAI,kBAAkB,UAAa,kBAAkB,MAAM;AACzD,eAAS,KAAK,MAAO,GAAG,UAAqB,GAAG;AAChD;AAAA,IACF;AAEA,QAAI,yBAAyB,UAAU;AACrC,UAAI,UAAU,cAAc,QAAQ,OAAO,YAAY,cAAc,OAAO,CAAC;AAE7E,UAAI,cAAc,MAAM;AACtB,cAAM,SAAS,cAAc,KAAK,UAAU;AAC5C,YAAI;AACF,iBAAO,MAAM;AACX,kBAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,gBAAI,KAAM;AACV,gBAAI,MAAM,KAAK;AAAA,UACjB;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,UAAI,IAAI;AACR;AAAA,IACF;AAEA,aAAS,KAAK,eAAgB,GAAG,UAAqB,GAAG;AAAA,EAC3D,SAAS,KAAK;AACZ,QAAI,eAAe,mBAAmB;AACpC,gBAAU,KAAK,IAAI,MAAM,IAAI,SAAS,IAAI,QAAQ,QAAW,SAAS;AACtE;AAAA,IACF;AACA,cAAU,KAAK,kBAAmB,IAAc,WAAW,yBAAyB,KAAK,QAAW,SAAS;AAAA,EAC/G;AACF;;;AChMA,SAAS,qBAAqB;AAIvB,SAAS,iBAAiB,MAAiC;AAChE,SAAO,CAAC,SAAS,KAAK,cAAc,IAAI;AAC1C;AAEO,SAAS,yBAAqC;AACnD,SAAO,OAAO,SAAS;AACrB,UAAM,MAAM,cAAc,IAAI,EAAE;AAChC,WAAO,OAAO;AAAA,EAChB;AACF;;;ACFA,IAAM,gBAA0B,CAAC,QAAQ;AACvC,UAAQ,IAAI,KAAK,UAAU,GAAG,CAAC;AACjC;AAEO,SAAS,WACd,MACA,cACM;AACN,QAAM,MAAkB;AAAA,IACtB,OAAO;AAAA,IACP,GAAG;AAAA,IACH,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,EACpC;AACA,QAAM,SAAS,gBAAgB;AAC/B,SAAO,GAAG;AACZ;;;ACVO,SAAS,kBAAkB,QAAyB;AACzD,QAAM,QAAQ,oBAAI,IAAwB;AAC1C,MAAI,aAAa;AAEjB,SAAO,SAAS,eAAe,KAAuC;AACpE,UAAM,MAAM,IAAI,QAAQ,iBAAiB;AACzC,UAAM,MAAM,KAAK,IAAI;AAGrB,QAAI,EAAE,aAAa,QAAS,GAAG;AAC7B,iBAAW,CAAC,GAAG,CAAC,KAAK,OAAO;AAC1B,YAAI,EAAE,UAAU,IAAK,OAAM,OAAO,CAAC;AAAA,MACrC;AAAA,IACF;AAEA,UAAM,QAAQ,MAAM,IAAI,GAAG;AAE3B,QAAI,CAAC,SAAS,MAAM,MAAM,SAAS;AACjC,YAAM,IAAI,KAAK,EAAE,OAAO,GAAG,SAAS,MAAM,OAAO,SAAS,CAAC;AAC3D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS;AAAA,UACP,qBAAqB,OAAO,OAAO,GAAG;AAAA,UACtC,yBAAyB,OAAO,OAAO,MAAM,CAAC;AAAA,QAChD;AAAA,MACF;AAAA,IACF;AAEA,UAAM;AAEN,QAAI,MAAM,QAAQ,OAAO,KAAK;AAC5B,YAAM,aAAa,KAAK,MAAM,MAAM,UAAU,OAAO,GAAI;AACzD,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS;AAAA,UACP,qBAAqB,OAAO,OAAO,GAAG;AAAA,UACtC,yBAAyB;AAAA,UACzB,eAAe,OAAO,UAAU;AAAA,QAClC;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,SAAS;AAAA,MACT,SAAS;AAAA,QACP,qBAAqB,OAAO,OAAO,GAAG;AAAA,QACtC,yBAAyB,OAAO,OAAO,MAAM,MAAM,KAAK;AAAA,MAC1D;AAAA,IACF;AAAA,EACF;AACF;;;ACjEO,SAAS,aACd,KACoD;AAEpD,MAAI,IAAI,QAAQ,eAAe,MAAM,KAAK;AACxC,WAAO,EAAE,OAAO,OAAO,QAAQ,+BAA+B;AAAA,EAChE;AAGA,QAAM,SAAS,IAAI,QAAQ,QAAQ;AACnC,MAAI,CAAC,QAAQ;AAEX,WAAO,EAAE,OAAO,KAAK;AAAA,EACvB;AAEA,QAAM,OAAO,IAAI,QAAQ,MAAM;AAC/B,MAAI,CAAC,MAAM;AACT,WAAO,EAAE,OAAO,KAAK;AAAA,EACvB;AAEA,MAAI;AACF,UAAM,aAAa,IAAI,IAAI,MAAM,EAAE;AACnC,QAAI,eAAe,MAAM;AACvB,aAAO,EAAE,OAAO,OAAO,QAAQ,UAAU,MAAM,wBAAwB,IAAI,GAAG;AAAA,IAChF;AAAA,EACF,QAAQ;AACN,WAAO,EAAE,OAAO,OAAO,QAAQ,mBAAmB,MAAM,GAAG;AAAA,EAC7D;AAEA,SAAO,EAAE,OAAO,KAAK;AACvB;;;ACzBA,eAAsB,cACpB,UACA,YACA,KACA,KACA,YACA,WACA,WACe;AACf,MAAI;AAEF,UAAM,UAAU,IAAI,UAAU,OAAO,YAAY;AACjD,QAAI,WAAW,QAAQ;AACrB,gBAAU,KAAK,sBAAsB,4BAA4B,KAAK,QAAW,SAAS;AAC1F;AAAA,IACF;AAGA,UAAM,OAAO,aAAa,GAAG;AAC7B,QAAI,CAAC,KAAK,OAAO;AACf,gBAAU,KAAK,aAAa,KAAK,QAAQ,KAAK,QAAW,SAAS;AAClE;AAAA,IACF;AAGA,QAAI,MAAe,CAAC;AACpB,QAAI,WAAW;AACb,YAAMA,UAAS,MAAM,wBAAwB,KAAK,KAAK,YAAY,SAAS;AAC5E,UAAIA,QAAO,QAAS;AACpB,YAAMA,QAAO;AAAA,IACf;AAGA,UAAM,MAAM,MAAM,WAAW,QAAQ;AAGrC,UAAM,eAAe,IAAI,UAAU;AACnC,QAAI,CAAC,gBAAgB,OAAO,aAAa,YAAY,cAAc,CAAC,aAAa,OAAO;AACtF,gBAAU,KAAK,aAAa,WAAW,UAAU,eAAe,KAAK,QAAW,SAAS;AACzF;AAAA,IACF;AAGA,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,UAAU,GAAG;AAAA,IAC5B,SAAS,KAAK;AACZ,gBAAU,KAAK,oBAAqB,IAAc,SAAS,KAAK,QAAW,SAAS;AACpF;AAAA,IACF;AAGA,UAAM,QAAQ,aAAa;AAC3B,UAAM,SAAS,MAAM,UAAU,IAAI;AACnC,QAAI,CAAC,OAAO,SAAS;AACnB,gBAAU,KAAK,oBAAoB,wBAAwB,KAAK,OAAO,OAAO,QAAQ,SAAS;AAC/F;AAAA,IACF;AAGA,UAAM,gBAAgB,MAAO,aAAa,QAAqB,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;AAG1F,QAAI,kBAAkB,UAAa,kBAAkB,MAAM;AACzD,eAAS,KAAK,MAAM,GAAG;AACvB;AAAA,IACF;AAEA,aAAS,KAAK,eAAe,GAAG;AAAA,EAClC,SAAS,KAAK;AACZ,QAAI,eAAe,mBAAmB;AACpC,gBAAU,KAAK,IAAI,MAAM,IAAI,SAAS,IAAI,QAAQ,QAAW,SAAS;AACtE;AAAA,IACF;AACA,cAAU,KAAK,kBAAmB,IAAc,WAAW,yBAAyB,KAAK,QAAW,SAAS;AAAA,EAC/G;AACF;","names":["result"]}
|