@yak-io/nextjs 0.8.1 → 0.9.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/bin/yak-nextjs.mjs +10 -0
- package/dist/cli/generate-manifest.js +0 -0
- package/dist/index.server.cjs +3 -2
- package/dist/index.server.cjs.map +2 -2
- package/dist/index.server.js +3 -2
- package/dist/index.server.js.map +2 -2
- package/dist/server/route-manifest-adapter.d.ts +28 -0
- package/dist/server/route-manifest-adapter.d.ts.map +1 -1
- package/package.json +5 -4
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Launcher for the `yak-nextjs` CLI.
|
|
3
|
+
//
|
|
4
|
+
// Pointing `bin` straight at `dist/cli/generate-manifest.js` makes pnpm warn
|
|
5
|
+
// (and fail to link the shim) on a clean monorepo install, because `dist/`
|
|
6
|
+
// is not built until after install — build-on-install was removed because it
|
|
7
|
+
// deadlocked the install step. This committed launcher always exists, so the
|
|
8
|
+
// bin symlink resolves at install time; it imports the built CLI at runtime,
|
|
9
|
+
// which runs `main()` against `process.argv`, so callers see no difference.
|
|
10
|
+
import "../dist/cli/generate-manifest.js";
|
|
File without changes
|
package/dist/index.server.cjs
CHANGED
|
@@ -353,11 +353,12 @@ function filterRoutes(routes, allowedRoutes, disallowedRoutes) {
|
|
|
353
353
|
});
|
|
354
354
|
}
|
|
355
355
|
function createRouteManifestAdapter(config) {
|
|
356
|
-
const { routes, id = "manifest", allowedRoutes, disallowedRoutes } = config;
|
|
356
|
+
const { routes, id = "manifest", allowedRoutes, disallowedRoutes, transform } = config;
|
|
357
357
|
return {
|
|
358
358
|
id,
|
|
359
359
|
getRoutes: async () => {
|
|
360
|
-
|
|
360
|
+
const filtered = filterRoutes(routes, allowedRoutes, disallowedRoutes);
|
|
361
|
+
return transform ? filtered.map(transform) : filtered;
|
|
361
362
|
}
|
|
362
363
|
};
|
|
363
364
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.server.ts", "../src/server/createNextYakHandler.ts", "../src/server/scan-routes.ts", "../src/server/route-manifest-adapter.ts"],
|
|
4
|
-
"sourcesContent": ["// Re-export everything from server/index.ts\nexport * from \"./server/index.js\";\n", "import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type {\n RouteInfo,\n RouteManifest,\n RouteSourceInput,\n ToolExecutor,\n ToolManifest,\n ToolSourceInput,\n} from \"@yak-io/javascript/server\";\nimport {\n createYakConfigHandler,\n createYakHandler,\n createYakToolsHandler,\n} from \"@yak-io/javascript/server\";\nimport { scanRoutes } from \"./scan-routes.js\";\n\n/**\n * Default paths to check for pre-built route manifests (JSON files).\n * These are read via fs.readFileSync at runtime.\n *\n * Priority:\n * 1. ./src/yak-routes-manifest.json - Default output, bundled by Next.js file tracing\n * 2. ./yak-routes-manifest.json - Root fallback\n * 3. ./public/yak-routes-manifest.json - Legacy location (doesn't work on Vercel serverless)\n */\nconst DEFAULT_MANIFEST_PATHS = [\n \"./src/yak-routes-manifest.json\",\n \"./yak-routes-manifest.json\",\n \"./public/yak-routes-manifest.json\",\n] as const;\n\n/**\n * Load a pre-built route manifest from disk (JSON file).\n *\n * This works in traditional Node.js deployments where the filesystem is available.\n * For Vercel serverless, the manifest must be imported directly in your route file.\n *\n * Generate the manifest at build time using:\n * yak-nextjs generate-manifest\n *\n * @param manifestPath - Path to the manifest JSON file (default: tries common locations)\n * @returns Route manifest or null if not found\n */\nexport function loadRouteManifest(manifestPath?: string): RouteManifest | null {\n const pathsToTry = manifestPath ? [manifestPath] : [...DEFAULT_MANIFEST_PATHS];\n\n for (const relativePath of pathsToTry) {\n try {\n const fullPath = path.resolve(process.cwd(), relativePath);\n if (fs.existsSync(fullPath)) {\n const content = fs.readFileSync(fullPath, \"utf-8\");\n return JSON.parse(content) as RouteManifest;\n }\n } catch {\n // Continue to next path\n }\n }\n\n return null;\n}\n\n/**\n * Load routes from a pre-built manifest.\n * Throws if manifest is not found.\n */\nexport function loadRoutes(manifestPath?: string): RouteInfo[] {\n const manifest = loadRouteManifest(manifestPath);\n if (!manifest) {\n const pathsChecked = manifestPath ? manifestPath : [...DEFAULT_MANIFEST_PATHS].join(\", \");\n throw new Error(\n `Route manifest not found. Checked: ${pathsChecked}\\n\\n` +\n \"In production environments (like Vercel), route scanning requires a pre-built manifest.\\n\" +\n \"Generate it at build time by adding to your build script:\\n\\n\" +\n \" yak-nextjs generate-manifest\\n\\n\" +\n \"Or provide routes explicitly in your handler configuration.\"\n );\n }\n return manifest.routes;\n}\n\nexport type NextYakRouteFilter = {\n include?: RegExp[];\n exclude?: RegExp[];\n};\n\nexport type NextYakHandlerConfig = {\n appDir?: string;\n routeFilter?: NextYakRouteFilter;\n routes?: RouteSourceInput;\n getRoutes?: () => Promise<RouteInfo[]>;\n tools?: ToolSourceInput;\n pagesDir?: string | false;\n getTools?: () => Promise<ToolManifest>;\n executeTool?: ToolExecutor;\n};\n\n/**\n * Create a unified Next.js App Router handler backed by the core primitives\n */\nexport function createNextYakHandler(config: NextYakHandlerConfig) {\n return createYakHandler({\n routes: resolveRouteSources(config),\n tools: resolveToolSources(config),\n });\n}\n\n/**\n * Attempt to load routes from filesystem or fetch from public URL.\n *\n * In development: scans the app directory directly\n * In production (Vercel, etc.): falls back to pre-built manifest via filesystem or HTTP fetch\n */\nasync function tryLoadRoutes(appDir: string): Promise<RouteInfo[]> {\n const targetDir = path.resolve(process.cwd(), appDir);\n\n // If the app directory exists, scan it directly (development mode)\n if (fs.existsSync(targetDir)) {\n return scanRoutes(appDir);\n }\n\n // In production, the source directory doesn't exist - try loading from manifest file\n const manifest = loadRouteManifest();\n if (manifest) {\n return manifest.routes;\n }\n\n // Neither source nor manifest available - provide helpful error\n throw new Error(\n \"Route manifest not found.\\n\\n\" +\n \"Generate the manifest at build time by adding to package.json:\\n\" +\n ` \"prebuild\": \"yak-nextjs generate-manifest\"\\n\\n` +\n \"The manifest will be created at ./src/yak-routes-manifest.json and automatically\\n\" +\n \"bundled with your serverless function.\\n\\n\" +\n \"Alternatively, provide routes explicitly:\\n\" +\n ` createNextYakHandler({ routes: [{ path: \"/\", title: \"Home\" }] })`\n );\n}\n\nfunction resolveRouteSources(config: NextYakHandlerConfig): RouteSourceInput {\n if (config.routes) {\n return config.routes;\n }\n if (config.getRoutes) {\n return config.getRoutes;\n }\n return async () => {\n const routes = await tryLoadRoutes(config.appDir ?? \"./src/app\");\n return applyRouteFilters(routes, config.routeFilter);\n };\n}\n\nfunction applyRouteFilters(routes: RouteInfo[], filter?: NextYakRouteFilter): RouteInfo[] {\n if (!filter) {\n return routes;\n }\n\n const { include = [], exclude = [] } = filter;\n\n return routes.filter((route) => {\n const path = route.path;\n\n if (include.length > 0 && !include.some((pattern) => matches(pattern, path))) {\n return false;\n }\n\n if (exclude.some((pattern) => matches(pattern, path))) {\n return false;\n }\n\n return true;\n });\n}\n\nfunction matches(pattern: RegExp, value: string): boolean {\n pattern.lastIndex = 0;\n return pattern.test(value);\n}\n\nfunction resolveToolSources(config: NextYakHandlerConfig): ToolSourceInput | undefined {\n if (config.tools) {\n return config.tools;\n }\n if (config.getTools) {\n return {\n getTools: config.getTools,\n executeTool: config.executeTool,\n } satisfies ToolSourceInput;\n }\n if (config.executeTool) {\n throw new Error(\"'executeTool' was provided without a matching tool manifest source\");\n }\n return undefined;\n}\n\nexport type NextYakConfigHandlerConfig = {\n appDir?: string;\n routeFilter?: NextYakRouteFilter;\n routes?: RouteSourceInput;\n getRoutes?: () => Promise<RouteInfo[]>;\n tools?: ToolSourceInput;\n getTools?: () => Promise<ToolManifest>;\n};\n\nexport function createNextYakConfigHandler(config: NextYakConfigHandlerConfig) {\n return createYakConfigHandler({\n routes: resolveRouteSources(config as NextYakHandlerConfig),\n tools: config.tools ?? (config.getTools ? { getTools: config.getTools } : undefined),\n });\n}\n\nexport type NextYakToolsHandlerConfig = {\n tools?: ToolSourceInput;\n getTools?: () => Promise<ToolManifest>;\n executeTool: ToolExecutor;\n};\n\nexport function createNextYakToolsHandler(config: NextYakToolsHandlerConfig) {\n if (!config.tools && !config.getTools) {\n throw new Error(\"createNextYakToolsHandler requires either 'tools' or 'getTools'\");\n }\n\n const toolSource: ToolSourceInput = config.tools ?? {\n // getTools is guaranteed to be defined here since we check above\n getTools: config.getTools as NonNullable<typeof config.getTools>,\n executeTool: config.executeTool,\n };\n\n return createYakToolsHandler({ tools: toolSource });\n}\n", "import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { RouteInfo } from \"@yak-io/javascript/server\";\n\nexport type ScanRoutesOptions = {\n directoryType?: \"app\" | \"pages\";\n};\n\n/**\n * Extract static metadata (title and description) from a Next.js page file.\n * Parses `export const metadata = { title: \"...\", description: \"...\" }` patterns.\n * Does not support dynamic generateMetadata functions.\n */\nfunction extractMetadata(filePath: string): { title?: string; description?: string } {\n try {\n const content = fs.readFileSync(filePath, \"utf-8\");\n\n // Match `export const metadata` object\n const metadataMatch = content.match(\n /export\\s+const\\s+metadata\\s*(?::\\s*Metadata\\s*)?=\\s*\\{([^}]*(?:\\{[^}]*\\}[^}]*)*)\\}/s\n );\n\n if (!metadataMatch) {\n return {};\n }\n\n const metadataBlock = metadataMatch[1];\n const result: { title?: string; description?: string } = {};\n\n // Extract title - handle both single and double quotes\n const titleMatch = metadataBlock?.match(/title\\s*:\\s*[\"'`]([^\"'`]+)[\"'`]/);\n if (titleMatch?.[1]) {\n result.title = titleMatch[1];\n }\n\n // Extract description - handle both single and double quotes\n const descMatch = metadataBlock?.match(/description\\s*:\\s*[\"'`]([^\"'`]+)[\"'`]/);\n if (descMatch?.[1]) {\n result.description = descMatch[1];\n }\n\n return result;\n } catch {\n return {};\n }\n}\n\n/**\n * Check if a path segment is a route group (organizational only, not part of URL)\n */\nfunction isRouteGroup(segment: string): boolean {\n return segment.startsWith(\"(\") && segment.endsWith(\")\");\n}\n\n/**\n * Check if a path segment is an optional catch-all (e.g., [[...slug]])\n * These segments match the base path without any additional segments\n */\nfunction isOptionalCatchAll(segment: string): boolean {\n return segment.startsWith(\"[[...\") && segment.endsWith(\"]]\");\n}\n\n/**\n * Check if a path segment is a required catch-all (e.g., [...slug])\n * These segments require at least one additional path segment\n */\nfunction isRequiredCatchAll(segment: string): boolean {\n return segment.startsWith(\"[...\") && segment.endsWith(\"]\") && !segment.startsWith(\"[[\");\n}\n\n/**\n * Check if a path segment is a dynamic segment (e.g., [id])\n */\nfunction isDynamicSegment(segment: string): boolean {\n return (\n segment.startsWith(\"[\") &&\n segment.endsWith(\"]\") &&\n !isOptionalCatchAll(segment) &&\n !isRequiredCatchAll(segment)\n );\n}\n\n/**\n * Normalize a dynamic segment for display\n * e.g., [id] \u2192 :id, [postId] \u2192 :postId\n */\nfunction normalizeDynamicSegment(segment: string): string {\n // Extract the parameter name from [name] format\n const paramName = segment.slice(1, -1);\n return `:${paramName}`;\n}\n\n/**\n * Normalize a route path for display (excludes route groups and handles dynamic segments)\n */\nfunction normalizeRoutePath(segments: string[]): string {\n // Filter out route groups - they don't appear in URLs\n // Filter out optional catch-all segments - they match the base path\n const urlSegments = segments\n .filter((seg) => !isRouteGroup(seg) && !isOptionalCatchAll(seg))\n .map((seg) => {\n if (isDynamicSegment(seg)) {\n return normalizeDynamicSegment(seg);\n }\n if (isRequiredCatchAll(seg)) {\n // For required catch-all, extract the name: [...slug] \u2192 :slug+\n const paramName = seg.slice(4, -1);\n return `:${paramName}+`;\n }\n return seg;\n });\n if (urlSegments.length === 0) return \"/\";\n return `/${urlSegments.join(\"/\")}`;\n}\n\n/**\n * Check if file is a page file\n */\nfunction isPageFile(filename: string): boolean {\n return filename === \"page.tsx\" || filename === \"page.js\";\n}\n\n/**\n * Recursively scan a directory for Next.js page routes\n */\nfunction scanAppDirectory(dirPath: string, segments: string[] = []): RouteInfo[] {\n const routes: RouteInfo[] = [];\n\n try {\n const entries = fs.readdirSync(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dirPath, entry.name);\n\n if (entry.isDirectory()) {\n // Skip special Next.js directories and api routes\n if (entry.name.startsWith(\"_\") || entry.name === \"api\") {\n continue;\n }\n\n // Recursively scan subdirectories\n routes.push(...scanAppDirectory(fullPath, [...segments, entry.name]));\n } else if (entry.isFile() && isPageFile(entry.name)) {\n const metadata = extractMetadata(fullPath);\n\n routes.push({\n path: normalizeRoutePath(segments),\n title: metadata.title,\n description: metadata.description,\n });\n }\n }\n } catch (error) {\n console.error(`Error scanning directory ${dirPath}:`, error);\n }\n\n return routes;\n}\n\n/**\n * File discovery helpers for legacy `pages/` directories\n */\nconst VALID_PAGE_EXTENSIONS = new Set([\".js\", \".jsx\", \".ts\", \".tsx\", \".md\", \".mdx\"]);\nconst SPECIAL_PAGE_FILENAMES = new Set([\n \"_app\",\n \"_document\",\n \"_error\",\n \"404\",\n \"500\",\n \"middleware\",\n \"_middleware\",\n]);\n\nfunction scanPagesDirectory(dirPath: string, segments: string[] = []): RouteInfo[] {\n const routes: RouteInfo[] = [];\n\n try {\n const entries = fs.readdirSync(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dirPath, entry.name);\n\n if (entry.isDirectory()) {\n // Skip special directories and api routes\n if (entry.name.startsWith(\"_\") || entry.name === \"api\") {\n continue;\n }\n\n routes.push(...scanPagesDirectory(fullPath, [...segments, entry.name]));\n } else if (entry.isFile()) {\n const extension = path.extname(entry.name);\n if (!VALID_PAGE_EXTENSIONS.has(extension)) {\n continue;\n }\n\n const baseName = entry.name.slice(0, -extension.length);\n if (!baseName || SPECIAL_PAGE_FILENAMES.has(baseName) || baseName.startsWith(\"_\")) {\n continue;\n }\n\n const routeSegments = buildPagesSegments(segments, baseName);\n const metadata = extractMetadata(fullPath);\n\n routes.push({\n path: normalizeRoutePath(routeSegments),\n title: metadata.title,\n description: metadata.description,\n });\n }\n }\n } catch (error) {\n console.error(`Error scanning directory ${dirPath}:`, error);\n }\n\n return routes;\n}\n\nfunction buildPagesSegments(segments: string[], baseName: string): string[] {\n const routeSegments = [...segments];\n if (baseName !== \"index\") {\n routeSegments.push(baseName);\n }\n return routeSegments;\n}\n\ntype NormalizedScanOptions = {\n directoryType: \"app\" | \"pages\";\n};\n\nfunction normalizeScanOptions(options?: ScanRoutesOptions): NormalizedScanOptions {\n return {\n directoryType: options?.directoryType ?? \"app\",\n };\n}\n\n/**\n * Scan a Next.js app or pages directory and return page route information\n */\nexport function scanRoutes(directory: string, options?: ScanRoutesOptions): RouteInfo[] {\n const normalizedOptions = normalizeScanOptions(options);\n const targetDir = path.resolve(process.cwd(), directory);\n\n if (!fs.existsSync(targetDir)) {\n throw new Error(`App directory not found: ${targetDir}`);\n }\n\n const routes =\n normalizedOptions.directoryType === \"pages\"\n ? scanPagesDirectory(targetDir, [])\n : scanAppDirectory(targetDir, []);\n\n return routes.sort((a: RouteInfo, b: RouteInfo) => a.path.localeCompare(b.path));\n}\n", "import type { RouteInfo, RouteSource } from \"@yak-io/javascript/server\";\n\n/**\n * Configuration for creating a route manifest adapter\n */\nexport type RouteManifestAdapterConfig = {\n /**\n * The routes from the generated manifest.\n * Import from the generated file: `import { routes } from \"@/yak.routes\"`\n */\n routes: RouteInfo[];\n\n /**\n * Optional ID for this route source (default: \"manifest\")\n */\n id?: string;\n\n /**\n * List of allowed route paths, e.g. [\"/\", \"/docs\", \"/pricing\"].\n * Supports exact matches and prefix patterns ending with `*`.\n * If provided, only these routes will be included.\n * If omitted, all routes are allowed by default.\n *\n * @example\n * ```ts\n * allowedRoutes: [\"/docs/*\", \"/pricing\", \"/\"]\n * ```\n */\n allowedRoutes?: string[];\n\n /**\n * List of disallowed route paths, e.g. [\"/admin/*\", \"/internal/*\"].\n * Supports exact matches and prefix patterns ending with `*`.\n * These routes will be excluded even if they would otherwise be allowed.\n * Applied after allowedRoutes filtering.\n *\n * @example\n * ```ts\n * disallowedRoutes: [\"/admin/*\", \"/settings/billing\"]\n * ```\n */\n disallowedRoutes?: string[];\n};\n\n/**\n * Check if a route path matches a pattern.\n * Supports exact matches and prefix patterns ending with `*`.\n */\nfunction matchesPattern(pattern: string, routePath: string): boolean {\n if (pattern.endsWith(\"/*\")) {\n const prefix = pattern.slice(0, -1); // Remove the *\n return routePath === prefix.slice(0, -1) || routePath.startsWith(prefix);\n }\n return pattern === routePath;\n}\n\n/**\n * Filter routes based on allowed and disallowed patterns.\n */\nfunction filterRoutes(\n routes: RouteInfo[],\n allowedRoutes?: string[],\n disallowedRoutes?: string[]\n): RouteInfo[] {\n return routes.filter((route) => {\n const path = route.path;\n\n // If allowedRoutes is set, route must match at least one pattern\n if (allowedRoutes && allowedRoutes.length > 0) {\n const isAllowed = allowedRoutes.some((pattern) => matchesPattern(pattern, path));\n if (!isAllowed) {\n return false;\n }\n }\n\n // If disallowedRoutes is set, route must not match any pattern\n if (disallowedRoutes && disallowedRoutes.length > 0) {\n const isDisallowed = disallowedRoutes.some((pattern) => matchesPattern(pattern, path));\n if (isDisallowed) {\n return false;\n }\n }\n\n return true;\n });\n}\n\n/**\n * Create a route source adapter from an imported route manifest.\n *\n * This adapter allows you to use generated route manifests with optional\n * filtering to control which routes are exposed to the chatbot.\n *\n * @example Basic usage - all routes\n * ```ts\n * import { createNextYakHandler } from \"@yak-io/nextjs/server\";\n * import { createRouteManifestAdapter } from \"@yak-io/nextjs/server\";\n * import { routes } from \"@/yak.routes\";\n *\n * export const { GET, POST } = createNextYakHandler({\n * routes: createRouteManifestAdapter({ routes }),\n * });\n * ```\n *\n * @example With filtering\n * ```ts\n * import { createNextYakHandler } from \"@yak-io/nextjs/server\";\n * import { createRouteManifestAdapter } from \"@yak-io/nextjs/server\";\n * import { routes } from \"@/yak.routes\";\n *\n * export const { GET, POST } = createNextYakHandler({\n * routes: createRouteManifestAdapter({\n * routes,\n * allowedRoutes: [\"/docs/*\", \"/pricing\", \"/\"],\n * disallowedRoutes: [\"/docs/internal/*\"],\n * }),\n * });\n * ```\n */\nexport function createRouteManifestAdapter(config: RouteManifestAdapterConfig): RouteSource {\n const { routes, id = \"manifest\", allowedRoutes, disallowedRoutes } = config;\n\n return {\n id,\n getRoutes: async () => {\n return filterRoutes(routes, allowedRoutes, disallowedRoutes);\n },\n };\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["// Re-export everything from server/index.ts\nexport * from \"./server/index.js\";\n", "import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type {\n RouteInfo,\n RouteManifest,\n RouteSourceInput,\n ToolExecutor,\n ToolManifest,\n ToolSourceInput,\n} from \"@yak-io/javascript/server\";\nimport {\n createYakConfigHandler,\n createYakHandler,\n createYakToolsHandler,\n} from \"@yak-io/javascript/server\";\nimport { scanRoutes } from \"./scan-routes.js\";\n\n/**\n * Default paths to check for pre-built route manifests (JSON files).\n * These are read via fs.readFileSync at runtime.\n *\n * Priority:\n * 1. ./src/yak-routes-manifest.json - Default output, bundled by Next.js file tracing\n * 2. ./yak-routes-manifest.json - Root fallback\n * 3. ./public/yak-routes-manifest.json - Legacy location (doesn't work on Vercel serverless)\n */\nconst DEFAULT_MANIFEST_PATHS = [\n \"./src/yak-routes-manifest.json\",\n \"./yak-routes-manifest.json\",\n \"./public/yak-routes-manifest.json\",\n] as const;\n\n/**\n * Load a pre-built route manifest from disk (JSON file).\n *\n * This works in traditional Node.js deployments where the filesystem is available.\n * For Vercel serverless, the manifest must be imported directly in your route file.\n *\n * Generate the manifest at build time using:\n * yak-nextjs generate-manifest\n *\n * @param manifestPath - Path to the manifest JSON file (default: tries common locations)\n * @returns Route manifest or null if not found\n */\nexport function loadRouteManifest(manifestPath?: string): RouteManifest | null {\n const pathsToTry = manifestPath ? [manifestPath] : [...DEFAULT_MANIFEST_PATHS];\n\n for (const relativePath of pathsToTry) {\n try {\n const fullPath = path.resolve(process.cwd(), relativePath);\n if (fs.existsSync(fullPath)) {\n const content = fs.readFileSync(fullPath, \"utf-8\");\n return JSON.parse(content) as RouteManifest;\n }\n } catch {\n // Continue to next path\n }\n }\n\n return null;\n}\n\n/**\n * Load routes from a pre-built manifest.\n * Throws if manifest is not found.\n */\nexport function loadRoutes(manifestPath?: string): RouteInfo[] {\n const manifest = loadRouteManifest(manifestPath);\n if (!manifest) {\n const pathsChecked = manifestPath ? manifestPath : [...DEFAULT_MANIFEST_PATHS].join(\", \");\n throw new Error(\n `Route manifest not found. Checked: ${pathsChecked}\\n\\n` +\n \"In production environments (like Vercel), route scanning requires a pre-built manifest.\\n\" +\n \"Generate it at build time by adding to your build script:\\n\\n\" +\n \" yak-nextjs generate-manifest\\n\\n\" +\n \"Or provide routes explicitly in your handler configuration.\"\n );\n }\n return manifest.routes;\n}\n\nexport type NextYakRouteFilter = {\n include?: RegExp[];\n exclude?: RegExp[];\n};\n\nexport type NextYakHandlerConfig = {\n appDir?: string;\n routeFilter?: NextYakRouteFilter;\n routes?: RouteSourceInput;\n getRoutes?: () => Promise<RouteInfo[]>;\n tools?: ToolSourceInput;\n pagesDir?: string | false;\n getTools?: () => Promise<ToolManifest>;\n executeTool?: ToolExecutor;\n};\n\n/**\n * Create a unified Next.js App Router handler backed by the core primitives\n */\nexport function createNextYakHandler(config: NextYakHandlerConfig) {\n return createYakHandler({\n routes: resolveRouteSources(config),\n tools: resolveToolSources(config),\n });\n}\n\n/**\n * Attempt to load routes from filesystem or fetch from public URL.\n *\n * In development: scans the app directory directly\n * In production (Vercel, etc.): falls back to pre-built manifest via filesystem or HTTP fetch\n */\nasync function tryLoadRoutes(appDir: string): Promise<RouteInfo[]> {\n const targetDir = path.resolve(process.cwd(), appDir);\n\n // If the app directory exists, scan it directly (development mode)\n if (fs.existsSync(targetDir)) {\n return scanRoutes(appDir);\n }\n\n // In production, the source directory doesn't exist - try loading from manifest file\n const manifest = loadRouteManifest();\n if (manifest) {\n return manifest.routes;\n }\n\n // Neither source nor manifest available - provide helpful error\n throw new Error(\n \"Route manifest not found.\\n\\n\" +\n \"Generate the manifest at build time by adding to package.json:\\n\" +\n ` \"prebuild\": \"yak-nextjs generate-manifest\"\\n\\n` +\n \"The manifest will be created at ./src/yak-routes-manifest.json and automatically\\n\" +\n \"bundled with your serverless function.\\n\\n\" +\n \"Alternatively, provide routes explicitly:\\n\" +\n ` createNextYakHandler({ routes: [{ path: \"/\", title: \"Home\" }] })`\n );\n}\n\nfunction resolveRouteSources(config: NextYakHandlerConfig): RouteSourceInput {\n if (config.routes) {\n return config.routes;\n }\n if (config.getRoutes) {\n return config.getRoutes;\n }\n return async () => {\n const routes = await tryLoadRoutes(config.appDir ?? \"./src/app\");\n return applyRouteFilters(routes, config.routeFilter);\n };\n}\n\nfunction applyRouteFilters(routes: RouteInfo[], filter?: NextYakRouteFilter): RouteInfo[] {\n if (!filter) {\n return routes;\n }\n\n const { include = [], exclude = [] } = filter;\n\n return routes.filter((route) => {\n const path = route.path;\n\n if (include.length > 0 && !include.some((pattern) => matches(pattern, path))) {\n return false;\n }\n\n if (exclude.some((pattern) => matches(pattern, path))) {\n return false;\n }\n\n return true;\n });\n}\n\nfunction matches(pattern: RegExp, value: string): boolean {\n pattern.lastIndex = 0;\n return pattern.test(value);\n}\n\nfunction resolveToolSources(config: NextYakHandlerConfig): ToolSourceInput | undefined {\n if (config.tools) {\n return config.tools;\n }\n if (config.getTools) {\n return {\n getTools: config.getTools,\n executeTool: config.executeTool,\n } satisfies ToolSourceInput;\n }\n if (config.executeTool) {\n throw new Error(\"'executeTool' was provided without a matching tool manifest source\");\n }\n return undefined;\n}\n\nexport type NextYakConfigHandlerConfig = {\n appDir?: string;\n routeFilter?: NextYakRouteFilter;\n routes?: RouteSourceInput;\n getRoutes?: () => Promise<RouteInfo[]>;\n tools?: ToolSourceInput;\n getTools?: () => Promise<ToolManifest>;\n};\n\nexport function createNextYakConfigHandler(config: NextYakConfigHandlerConfig) {\n return createYakConfigHandler({\n routes: resolveRouteSources(config as NextYakHandlerConfig),\n tools: config.tools ?? (config.getTools ? { getTools: config.getTools } : undefined),\n });\n}\n\nexport type NextYakToolsHandlerConfig = {\n tools?: ToolSourceInput;\n getTools?: () => Promise<ToolManifest>;\n executeTool: ToolExecutor;\n};\n\nexport function createNextYakToolsHandler(config: NextYakToolsHandlerConfig) {\n if (!config.tools && !config.getTools) {\n throw new Error(\"createNextYakToolsHandler requires either 'tools' or 'getTools'\");\n }\n\n const toolSource: ToolSourceInput = config.tools ?? {\n // getTools is guaranteed to be defined here since we check above\n getTools: config.getTools as NonNullable<typeof config.getTools>,\n executeTool: config.executeTool,\n };\n\n return createYakToolsHandler({ tools: toolSource });\n}\n", "import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { RouteInfo } from \"@yak-io/javascript/server\";\n\nexport type ScanRoutesOptions = {\n directoryType?: \"app\" | \"pages\";\n};\n\n/**\n * Extract static metadata (title and description) from a Next.js page file.\n * Parses `export const metadata = { title: \"...\", description: \"...\" }` patterns.\n * Does not support dynamic generateMetadata functions.\n */\nfunction extractMetadata(filePath: string): { title?: string; description?: string } {\n try {\n const content = fs.readFileSync(filePath, \"utf-8\");\n\n // Match `export const metadata` object\n const metadataMatch = content.match(\n /export\\s+const\\s+metadata\\s*(?::\\s*Metadata\\s*)?=\\s*\\{([^}]*(?:\\{[^}]*\\}[^}]*)*)\\}/s\n );\n\n if (!metadataMatch) {\n return {};\n }\n\n const metadataBlock = metadataMatch[1];\n const result: { title?: string; description?: string } = {};\n\n // Extract title - handle both single and double quotes\n const titleMatch = metadataBlock?.match(/title\\s*:\\s*[\"'`]([^\"'`]+)[\"'`]/);\n if (titleMatch?.[1]) {\n result.title = titleMatch[1];\n }\n\n // Extract description - handle both single and double quotes\n const descMatch = metadataBlock?.match(/description\\s*:\\s*[\"'`]([^\"'`]+)[\"'`]/);\n if (descMatch?.[1]) {\n result.description = descMatch[1];\n }\n\n return result;\n } catch {\n return {};\n }\n}\n\n/**\n * Check if a path segment is a route group (organizational only, not part of URL)\n */\nfunction isRouteGroup(segment: string): boolean {\n return segment.startsWith(\"(\") && segment.endsWith(\")\");\n}\n\n/**\n * Check if a path segment is an optional catch-all (e.g., [[...slug]])\n * These segments match the base path without any additional segments\n */\nfunction isOptionalCatchAll(segment: string): boolean {\n return segment.startsWith(\"[[...\") && segment.endsWith(\"]]\");\n}\n\n/**\n * Check if a path segment is a required catch-all (e.g., [...slug])\n * These segments require at least one additional path segment\n */\nfunction isRequiredCatchAll(segment: string): boolean {\n return segment.startsWith(\"[...\") && segment.endsWith(\"]\") && !segment.startsWith(\"[[\");\n}\n\n/**\n * Check if a path segment is a dynamic segment (e.g., [id])\n */\nfunction isDynamicSegment(segment: string): boolean {\n return (\n segment.startsWith(\"[\") &&\n segment.endsWith(\"]\") &&\n !isOptionalCatchAll(segment) &&\n !isRequiredCatchAll(segment)\n );\n}\n\n/**\n * Normalize a dynamic segment for display\n * e.g., [id] \u2192 :id, [postId] \u2192 :postId\n */\nfunction normalizeDynamicSegment(segment: string): string {\n // Extract the parameter name from [name] format\n const paramName = segment.slice(1, -1);\n return `:${paramName}`;\n}\n\n/**\n * Normalize a route path for display (excludes route groups and handles dynamic segments)\n */\nfunction normalizeRoutePath(segments: string[]): string {\n // Filter out route groups - they don't appear in URLs\n // Filter out optional catch-all segments - they match the base path\n const urlSegments = segments\n .filter((seg) => !isRouteGroup(seg) && !isOptionalCatchAll(seg))\n .map((seg) => {\n if (isDynamicSegment(seg)) {\n return normalizeDynamicSegment(seg);\n }\n if (isRequiredCatchAll(seg)) {\n // For required catch-all, extract the name: [...slug] \u2192 :slug+\n const paramName = seg.slice(4, -1);\n return `:${paramName}+`;\n }\n return seg;\n });\n if (urlSegments.length === 0) return \"/\";\n return `/${urlSegments.join(\"/\")}`;\n}\n\n/**\n * Check if file is a page file\n */\nfunction isPageFile(filename: string): boolean {\n return filename === \"page.tsx\" || filename === \"page.js\";\n}\n\n/**\n * Recursively scan a directory for Next.js page routes\n */\nfunction scanAppDirectory(dirPath: string, segments: string[] = []): RouteInfo[] {\n const routes: RouteInfo[] = [];\n\n try {\n const entries = fs.readdirSync(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dirPath, entry.name);\n\n if (entry.isDirectory()) {\n // Skip special Next.js directories and api routes\n if (entry.name.startsWith(\"_\") || entry.name === \"api\") {\n continue;\n }\n\n // Recursively scan subdirectories\n routes.push(...scanAppDirectory(fullPath, [...segments, entry.name]));\n } else if (entry.isFile() && isPageFile(entry.name)) {\n const metadata = extractMetadata(fullPath);\n\n routes.push({\n path: normalizeRoutePath(segments),\n title: metadata.title,\n description: metadata.description,\n });\n }\n }\n } catch (error) {\n console.error(`Error scanning directory ${dirPath}:`, error);\n }\n\n return routes;\n}\n\n/**\n * File discovery helpers for legacy `pages/` directories\n */\nconst VALID_PAGE_EXTENSIONS = new Set([\".js\", \".jsx\", \".ts\", \".tsx\", \".md\", \".mdx\"]);\nconst SPECIAL_PAGE_FILENAMES = new Set([\n \"_app\",\n \"_document\",\n \"_error\",\n \"404\",\n \"500\",\n \"middleware\",\n \"_middleware\",\n]);\n\nfunction scanPagesDirectory(dirPath: string, segments: string[] = []): RouteInfo[] {\n const routes: RouteInfo[] = [];\n\n try {\n const entries = fs.readdirSync(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dirPath, entry.name);\n\n if (entry.isDirectory()) {\n // Skip special directories and api routes\n if (entry.name.startsWith(\"_\") || entry.name === \"api\") {\n continue;\n }\n\n routes.push(...scanPagesDirectory(fullPath, [...segments, entry.name]));\n } else if (entry.isFile()) {\n const extension = path.extname(entry.name);\n if (!VALID_PAGE_EXTENSIONS.has(extension)) {\n continue;\n }\n\n const baseName = entry.name.slice(0, -extension.length);\n if (!baseName || SPECIAL_PAGE_FILENAMES.has(baseName) || baseName.startsWith(\"_\")) {\n continue;\n }\n\n const routeSegments = buildPagesSegments(segments, baseName);\n const metadata = extractMetadata(fullPath);\n\n routes.push({\n path: normalizeRoutePath(routeSegments),\n title: metadata.title,\n description: metadata.description,\n });\n }\n }\n } catch (error) {\n console.error(`Error scanning directory ${dirPath}:`, error);\n }\n\n return routes;\n}\n\nfunction buildPagesSegments(segments: string[], baseName: string): string[] {\n const routeSegments = [...segments];\n if (baseName !== \"index\") {\n routeSegments.push(baseName);\n }\n return routeSegments;\n}\n\ntype NormalizedScanOptions = {\n directoryType: \"app\" | \"pages\";\n};\n\nfunction normalizeScanOptions(options?: ScanRoutesOptions): NormalizedScanOptions {\n return {\n directoryType: options?.directoryType ?? \"app\",\n };\n}\n\n/**\n * Scan a Next.js app or pages directory and return page route information\n */\nexport function scanRoutes(directory: string, options?: ScanRoutesOptions): RouteInfo[] {\n const normalizedOptions = normalizeScanOptions(options);\n const targetDir = path.resolve(process.cwd(), directory);\n\n if (!fs.existsSync(targetDir)) {\n throw new Error(`App directory not found: ${targetDir}`);\n }\n\n const routes =\n normalizedOptions.directoryType === \"pages\"\n ? scanPagesDirectory(targetDir, [])\n : scanAppDirectory(targetDir, []);\n\n return routes.sort((a: RouteInfo, b: RouteInfo) => a.path.localeCompare(b.path));\n}\n", "import type { RouteInfo, RouteSource } from \"@yak-io/javascript/server\";\n\n/**\n * Configuration for creating a route manifest adapter\n */\nexport type RouteManifestAdapterConfig = {\n /**\n * The routes from the generated manifest.\n * Import from the generated file: `import { routes } from \"@/yak.routes\"`\n */\n routes: RouteInfo[];\n\n /**\n * Optional ID for this route source (default: \"manifest\")\n */\n id?: string;\n\n /**\n * List of allowed route paths, e.g. [\"/\", \"/docs\", \"/pricing\"].\n * Supports exact matches and prefix patterns ending with `*`.\n * If provided, only these routes will be included.\n * If omitted, all routes are allowed by default.\n *\n * @example\n * ```ts\n * allowedRoutes: [\"/docs/*\", \"/pricing\", \"/\"]\n * ```\n */\n allowedRoutes?: string[];\n\n /**\n * List of disallowed route paths, e.g. [\"/admin/*\", \"/internal/*\"].\n * Supports exact matches and prefix patterns ending with `*`.\n * These routes will be excluded even if they would otherwise be allowed.\n * Applied after allowedRoutes filtering.\n *\n * @example\n * ```ts\n * disallowedRoutes: [\"/admin/*\", \"/settings/billing\"]\n * ```\n */\n disallowedRoutes?: string[];\n\n /**\n * Optional per-route enrichment, applied after filtering. Use it to annotate\n * auto-scanned routes with capabilities the scanner can't infer \u2014 most often\n * marking a route as searchable or declaring the filters it accepts so the\n * assistant can drive your UI (e.g. \"size 10 boots\" \u2192 `/products?size=10`)\n * instead of rendering results in the chat.\n *\n * Return the route unchanged to leave it as-is.\n *\n * @example\n * ```ts\n * createRouteManifestAdapter({\n * routes,\n * transform: (route) =>\n * route.path === \"/products\"\n * ? {\n * ...route,\n * search: { queryParam: \"q\" },\n * filters: [\n * { param: \"category\", description: \"Product category\" },\n * { param: \"size\", values: [\"8\", \"9\", \"10\", \"11\"] },\n * ],\n * }\n * : route,\n * });\n * ```\n */\n transform?: (route: RouteInfo) => RouteInfo;\n};\n\n/**\n * Check if a route path matches a pattern.\n * Supports exact matches and prefix patterns ending with `*`.\n */\nfunction matchesPattern(pattern: string, routePath: string): boolean {\n if (pattern.endsWith(\"/*\")) {\n const prefix = pattern.slice(0, -1); // Remove the *\n return routePath === prefix.slice(0, -1) || routePath.startsWith(prefix);\n }\n return pattern === routePath;\n}\n\n/**\n * Filter routes based on allowed and disallowed patterns.\n */\nfunction filterRoutes(\n routes: RouteInfo[],\n allowedRoutes?: string[],\n disallowedRoutes?: string[]\n): RouteInfo[] {\n return routes.filter((route) => {\n const path = route.path;\n\n // If allowedRoutes is set, route must match at least one pattern\n if (allowedRoutes && allowedRoutes.length > 0) {\n const isAllowed = allowedRoutes.some((pattern) => matchesPattern(pattern, path));\n if (!isAllowed) {\n return false;\n }\n }\n\n // If disallowedRoutes is set, route must not match any pattern\n if (disallowedRoutes && disallowedRoutes.length > 0) {\n const isDisallowed = disallowedRoutes.some((pattern) => matchesPattern(pattern, path));\n if (isDisallowed) {\n return false;\n }\n }\n\n return true;\n });\n}\n\n/**\n * Create a route source adapter from an imported route manifest.\n *\n * This adapter allows you to use generated route manifests with optional\n * filtering to control which routes are exposed to the chatbot.\n *\n * @example Basic usage - all routes\n * ```ts\n * import { createNextYakHandler } from \"@yak-io/nextjs/server\";\n * import { createRouteManifestAdapter } from \"@yak-io/nextjs/server\";\n * import { routes } from \"@/yak.routes\";\n *\n * export const { GET, POST } = createNextYakHandler({\n * routes: createRouteManifestAdapter({ routes }),\n * });\n * ```\n *\n * @example With filtering\n * ```ts\n * import { createNextYakHandler } from \"@yak-io/nextjs/server\";\n * import { createRouteManifestAdapter } from \"@yak-io/nextjs/server\";\n * import { routes } from \"@/yak.routes\";\n *\n * export const { GET, POST } = createNextYakHandler({\n * routes: createRouteManifestAdapter({\n * routes,\n * allowedRoutes: [\"/docs/*\", \"/pricing\", \"/\"],\n * disallowedRoutes: [\"/docs/internal/*\"],\n * }),\n * });\n * ```\n */\nexport function createRouteManifestAdapter(config: RouteManifestAdapterConfig): RouteSource {\n const { routes, id = \"manifest\", allowedRoutes, disallowedRoutes, transform } = config;\n\n return {\n id,\n getRoutes: async () => {\n const filtered = filterRoutes(routes, allowedRoutes, disallowedRoutes);\n return transform ? filtered.map(transform) : filtered;\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["fs", "path", "path", "path"]
|
|
7
7
|
}
|
package/dist/index.server.js
CHANGED
|
@@ -315,11 +315,12 @@ function filterRoutes(routes, allowedRoutes, disallowedRoutes) {
|
|
|
315
315
|
});
|
|
316
316
|
}
|
|
317
317
|
function createRouteManifestAdapter(config) {
|
|
318
|
-
const { routes, id = "manifest", allowedRoutes, disallowedRoutes } = config;
|
|
318
|
+
const { routes, id = "manifest", allowedRoutes, disallowedRoutes, transform } = config;
|
|
319
319
|
return {
|
|
320
320
|
id,
|
|
321
321
|
getRoutes: async () => {
|
|
322
|
-
|
|
322
|
+
const filtered = filterRoutes(routes, allowedRoutes, disallowedRoutes);
|
|
323
|
+
return transform ? filtered.map(transform) : filtered;
|
|
323
324
|
}
|
|
324
325
|
};
|
|
325
326
|
}
|
package/dist/index.server.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/server/createNextYakHandler.ts", "../src/server/scan-routes.ts", "../src/server/route-manifest-adapter.ts"],
|
|
4
|
-
"sourcesContent": ["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type {\n RouteInfo,\n RouteManifest,\n RouteSourceInput,\n ToolExecutor,\n ToolManifest,\n ToolSourceInput,\n} from \"@yak-io/javascript/server\";\nimport {\n createYakConfigHandler,\n createYakHandler,\n createYakToolsHandler,\n} from \"@yak-io/javascript/server\";\nimport { scanRoutes } from \"./scan-routes.js\";\n\n/**\n * Default paths to check for pre-built route manifests (JSON files).\n * These are read via fs.readFileSync at runtime.\n *\n * Priority:\n * 1. ./src/yak-routes-manifest.json - Default output, bundled by Next.js file tracing\n * 2. ./yak-routes-manifest.json - Root fallback\n * 3. ./public/yak-routes-manifest.json - Legacy location (doesn't work on Vercel serverless)\n */\nconst DEFAULT_MANIFEST_PATHS = [\n \"./src/yak-routes-manifest.json\",\n \"./yak-routes-manifest.json\",\n \"./public/yak-routes-manifest.json\",\n] as const;\n\n/**\n * Load a pre-built route manifest from disk (JSON file).\n *\n * This works in traditional Node.js deployments where the filesystem is available.\n * For Vercel serverless, the manifest must be imported directly in your route file.\n *\n * Generate the manifest at build time using:\n * yak-nextjs generate-manifest\n *\n * @param manifestPath - Path to the manifest JSON file (default: tries common locations)\n * @returns Route manifest or null if not found\n */\nexport function loadRouteManifest(manifestPath?: string): RouteManifest | null {\n const pathsToTry = manifestPath ? [manifestPath] : [...DEFAULT_MANIFEST_PATHS];\n\n for (const relativePath of pathsToTry) {\n try {\n const fullPath = path.resolve(process.cwd(), relativePath);\n if (fs.existsSync(fullPath)) {\n const content = fs.readFileSync(fullPath, \"utf-8\");\n return JSON.parse(content) as RouteManifest;\n }\n } catch {\n // Continue to next path\n }\n }\n\n return null;\n}\n\n/**\n * Load routes from a pre-built manifest.\n * Throws if manifest is not found.\n */\nexport function loadRoutes(manifestPath?: string): RouteInfo[] {\n const manifest = loadRouteManifest(manifestPath);\n if (!manifest) {\n const pathsChecked = manifestPath ? manifestPath : [...DEFAULT_MANIFEST_PATHS].join(\", \");\n throw new Error(\n `Route manifest not found. Checked: ${pathsChecked}\\n\\n` +\n \"In production environments (like Vercel), route scanning requires a pre-built manifest.\\n\" +\n \"Generate it at build time by adding to your build script:\\n\\n\" +\n \" yak-nextjs generate-manifest\\n\\n\" +\n \"Or provide routes explicitly in your handler configuration.\"\n );\n }\n return manifest.routes;\n}\n\nexport type NextYakRouteFilter = {\n include?: RegExp[];\n exclude?: RegExp[];\n};\n\nexport type NextYakHandlerConfig = {\n appDir?: string;\n routeFilter?: NextYakRouteFilter;\n routes?: RouteSourceInput;\n getRoutes?: () => Promise<RouteInfo[]>;\n tools?: ToolSourceInput;\n pagesDir?: string | false;\n getTools?: () => Promise<ToolManifest>;\n executeTool?: ToolExecutor;\n};\n\n/**\n * Create a unified Next.js App Router handler backed by the core primitives\n */\nexport function createNextYakHandler(config: NextYakHandlerConfig) {\n return createYakHandler({\n routes: resolveRouteSources(config),\n tools: resolveToolSources(config),\n });\n}\n\n/**\n * Attempt to load routes from filesystem or fetch from public URL.\n *\n * In development: scans the app directory directly\n * In production (Vercel, etc.): falls back to pre-built manifest via filesystem or HTTP fetch\n */\nasync function tryLoadRoutes(appDir: string): Promise<RouteInfo[]> {\n const targetDir = path.resolve(process.cwd(), appDir);\n\n // If the app directory exists, scan it directly (development mode)\n if (fs.existsSync(targetDir)) {\n return scanRoutes(appDir);\n }\n\n // In production, the source directory doesn't exist - try loading from manifest file\n const manifest = loadRouteManifest();\n if (manifest) {\n return manifest.routes;\n }\n\n // Neither source nor manifest available - provide helpful error\n throw new Error(\n \"Route manifest not found.\\n\\n\" +\n \"Generate the manifest at build time by adding to package.json:\\n\" +\n ` \"prebuild\": \"yak-nextjs generate-manifest\"\\n\\n` +\n \"The manifest will be created at ./src/yak-routes-manifest.json and automatically\\n\" +\n \"bundled with your serverless function.\\n\\n\" +\n \"Alternatively, provide routes explicitly:\\n\" +\n ` createNextYakHandler({ routes: [{ path: \"/\", title: \"Home\" }] })`\n );\n}\n\nfunction resolveRouteSources(config: NextYakHandlerConfig): RouteSourceInput {\n if (config.routes) {\n return config.routes;\n }\n if (config.getRoutes) {\n return config.getRoutes;\n }\n return async () => {\n const routes = await tryLoadRoutes(config.appDir ?? \"./src/app\");\n return applyRouteFilters(routes, config.routeFilter);\n };\n}\n\nfunction applyRouteFilters(routes: RouteInfo[], filter?: NextYakRouteFilter): RouteInfo[] {\n if (!filter) {\n return routes;\n }\n\n const { include = [], exclude = [] } = filter;\n\n return routes.filter((route) => {\n const path = route.path;\n\n if (include.length > 0 && !include.some((pattern) => matches(pattern, path))) {\n return false;\n }\n\n if (exclude.some((pattern) => matches(pattern, path))) {\n return false;\n }\n\n return true;\n });\n}\n\nfunction matches(pattern: RegExp, value: string): boolean {\n pattern.lastIndex = 0;\n return pattern.test(value);\n}\n\nfunction resolveToolSources(config: NextYakHandlerConfig): ToolSourceInput | undefined {\n if (config.tools) {\n return config.tools;\n }\n if (config.getTools) {\n return {\n getTools: config.getTools,\n executeTool: config.executeTool,\n } satisfies ToolSourceInput;\n }\n if (config.executeTool) {\n throw new Error(\"'executeTool' was provided without a matching tool manifest source\");\n }\n return undefined;\n}\n\nexport type NextYakConfigHandlerConfig = {\n appDir?: string;\n routeFilter?: NextYakRouteFilter;\n routes?: RouteSourceInput;\n getRoutes?: () => Promise<RouteInfo[]>;\n tools?: ToolSourceInput;\n getTools?: () => Promise<ToolManifest>;\n};\n\nexport function createNextYakConfigHandler(config: NextYakConfigHandlerConfig) {\n return createYakConfigHandler({\n routes: resolveRouteSources(config as NextYakHandlerConfig),\n tools: config.tools ?? (config.getTools ? { getTools: config.getTools } : undefined),\n });\n}\n\nexport type NextYakToolsHandlerConfig = {\n tools?: ToolSourceInput;\n getTools?: () => Promise<ToolManifest>;\n executeTool: ToolExecutor;\n};\n\nexport function createNextYakToolsHandler(config: NextYakToolsHandlerConfig) {\n if (!config.tools && !config.getTools) {\n throw new Error(\"createNextYakToolsHandler requires either 'tools' or 'getTools'\");\n }\n\n const toolSource: ToolSourceInput = config.tools ?? {\n // getTools is guaranteed to be defined here since we check above\n getTools: config.getTools as NonNullable<typeof config.getTools>,\n executeTool: config.executeTool,\n };\n\n return createYakToolsHandler({ tools: toolSource });\n}\n", "import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { RouteInfo } from \"@yak-io/javascript/server\";\n\nexport type ScanRoutesOptions = {\n directoryType?: \"app\" | \"pages\";\n};\n\n/**\n * Extract static metadata (title and description) from a Next.js page file.\n * Parses `export const metadata = { title: \"...\", description: \"...\" }` patterns.\n * Does not support dynamic generateMetadata functions.\n */\nfunction extractMetadata(filePath: string): { title?: string; description?: string } {\n try {\n const content = fs.readFileSync(filePath, \"utf-8\");\n\n // Match `export const metadata` object\n const metadataMatch = content.match(\n /export\\s+const\\s+metadata\\s*(?::\\s*Metadata\\s*)?=\\s*\\{([^}]*(?:\\{[^}]*\\}[^}]*)*)\\}/s\n );\n\n if (!metadataMatch) {\n return {};\n }\n\n const metadataBlock = metadataMatch[1];\n const result: { title?: string; description?: string } = {};\n\n // Extract title - handle both single and double quotes\n const titleMatch = metadataBlock?.match(/title\\s*:\\s*[\"'`]([^\"'`]+)[\"'`]/);\n if (titleMatch?.[1]) {\n result.title = titleMatch[1];\n }\n\n // Extract description - handle both single and double quotes\n const descMatch = metadataBlock?.match(/description\\s*:\\s*[\"'`]([^\"'`]+)[\"'`]/);\n if (descMatch?.[1]) {\n result.description = descMatch[1];\n }\n\n return result;\n } catch {\n return {};\n }\n}\n\n/**\n * Check if a path segment is a route group (organizational only, not part of URL)\n */\nfunction isRouteGroup(segment: string): boolean {\n return segment.startsWith(\"(\") && segment.endsWith(\")\");\n}\n\n/**\n * Check if a path segment is an optional catch-all (e.g., [[...slug]])\n * These segments match the base path without any additional segments\n */\nfunction isOptionalCatchAll(segment: string): boolean {\n return segment.startsWith(\"[[...\") && segment.endsWith(\"]]\");\n}\n\n/**\n * Check if a path segment is a required catch-all (e.g., [...slug])\n * These segments require at least one additional path segment\n */\nfunction isRequiredCatchAll(segment: string): boolean {\n return segment.startsWith(\"[...\") && segment.endsWith(\"]\") && !segment.startsWith(\"[[\");\n}\n\n/**\n * Check if a path segment is a dynamic segment (e.g., [id])\n */\nfunction isDynamicSegment(segment: string): boolean {\n return (\n segment.startsWith(\"[\") &&\n segment.endsWith(\"]\") &&\n !isOptionalCatchAll(segment) &&\n !isRequiredCatchAll(segment)\n );\n}\n\n/**\n * Normalize a dynamic segment for display\n * e.g., [id] \u2192 :id, [postId] \u2192 :postId\n */\nfunction normalizeDynamicSegment(segment: string): string {\n // Extract the parameter name from [name] format\n const paramName = segment.slice(1, -1);\n return `:${paramName}`;\n}\n\n/**\n * Normalize a route path for display (excludes route groups and handles dynamic segments)\n */\nfunction normalizeRoutePath(segments: string[]): string {\n // Filter out route groups - they don't appear in URLs\n // Filter out optional catch-all segments - they match the base path\n const urlSegments = segments\n .filter((seg) => !isRouteGroup(seg) && !isOptionalCatchAll(seg))\n .map((seg) => {\n if (isDynamicSegment(seg)) {\n return normalizeDynamicSegment(seg);\n }\n if (isRequiredCatchAll(seg)) {\n // For required catch-all, extract the name: [...slug] \u2192 :slug+\n const paramName = seg.slice(4, -1);\n return `:${paramName}+`;\n }\n return seg;\n });\n if (urlSegments.length === 0) return \"/\";\n return `/${urlSegments.join(\"/\")}`;\n}\n\n/**\n * Check if file is a page file\n */\nfunction isPageFile(filename: string): boolean {\n return filename === \"page.tsx\" || filename === \"page.js\";\n}\n\n/**\n * Recursively scan a directory for Next.js page routes\n */\nfunction scanAppDirectory(dirPath: string, segments: string[] = []): RouteInfo[] {\n const routes: RouteInfo[] = [];\n\n try {\n const entries = fs.readdirSync(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dirPath, entry.name);\n\n if (entry.isDirectory()) {\n // Skip special Next.js directories and api routes\n if (entry.name.startsWith(\"_\") || entry.name === \"api\") {\n continue;\n }\n\n // Recursively scan subdirectories\n routes.push(...scanAppDirectory(fullPath, [...segments, entry.name]));\n } else if (entry.isFile() && isPageFile(entry.name)) {\n const metadata = extractMetadata(fullPath);\n\n routes.push({\n path: normalizeRoutePath(segments),\n title: metadata.title,\n description: metadata.description,\n });\n }\n }\n } catch (error) {\n console.error(`Error scanning directory ${dirPath}:`, error);\n }\n\n return routes;\n}\n\n/**\n * File discovery helpers for legacy `pages/` directories\n */\nconst VALID_PAGE_EXTENSIONS = new Set([\".js\", \".jsx\", \".ts\", \".tsx\", \".md\", \".mdx\"]);\nconst SPECIAL_PAGE_FILENAMES = new Set([\n \"_app\",\n \"_document\",\n \"_error\",\n \"404\",\n \"500\",\n \"middleware\",\n \"_middleware\",\n]);\n\nfunction scanPagesDirectory(dirPath: string, segments: string[] = []): RouteInfo[] {\n const routes: RouteInfo[] = [];\n\n try {\n const entries = fs.readdirSync(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dirPath, entry.name);\n\n if (entry.isDirectory()) {\n // Skip special directories and api routes\n if (entry.name.startsWith(\"_\") || entry.name === \"api\") {\n continue;\n }\n\n routes.push(...scanPagesDirectory(fullPath, [...segments, entry.name]));\n } else if (entry.isFile()) {\n const extension = path.extname(entry.name);\n if (!VALID_PAGE_EXTENSIONS.has(extension)) {\n continue;\n }\n\n const baseName = entry.name.slice(0, -extension.length);\n if (!baseName || SPECIAL_PAGE_FILENAMES.has(baseName) || baseName.startsWith(\"_\")) {\n continue;\n }\n\n const routeSegments = buildPagesSegments(segments, baseName);\n const metadata = extractMetadata(fullPath);\n\n routes.push({\n path: normalizeRoutePath(routeSegments),\n title: metadata.title,\n description: metadata.description,\n });\n }\n }\n } catch (error) {\n console.error(`Error scanning directory ${dirPath}:`, error);\n }\n\n return routes;\n}\n\nfunction buildPagesSegments(segments: string[], baseName: string): string[] {\n const routeSegments = [...segments];\n if (baseName !== \"index\") {\n routeSegments.push(baseName);\n }\n return routeSegments;\n}\n\ntype NormalizedScanOptions = {\n directoryType: \"app\" | \"pages\";\n};\n\nfunction normalizeScanOptions(options?: ScanRoutesOptions): NormalizedScanOptions {\n return {\n directoryType: options?.directoryType ?? \"app\",\n };\n}\n\n/**\n * Scan a Next.js app or pages directory and return page route information\n */\nexport function scanRoutes(directory: string, options?: ScanRoutesOptions): RouteInfo[] {\n const normalizedOptions = normalizeScanOptions(options);\n const targetDir = path.resolve(process.cwd(), directory);\n\n if (!fs.existsSync(targetDir)) {\n throw new Error(`App directory not found: ${targetDir}`);\n }\n\n const routes =\n normalizedOptions.directoryType === \"pages\"\n ? scanPagesDirectory(targetDir, [])\n : scanAppDirectory(targetDir, []);\n\n return routes.sort((a: RouteInfo, b: RouteInfo) => a.path.localeCompare(b.path));\n}\n", "import type { RouteInfo, RouteSource } from \"@yak-io/javascript/server\";\n\n/**\n * Configuration for creating a route manifest adapter\n */\nexport type RouteManifestAdapterConfig = {\n /**\n * The routes from the generated manifest.\n * Import from the generated file: `import { routes } from \"@/yak.routes\"`\n */\n routes: RouteInfo[];\n\n /**\n * Optional ID for this route source (default: \"manifest\")\n */\n id?: string;\n\n /**\n * List of allowed route paths, e.g. [\"/\", \"/docs\", \"/pricing\"].\n * Supports exact matches and prefix patterns ending with `*`.\n * If provided, only these routes will be included.\n * If omitted, all routes are allowed by default.\n *\n * @example\n * ```ts\n * allowedRoutes: [\"/docs/*\", \"/pricing\", \"/\"]\n * ```\n */\n allowedRoutes?: string[];\n\n /**\n * List of disallowed route paths, e.g. [\"/admin/*\", \"/internal/*\"].\n * Supports exact matches and prefix patterns ending with `*`.\n * These routes will be excluded even if they would otherwise be allowed.\n * Applied after allowedRoutes filtering.\n *\n * @example\n * ```ts\n * disallowedRoutes: [\"/admin/*\", \"/settings/billing\"]\n * ```\n */\n disallowedRoutes?: string[];\n};\n\n/**\n * Check if a route path matches a pattern.\n * Supports exact matches and prefix patterns ending with `*`.\n */\nfunction matchesPattern(pattern: string, routePath: string): boolean {\n if (pattern.endsWith(\"/*\")) {\n const prefix = pattern.slice(0, -1); // Remove the *\n return routePath === prefix.slice(0, -1) || routePath.startsWith(prefix);\n }\n return pattern === routePath;\n}\n\n/**\n * Filter routes based on allowed and disallowed patterns.\n */\nfunction filterRoutes(\n routes: RouteInfo[],\n allowedRoutes?: string[],\n disallowedRoutes?: string[]\n): RouteInfo[] {\n return routes.filter((route) => {\n const path = route.path;\n\n // If allowedRoutes is set, route must match at least one pattern\n if (allowedRoutes && allowedRoutes.length > 0) {\n const isAllowed = allowedRoutes.some((pattern) => matchesPattern(pattern, path));\n if (!isAllowed) {\n return false;\n }\n }\n\n // If disallowedRoutes is set, route must not match any pattern\n if (disallowedRoutes && disallowedRoutes.length > 0) {\n const isDisallowed = disallowedRoutes.some((pattern) => matchesPattern(pattern, path));\n if (isDisallowed) {\n return false;\n }\n }\n\n return true;\n });\n}\n\n/**\n * Create a route source adapter from an imported route manifest.\n *\n * This adapter allows you to use generated route manifests with optional\n * filtering to control which routes are exposed to the chatbot.\n *\n * @example Basic usage - all routes\n * ```ts\n * import { createNextYakHandler } from \"@yak-io/nextjs/server\";\n * import { createRouteManifestAdapter } from \"@yak-io/nextjs/server\";\n * import { routes } from \"@/yak.routes\";\n *\n * export const { GET, POST } = createNextYakHandler({\n * routes: createRouteManifestAdapter({ routes }),\n * });\n * ```\n *\n * @example With filtering\n * ```ts\n * import { createNextYakHandler } from \"@yak-io/nextjs/server\";\n * import { createRouteManifestAdapter } from \"@yak-io/nextjs/server\";\n * import { routes } from \"@/yak.routes\";\n *\n * export const { GET, POST } = createNextYakHandler({\n * routes: createRouteManifestAdapter({\n * routes,\n * allowedRoutes: [\"/docs/*\", \"/pricing\", \"/\"],\n * disallowedRoutes: [\"/docs/internal/*\"],\n * }),\n * });\n * ```\n */\nexport function createRouteManifestAdapter(config: RouteManifestAdapterConfig): RouteSource {\n const { routes, id = \"manifest\", allowedRoutes, disallowedRoutes } = config;\n\n return {\n id,\n getRoutes: async () => {\n return filterRoutes(routes, allowedRoutes, disallowedRoutes);\n },\n };\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type {\n RouteInfo,\n RouteManifest,\n RouteSourceInput,\n ToolExecutor,\n ToolManifest,\n ToolSourceInput,\n} from \"@yak-io/javascript/server\";\nimport {\n createYakConfigHandler,\n createYakHandler,\n createYakToolsHandler,\n} from \"@yak-io/javascript/server\";\nimport { scanRoutes } from \"./scan-routes.js\";\n\n/**\n * Default paths to check for pre-built route manifests (JSON files).\n * These are read via fs.readFileSync at runtime.\n *\n * Priority:\n * 1. ./src/yak-routes-manifest.json - Default output, bundled by Next.js file tracing\n * 2. ./yak-routes-manifest.json - Root fallback\n * 3. ./public/yak-routes-manifest.json - Legacy location (doesn't work on Vercel serverless)\n */\nconst DEFAULT_MANIFEST_PATHS = [\n \"./src/yak-routes-manifest.json\",\n \"./yak-routes-manifest.json\",\n \"./public/yak-routes-manifest.json\",\n] as const;\n\n/**\n * Load a pre-built route manifest from disk (JSON file).\n *\n * This works in traditional Node.js deployments where the filesystem is available.\n * For Vercel serverless, the manifest must be imported directly in your route file.\n *\n * Generate the manifest at build time using:\n * yak-nextjs generate-manifest\n *\n * @param manifestPath - Path to the manifest JSON file (default: tries common locations)\n * @returns Route manifest or null if not found\n */\nexport function loadRouteManifest(manifestPath?: string): RouteManifest | null {\n const pathsToTry = manifestPath ? [manifestPath] : [...DEFAULT_MANIFEST_PATHS];\n\n for (const relativePath of pathsToTry) {\n try {\n const fullPath = path.resolve(process.cwd(), relativePath);\n if (fs.existsSync(fullPath)) {\n const content = fs.readFileSync(fullPath, \"utf-8\");\n return JSON.parse(content) as RouteManifest;\n }\n } catch {\n // Continue to next path\n }\n }\n\n return null;\n}\n\n/**\n * Load routes from a pre-built manifest.\n * Throws if manifest is not found.\n */\nexport function loadRoutes(manifestPath?: string): RouteInfo[] {\n const manifest = loadRouteManifest(manifestPath);\n if (!manifest) {\n const pathsChecked = manifestPath ? manifestPath : [...DEFAULT_MANIFEST_PATHS].join(\", \");\n throw new Error(\n `Route manifest not found. Checked: ${pathsChecked}\\n\\n` +\n \"In production environments (like Vercel), route scanning requires a pre-built manifest.\\n\" +\n \"Generate it at build time by adding to your build script:\\n\\n\" +\n \" yak-nextjs generate-manifest\\n\\n\" +\n \"Or provide routes explicitly in your handler configuration.\"\n );\n }\n return manifest.routes;\n}\n\nexport type NextYakRouteFilter = {\n include?: RegExp[];\n exclude?: RegExp[];\n};\n\nexport type NextYakHandlerConfig = {\n appDir?: string;\n routeFilter?: NextYakRouteFilter;\n routes?: RouteSourceInput;\n getRoutes?: () => Promise<RouteInfo[]>;\n tools?: ToolSourceInput;\n pagesDir?: string | false;\n getTools?: () => Promise<ToolManifest>;\n executeTool?: ToolExecutor;\n};\n\n/**\n * Create a unified Next.js App Router handler backed by the core primitives\n */\nexport function createNextYakHandler(config: NextYakHandlerConfig) {\n return createYakHandler({\n routes: resolveRouteSources(config),\n tools: resolveToolSources(config),\n });\n}\n\n/**\n * Attempt to load routes from filesystem or fetch from public URL.\n *\n * In development: scans the app directory directly\n * In production (Vercel, etc.): falls back to pre-built manifest via filesystem or HTTP fetch\n */\nasync function tryLoadRoutes(appDir: string): Promise<RouteInfo[]> {\n const targetDir = path.resolve(process.cwd(), appDir);\n\n // If the app directory exists, scan it directly (development mode)\n if (fs.existsSync(targetDir)) {\n return scanRoutes(appDir);\n }\n\n // In production, the source directory doesn't exist - try loading from manifest file\n const manifest = loadRouteManifest();\n if (manifest) {\n return manifest.routes;\n }\n\n // Neither source nor manifest available - provide helpful error\n throw new Error(\n \"Route manifest not found.\\n\\n\" +\n \"Generate the manifest at build time by adding to package.json:\\n\" +\n ` \"prebuild\": \"yak-nextjs generate-manifest\"\\n\\n` +\n \"The manifest will be created at ./src/yak-routes-manifest.json and automatically\\n\" +\n \"bundled with your serverless function.\\n\\n\" +\n \"Alternatively, provide routes explicitly:\\n\" +\n ` createNextYakHandler({ routes: [{ path: \"/\", title: \"Home\" }] })`\n );\n}\n\nfunction resolveRouteSources(config: NextYakHandlerConfig): RouteSourceInput {\n if (config.routes) {\n return config.routes;\n }\n if (config.getRoutes) {\n return config.getRoutes;\n }\n return async () => {\n const routes = await tryLoadRoutes(config.appDir ?? \"./src/app\");\n return applyRouteFilters(routes, config.routeFilter);\n };\n}\n\nfunction applyRouteFilters(routes: RouteInfo[], filter?: NextYakRouteFilter): RouteInfo[] {\n if (!filter) {\n return routes;\n }\n\n const { include = [], exclude = [] } = filter;\n\n return routes.filter((route) => {\n const path = route.path;\n\n if (include.length > 0 && !include.some((pattern) => matches(pattern, path))) {\n return false;\n }\n\n if (exclude.some((pattern) => matches(pattern, path))) {\n return false;\n }\n\n return true;\n });\n}\n\nfunction matches(pattern: RegExp, value: string): boolean {\n pattern.lastIndex = 0;\n return pattern.test(value);\n}\n\nfunction resolveToolSources(config: NextYakHandlerConfig): ToolSourceInput | undefined {\n if (config.tools) {\n return config.tools;\n }\n if (config.getTools) {\n return {\n getTools: config.getTools,\n executeTool: config.executeTool,\n } satisfies ToolSourceInput;\n }\n if (config.executeTool) {\n throw new Error(\"'executeTool' was provided without a matching tool manifest source\");\n }\n return undefined;\n}\n\nexport type NextYakConfigHandlerConfig = {\n appDir?: string;\n routeFilter?: NextYakRouteFilter;\n routes?: RouteSourceInput;\n getRoutes?: () => Promise<RouteInfo[]>;\n tools?: ToolSourceInput;\n getTools?: () => Promise<ToolManifest>;\n};\n\nexport function createNextYakConfigHandler(config: NextYakConfigHandlerConfig) {\n return createYakConfigHandler({\n routes: resolveRouteSources(config as NextYakHandlerConfig),\n tools: config.tools ?? (config.getTools ? { getTools: config.getTools } : undefined),\n });\n}\n\nexport type NextYakToolsHandlerConfig = {\n tools?: ToolSourceInput;\n getTools?: () => Promise<ToolManifest>;\n executeTool: ToolExecutor;\n};\n\nexport function createNextYakToolsHandler(config: NextYakToolsHandlerConfig) {\n if (!config.tools && !config.getTools) {\n throw new Error(\"createNextYakToolsHandler requires either 'tools' or 'getTools'\");\n }\n\n const toolSource: ToolSourceInput = config.tools ?? {\n // getTools is guaranteed to be defined here since we check above\n getTools: config.getTools as NonNullable<typeof config.getTools>,\n executeTool: config.executeTool,\n };\n\n return createYakToolsHandler({ tools: toolSource });\n}\n", "import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { RouteInfo } from \"@yak-io/javascript/server\";\n\nexport type ScanRoutesOptions = {\n directoryType?: \"app\" | \"pages\";\n};\n\n/**\n * Extract static metadata (title and description) from a Next.js page file.\n * Parses `export const metadata = { title: \"...\", description: \"...\" }` patterns.\n * Does not support dynamic generateMetadata functions.\n */\nfunction extractMetadata(filePath: string): { title?: string; description?: string } {\n try {\n const content = fs.readFileSync(filePath, \"utf-8\");\n\n // Match `export const metadata` object\n const metadataMatch = content.match(\n /export\\s+const\\s+metadata\\s*(?::\\s*Metadata\\s*)?=\\s*\\{([^}]*(?:\\{[^}]*\\}[^}]*)*)\\}/s\n );\n\n if (!metadataMatch) {\n return {};\n }\n\n const metadataBlock = metadataMatch[1];\n const result: { title?: string; description?: string } = {};\n\n // Extract title - handle both single and double quotes\n const titleMatch = metadataBlock?.match(/title\\s*:\\s*[\"'`]([^\"'`]+)[\"'`]/);\n if (titleMatch?.[1]) {\n result.title = titleMatch[1];\n }\n\n // Extract description - handle both single and double quotes\n const descMatch = metadataBlock?.match(/description\\s*:\\s*[\"'`]([^\"'`]+)[\"'`]/);\n if (descMatch?.[1]) {\n result.description = descMatch[1];\n }\n\n return result;\n } catch {\n return {};\n }\n}\n\n/**\n * Check if a path segment is a route group (organizational only, not part of URL)\n */\nfunction isRouteGroup(segment: string): boolean {\n return segment.startsWith(\"(\") && segment.endsWith(\")\");\n}\n\n/**\n * Check if a path segment is an optional catch-all (e.g., [[...slug]])\n * These segments match the base path without any additional segments\n */\nfunction isOptionalCatchAll(segment: string): boolean {\n return segment.startsWith(\"[[...\") && segment.endsWith(\"]]\");\n}\n\n/**\n * Check if a path segment is a required catch-all (e.g., [...slug])\n * These segments require at least one additional path segment\n */\nfunction isRequiredCatchAll(segment: string): boolean {\n return segment.startsWith(\"[...\") && segment.endsWith(\"]\") && !segment.startsWith(\"[[\");\n}\n\n/**\n * Check if a path segment is a dynamic segment (e.g., [id])\n */\nfunction isDynamicSegment(segment: string): boolean {\n return (\n segment.startsWith(\"[\") &&\n segment.endsWith(\"]\") &&\n !isOptionalCatchAll(segment) &&\n !isRequiredCatchAll(segment)\n );\n}\n\n/**\n * Normalize a dynamic segment for display\n * e.g., [id] \u2192 :id, [postId] \u2192 :postId\n */\nfunction normalizeDynamicSegment(segment: string): string {\n // Extract the parameter name from [name] format\n const paramName = segment.slice(1, -1);\n return `:${paramName}`;\n}\n\n/**\n * Normalize a route path for display (excludes route groups and handles dynamic segments)\n */\nfunction normalizeRoutePath(segments: string[]): string {\n // Filter out route groups - they don't appear in URLs\n // Filter out optional catch-all segments - they match the base path\n const urlSegments = segments\n .filter((seg) => !isRouteGroup(seg) && !isOptionalCatchAll(seg))\n .map((seg) => {\n if (isDynamicSegment(seg)) {\n return normalizeDynamicSegment(seg);\n }\n if (isRequiredCatchAll(seg)) {\n // For required catch-all, extract the name: [...slug] \u2192 :slug+\n const paramName = seg.slice(4, -1);\n return `:${paramName}+`;\n }\n return seg;\n });\n if (urlSegments.length === 0) return \"/\";\n return `/${urlSegments.join(\"/\")}`;\n}\n\n/**\n * Check if file is a page file\n */\nfunction isPageFile(filename: string): boolean {\n return filename === \"page.tsx\" || filename === \"page.js\";\n}\n\n/**\n * Recursively scan a directory for Next.js page routes\n */\nfunction scanAppDirectory(dirPath: string, segments: string[] = []): RouteInfo[] {\n const routes: RouteInfo[] = [];\n\n try {\n const entries = fs.readdirSync(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dirPath, entry.name);\n\n if (entry.isDirectory()) {\n // Skip special Next.js directories and api routes\n if (entry.name.startsWith(\"_\") || entry.name === \"api\") {\n continue;\n }\n\n // Recursively scan subdirectories\n routes.push(...scanAppDirectory(fullPath, [...segments, entry.name]));\n } else if (entry.isFile() && isPageFile(entry.name)) {\n const metadata = extractMetadata(fullPath);\n\n routes.push({\n path: normalizeRoutePath(segments),\n title: metadata.title,\n description: metadata.description,\n });\n }\n }\n } catch (error) {\n console.error(`Error scanning directory ${dirPath}:`, error);\n }\n\n return routes;\n}\n\n/**\n * File discovery helpers for legacy `pages/` directories\n */\nconst VALID_PAGE_EXTENSIONS = new Set([\".js\", \".jsx\", \".ts\", \".tsx\", \".md\", \".mdx\"]);\nconst SPECIAL_PAGE_FILENAMES = new Set([\n \"_app\",\n \"_document\",\n \"_error\",\n \"404\",\n \"500\",\n \"middleware\",\n \"_middleware\",\n]);\n\nfunction scanPagesDirectory(dirPath: string, segments: string[] = []): RouteInfo[] {\n const routes: RouteInfo[] = [];\n\n try {\n const entries = fs.readdirSync(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dirPath, entry.name);\n\n if (entry.isDirectory()) {\n // Skip special directories and api routes\n if (entry.name.startsWith(\"_\") || entry.name === \"api\") {\n continue;\n }\n\n routes.push(...scanPagesDirectory(fullPath, [...segments, entry.name]));\n } else if (entry.isFile()) {\n const extension = path.extname(entry.name);\n if (!VALID_PAGE_EXTENSIONS.has(extension)) {\n continue;\n }\n\n const baseName = entry.name.slice(0, -extension.length);\n if (!baseName || SPECIAL_PAGE_FILENAMES.has(baseName) || baseName.startsWith(\"_\")) {\n continue;\n }\n\n const routeSegments = buildPagesSegments(segments, baseName);\n const metadata = extractMetadata(fullPath);\n\n routes.push({\n path: normalizeRoutePath(routeSegments),\n title: metadata.title,\n description: metadata.description,\n });\n }\n }\n } catch (error) {\n console.error(`Error scanning directory ${dirPath}:`, error);\n }\n\n return routes;\n}\n\nfunction buildPagesSegments(segments: string[], baseName: string): string[] {\n const routeSegments = [...segments];\n if (baseName !== \"index\") {\n routeSegments.push(baseName);\n }\n return routeSegments;\n}\n\ntype NormalizedScanOptions = {\n directoryType: \"app\" | \"pages\";\n};\n\nfunction normalizeScanOptions(options?: ScanRoutesOptions): NormalizedScanOptions {\n return {\n directoryType: options?.directoryType ?? \"app\",\n };\n}\n\n/**\n * Scan a Next.js app or pages directory and return page route information\n */\nexport function scanRoutes(directory: string, options?: ScanRoutesOptions): RouteInfo[] {\n const normalizedOptions = normalizeScanOptions(options);\n const targetDir = path.resolve(process.cwd(), directory);\n\n if (!fs.existsSync(targetDir)) {\n throw new Error(`App directory not found: ${targetDir}`);\n }\n\n const routes =\n normalizedOptions.directoryType === \"pages\"\n ? scanPagesDirectory(targetDir, [])\n : scanAppDirectory(targetDir, []);\n\n return routes.sort((a: RouteInfo, b: RouteInfo) => a.path.localeCompare(b.path));\n}\n", "import type { RouteInfo, RouteSource } from \"@yak-io/javascript/server\";\n\n/**\n * Configuration for creating a route manifest adapter\n */\nexport type RouteManifestAdapterConfig = {\n /**\n * The routes from the generated manifest.\n * Import from the generated file: `import { routes } from \"@/yak.routes\"`\n */\n routes: RouteInfo[];\n\n /**\n * Optional ID for this route source (default: \"manifest\")\n */\n id?: string;\n\n /**\n * List of allowed route paths, e.g. [\"/\", \"/docs\", \"/pricing\"].\n * Supports exact matches and prefix patterns ending with `*`.\n * If provided, only these routes will be included.\n * If omitted, all routes are allowed by default.\n *\n * @example\n * ```ts\n * allowedRoutes: [\"/docs/*\", \"/pricing\", \"/\"]\n * ```\n */\n allowedRoutes?: string[];\n\n /**\n * List of disallowed route paths, e.g. [\"/admin/*\", \"/internal/*\"].\n * Supports exact matches and prefix patterns ending with `*`.\n * These routes will be excluded even if they would otherwise be allowed.\n * Applied after allowedRoutes filtering.\n *\n * @example\n * ```ts\n * disallowedRoutes: [\"/admin/*\", \"/settings/billing\"]\n * ```\n */\n disallowedRoutes?: string[];\n\n /**\n * Optional per-route enrichment, applied after filtering. Use it to annotate\n * auto-scanned routes with capabilities the scanner can't infer \u2014 most often\n * marking a route as searchable or declaring the filters it accepts so the\n * assistant can drive your UI (e.g. \"size 10 boots\" \u2192 `/products?size=10`)\n * instead of rendering results in the chat.\n *\n * Return the route unchanged to leave it as-is.\n *\n * @example\n * ```ts\n * createRouteManifestAdapter({\n * routes,\n * transform: (route) =>\n * route.path === \"/products\"\n * ? {\n * ...route,\n * search: { queryParam: \"q\" },\n * filters: [\n * { param: \"category\", description: \"Product category\" },\n * { param: \"size\", values: [\"8\", \"9\", \"10\", \"11\"] },\n * ],\n * }\n * : route,\n * });\n * ```\n */\n transform?: (route: RouteInfo) => RouteInfo;\n};\n\n/**\n * Check if a route path matches a pattern.\n * Supports exact matches and prefix patterns ending with `*`.\n */\nfunction matchesPattern(pattern: string, routePath: string): boolean {\n if (pattern.endsWith(\"/*\")) {\n const prefix = pattern.slice(0, -1); // Remove the *\n return routePath === prefix.slice(0, -1) || routePath.startsWith(prefix);\n }\n return pattern === routePath;\n}\n\n/**\n * Filter routes based on allowed and disallowed patterns.\n */\nfunction filterRoutes(\n routes: RouteInfo[],\n allowedRoutes?: string[],\n disallowedRoutes?: string[]\n): RouteInfo[] {\n return routes.filter((route) => {\n const path = route.path;\n\n // If allowedRoutes is set, route must match at least one pattern\n if (allowedRoutes && allowedRoutes.length > 0) {\n const isAllowed = allowedRoutes.some((pattern) => matchesPattern(pattern, path));\n if (!isAllowed) {\n return false;\n }\n }\n\n // If disallowedRoutes is set, route must not match any pattern\n if (disallowedRoutes && disallowedRoutes.length > 0) {\n const isDisallowed = disallowedRoutes.some((pattern) => matchesPattern(pattern, path));\n if (isDisallowed) {\n return false;\n }\n }\n\n return true;\n });\n}\n\n/**\n * Create a route source adapter from an imported route manifest.\n *\n * This adapter allows you to use generated route manifests with optional\n * filtering to control which routes are exposed to the chatbot.\n *\n * @example Basic usage - all routes\n * ```ts\n * import { createNextYakHandler } from \"@yak-io/nextjs/server\";\n * import { createRouteManifestAdapter } from \"@yak-io/nextjs/server\";\n * import { routes } from \"@/yak.routes\";\n *\n * export const { GET, POST } = createNextYakHandler({\n * routes: createRouteManifestAdapter({ routes }),\n * });\n * ```\n *\n * @example With filtering\n * ```ts\n * import { createNextYakHandler } from \"@yak-io/nextjs/server\";\n * import { createRouteManifestAdapter } from \"@yak-io/nextjs/server\";\n * import { routes } from \"@/yak.routes\";\n *\n * export const { GET, POST } = createNextYakHandler({\n * routes: createRouteManifestAdapter({\n * routes,\n * allowedRoutes: [\"/docs/*\", \"/pricing\", \"/\"],\n * disallowedRoutes: [\"/docs/internal/*\"],\n * }),\n * });\n * ```\n */\nexport function createRouteManifestAdapter(config: RouteManifestAdapterConfig): RouteSource {\n const { routes, id = \"manifest\", allowedRoutes, disallowedRoutes, transform } = config;\n\n return {\n id,\n getRoutes: async () => {\n const filtered = filterRoutes(routes, allowedRoutes, disallowedRoutes);\n return transform ? filtered.map(transform) : filtered;\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["fs", "path", "path", "path"]
|
|
7
7
|
}
|
|
@@ -36,6 +36,34 @@ export type RouteManifestAdapterConfig = {
|
|
|
36
36
|
* ```
|
|
37
37
|
*/
|
|
38
38
|
disallowedRoutes?: string[];
|
|
39
|
+
/**
|
|
40
|
+
* Optional per-route enrichment, applied after filtering. Use it to annotate
|
|
41
|
+
* auto-scanned routes with capabilities the scanner can't infer — most often
|
|
42
|
+
* marking a route as searchable or declaring the filters it accepts so the
|
|
43
|
+
* assistant can drive your UI (e.g. "size 10 boots" → `/products?size=10`)
|
|
44
|
+
* instead of rendering results in the chat.
|
|
45
|
+
*
|
|
46
|
+
* Return the route unchanged to leave it as-is.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* createRouteManifestAdapter({
|
|
51
|
+
* routes,
|
|
52
|
+
* transform: (route) =>
|
|
53
|
+
* route.path === "/products"
|
|
54
|
+
* ? {
|
|
55
|
+
* ...route,
|
|
56
|
+
* search: { queryParam: "q" },
|
|
57
|
+
* filters: [
|
|
58
|
+
* { param: "category", description: "Product category" },
|
|
59
|
+
* { param: "size", values: ["8", "9", "10", "11"] },
|
|
60
|
+
* ],
|
|
61
|
+
* }
|
|
62
|
+
* : route,
|
|
63
|
+
* });
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
transform?: (route: RouteInfo) => RouteInfo;
|
|
39
67
|
};
|
|
40
68
|
/**
|
|
41
69
|
* Create a route source adapter from an imported route manifest.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-manifest-adapter.d.ts","sourceRoot":"","sources":["../../src/server/route-manifest-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAExE;;GAEG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC;;;OAGG;IACH,MAAM,EAAE,SAAS,EAAE,CAAC;IAEpB;;OAEG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IAEzB;;;;;;;;;;OAUG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"route-manifest-adapter.d.ts","sourceRoot":"","sources":["../../src/server/route-manifest-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAExE;;GAEG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC;;;OAGG;IACH,MAAM,EAAE,SAAS,EAAE,CAAC;IAEpB;;OAEG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IAEzB;;;;;;;;;;OAUG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE5B;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,SAAS,CAAC;CAC7C,CAAC;AA6CF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,0BAA0B,GAAG,WAAW,CAU1F"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yak-io/nextjs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Next.js SDK for embedding yak chatbot with route manifest generation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"files": [
|
|
29
29
|
"README.md",
|
|
30
30
|
"dist",
|
|
31
|
+
"bin",
|
|
31
32
|
"LICENSE"
|
|
32
33
|
],
|
|
33
34
|
"sideEffects": false,
|
|
@@ -60,11 +61,11 @@
|
|
|
60
61
|
}
|
|
61
62
|
},
|
|
62
63
|
"bin": {
|
|
63
|
-
"yak-nextjs": "./
|
|
64
|
+
"yak-nextjs": "./bin/yak-nextjs.mjs"
|
|
64
65
|
},
|
|
65
66
|
"dependencies": {
|
|
66
|
-
"@yak-io/javascript": "0.
|
|
67
|
-
"@yak-io/react": "0.12.
|
|
67
|
+
"@yak-io/javascript": "0.12.0",
|
|
68
|
+
"@yak-io/react": "0.12.3"
|
|
68
69
|
},
|
|
69
70
|
"peerDependencies": {
|
|
70
71
|
"next": "^14.0.0 || ^15.0.0 || ^16.0.0",
|