@yak-io/nextjs 0.7.0 → 0.8.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/cli/generate-manifest.cjs +266 -0
- package/dist/cli/generate-manifest.cjs.map +7 -0
- package/dist/cli/generate-manifest.js +169 -259
- package/dist/cli/generate-manifest.js.map +7 -0
- package/dist/index.client.cjs +86 -0
- package/dist/index.client.cjs.map +7 -0
- package/dist/index.client.js +65 -2
- package/dist/index.client.js.map +7 -0
- package/dist/index.server.cjs +364 -0
- package/dist/index.server.cjs.map +7 -0
- package/dist/index.server.js +335 -2
- package/dist/index.server.js.map +7 -0
- package/package.json +7 -5
- package/dist/client/YakProvider.js +0 -57
- package/dist/client/useYak.js +0 -4
- package/dist/server/createNextYakConfigHandler.js +0 -1
- package/dist/server/createNextYakHandler.js +0 -161
- package/dist/server/createNextYakToolsHandler.js +0 -1
- package/dist/server/index.js +0 -3
- package/dist/server/route-manifest-adapter.js +0 -75
- package/dist/server/scan-routes.js +0 -208
- package/dist/types/config.js +0 -1
- package/dist/types/messaging.js +0 -1
- package/dist/types/routes.js +0 -1
- package/dist/types/tools.js +0 -1
package/dist/index.client.js
CHANGED
|
@@ -1,4 +1,67 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
|
|
3
|
+
// src/index.client.ts
|
|
2
4
|
export * from "@yak-io/react";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
+
|
|
6
|
+
// src/client/useYak.ts
|
|
7
|
+
import { useYak } from "@yak-io/react";
|
|
8
|
+
|
|
9
|
+
// src/client/YakProvider.tsx
|
|
10
|
+
import {
|
|
11
|
+
YakProvider as CoreYakProvider
|
|
12
|
+
} from "@yak-io/react";
|
|
13
|
+
import { useRouter } from "next/navigation";
|
|
14
|
+
import { useCallback, useMemo } from "react";
|
|
15
|
+
import { jsx } from "react/jsx-runtime";
|
|
16
|
+
function isAbsoluteUrl(path) {
|
|
17
|
+
return /^https?:\/\//i.test(path);
|
|
18
|
+
}
|
|
19
|
+
async function defaultGetConfig() {
|
|
20
|
+
const res = await fetch("/api/yak", { credentials: "include" });
|
|
21
|
+
if (!res.ok) {
|
|
22
|
+
throw new Error(`Failed to fetch config: ${res.status}`);
|
|
23
|
+
}
|
|
24
|
+
return res.json();
|
|
25
|
+
}
|
|
26
|
+
async function defaultOnToolCall(name, args) {
|
|
27
|
+
const res = await fetch("/api/yak", {
|
|
28
|
+
method: "POST",
|
|
29
|
+
headers: { "Content-Type": "application/json" },
|
|
30
|
+
credentials: "include",
|
|
31
|
+
body: JSON.stringify({ name, args })
|
|
32
|
+
});
|
|
33
|
+
const data = await res.json();
|
|
34
|
+
if (!data.ok) {
|
|
35
|
+
throw new Error(data.error ?? "Tool execution failed");
|
|
36
|
+
}
|
|
37
|
+
return data.result;
|
|
38
|
+
}
|
|
39
|
+
function YakProvider(props) {
|
|
40
|
+
const router = useRouter();
|
|
41
|
+
const handleRedirect = useCallback(
|
|
42
|
+
(path) => {
|
|
43
|
+
if (isAbsoluteUrl(path)) {
|
|
44
|
+
window.location.assign(path);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
router.push(path);
|
|
48
|
+
},
|
|
49
|
+
[router]
|
|
50
|
+
);
|
|
51
|
+
const getConfig = useMemo(() => props.getConfig ?? defaultGetConfig, [props.getConfig]);
|
|
52
|
+
const onToolCall = useMemo(() => props.onToolCall ?? defaultOnToolCall, [props.onToolCall]);
|
|
53
|
+
return /* @__PURE__ */ jsx(
|
|
54
|
+
CoreYakProvider,
|
|
55
|
+
{
|
|
56
|
+
...props,
|
|
57
|
+
getConfig,
|
|
58
|
+
onToolCall,
|
|
59
|
+
onRedirect: props.onRedirect ?? handleRedirect
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
export {
|
|
64
|
+
YakProvider,
|
|
65
|
+
useYak
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=index.client.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.client.ts", "../src/client/useYak.ts", "../src/client/YakProvider.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\";\n\nexport * from \"@yak-io/react\";\nexport type { YakChatAPI } from \"./client/useYak.js\";\nexport { useYak } from \"./client/useYak.js\";\nexport type { YakProviderProps } from \"./client/YakProvider.js\";\nexport { YakProvider } from \"./client/YakProvider.js\";\n", "\"use client\";\n\nimport { useYak } from \"@yak-io/react\";\n\n// Re-export useYak for convenience\nexport { useYak };\n\n/**\n * The object returned by {@link useYak} \u2014 the imperative handle for controlling\n * chat and voice (`open`, `close`, `openWithPrompt`, `voiceStart`, `voiceStop`,\n * `voiceToggle`) plus reactive state (`isOpen`, `isReady`, `chatLoading`,\n * `voiceState`, `voiceLoading`, \u2026).\n */\nexport type YakChatAPI = ReturnType<typeof useYak>;\n", "\"use client\";\n\nimport {\n YakProvider as CoreYakProvider,\n type YakProviderProps as CoreYakProviderProps,\n} from \"@yak-io/react\";\nimport { useRouter } from \"next/navigation\";\nimport type React from \"react\";\nimport { useCallback, useMemo } from \"react\";\n\nexport type YakProviderProps = CoreYakProviderProps;\n\nfunction isAbsoluteUrl(path: string): boolean {\n return /^https?:\\/\\//i.test(path);\n}\n\n/**\n * Default getConfig that fetches from /api/yak\n */\nasync function defaultGetConfig() {\n const res = await fetch(\"/api/yak\", { credentials: \"include\" });\n if (!res.ok) {\n throw new Error(`Failed to fetch config: ${res.status}`);\n }\n return res.json();\n}\n\n/**\n * Default onToolCall that POSTs to /api/yak\n */\nasync function defaultOnToolCall(name: string, args: unknown) {\n const res = await fetch(\"/api/yak\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: \"include\",\n body: JSON.stringify({ name, args }),\n });\n const data = await res.json();\n if (!data.ok) {\n throw new Error(data.error ?? \"Tool execution failed\");\n }\n return data.result;\n}\n\n/**\n * Next-aware YakProvider that falls back to client-side navigation\n * and provides sensible defaults for getConfig and onToolCall.\n *\n * By default:\n * - `getConfig` fetches from `/api/yak` (GET)\n * - `onToolCall` POSTs to `/api/yak`\n *\n * These defaults assume you've set up the API handler via `createNextYakHandler`.\n */\nexport function YakProvider(props: YakProviderProps): React.JSX.Element {\n const router = useRouter();\n\n const handleRedirect = useCallback(\n (path: string) => {\n if (isAbsoluteUrl(path)) {\n window.location.assign(path);\n return;\n }\n\n router.push(path);\n },\n [router]\n );\n\n const getConfig = useMemo(() => props.getConfig ?? defaultGetConfig, [props.getConfig]);\n\n const onToolCall = useMemo(() => props.onToolCall ?? defaultOnToolCall, [props.onToolCall]);\n\n return (\n <CoreYakProvider\n {...props}\n getConfig={getConfig}\n onToolCall={onToolCall}\n onRedirect={props.onRedirect ?? handleRedirect}\n />\n );\n}\n"],
|
|
5
|
+
"mappings": ";;;AAEA,cAAc;;;ACAd,SAAS,cAAc;;;ACAvB;AAAA,EACE,eAAe;AAAA,OAEV;AACP,SAAS,iBAAiB;AAE1B,SAAS,aAAa,eAAe;AAkEjC;AA9DJ,SAAS,cAAc,MAAuB;AAC5C,SAAO,gBAAgB,KAAK,IAAI;AAClC;AAKA,eAAe,mBAAmB;AAChC,QAAM,MAAM,MAAM,MAAM,YAAY,EAAE,aAAa,UAAU,CAAC;AAC9D,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,IAAI,MAAM,2BAA2B,IAAI,MAAM,EAAE;AAAA,EACzD;AACA,SAAO,IAAI,KAAK;AAClB;AAKA,eAAe,kBAAkB,MAAc,MAAe;AAC5D,QAAM,MAAM,MAAM,MAAM,YAAY;AAAA,IAClC,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,aAAa;AAAA,IACb,MAAM,KAAK,UAAU,EAAE,MAAM,KAAK,CAAC;AAAA,EACrC,CAAC;AACD,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,CAAC,KAAK,IAAI;AACZ,UAAM,IAAI,MAAM,KAAK,SAAS,uBAAuB;AAAA,EACvD;AACA,SAAO,KAAK;AACd;AAYO,SAAS,YAAY,OAA4C;AACtE,QAAM,SAAS,UAAU;AAEzB,QAAM,iBAAiB;AAAA,IACrB,CAAC,SAAiB;AAChB,UAAI,cAAc,IAAI,GAAG;AACvB,eAAO,SAAS,OAAO,IAAI;AAC3B;AAAA,MACF;AAEA,aAAO,KAAK,IAAI;AAAA,IAClB;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,QAAM,YAAY,QAAQ,MAAM,MAAM,aAAa,kBAAkB,CAAC,MAAM,SAAS,CAAC;AAEtF,QAAM,aAAa,QAAQ,MAAM,MAAM,cAAc,mBAAmB,CAAC,MAAM,UAAU,CAAC;AAE1F,SACE;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ;AAAA,MACA;AAAA,MACA,YAAY,MAAM,cAAc;AAAA;AAAA,EAClC;AAEJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.server.ts
|
|
31
|
+
var index_server_exports = {};
|
|
32
|
+
__export(index_server_exports, {
|
|
33
|
+
createNextYakConfigHandler: () => createNextYakConfigHandler,
|
|
34
|
+
createNextYakHandler: () => createNextYakHandler,
|
|
35
|
+
createNextYakToolsHandler: () => createNextYakToolsHandler,
|
|
36
|
+
createRouteManifestAdapter: () => createRouteManifestAdapter,
|
|
37
|
+
loadRouteManifest: () => loadRouteManifest,
|
|
38
|
+
loadRoutes: () => loadRoutes,
|
|
39
|
+
scanRoutes: () => scanRoutes
|
|
40
|
+
});
|
|
41
|
+
module.exports = __toCommonJS(index_server_exports);
|
|
42
|
+
|
|
43
|
+
// src/server/createNextYakHandler.ts
|
|
44
|
+
var fs2 = __toESM(require("node:fs"), 1);
|
|
45
|
+
var path2 = __toESM(require("node:path"), 1);
|
|
46
|
+
var import_server = require("@yak-io/javascript/server");
|
|
47
|
+
|
|
48
|
+
// src/server/scan-routes.ts
|
|
49
|
+
var fs = __toESM(require("node:fs"), 1);
|
|
50
|
+
var path = __toESM(require("node:path"), 1);
|
|
51
|
+
function extractMetadata(filePath) {
|
|
52
|
+
try {
|
|
53
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
54
|
+
const metadataMatch = content.match(
|
|
55
|
+
/export\s+const\s+metadata\s*(?::\s*Metadata\s*)?=\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}/s
|
|
56
|
+
);
|
|
57
|
+
if (!metadataMatch) {
|
|
58
|
+
return {};
|
|
59
|
+
}
|
|
60
|
+
const metadataBlock = metadataMatch[1];
|
|
61
|
+
const result = {};
|
|
62
|
+
const titleMatch = metadataBlock?.match(/title\s*:\s*["'`]([^"'`]+)["'`]/);
|
|
63
|
+
if (titleMatch?.[1]) {
|
|
64
|
+
result.title = titleMatch[1];
|
|
65
|
+
}
|
|
66
|
+
const descMatch = metadataBlock?.match(/description\s*:\s*["'`]([^"'`]+)["'`]/);
|
|
67
|
+
if (descMatch?.[1]) {
|
|
68
|
+
result.description = descMatch[1];
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
} catch {
|
|
72
|
+
return {};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function isRouteGroup(segment) {
|
|
76
|
+
return segment.startsWith("(") && segment.endsWith(")");
|
|
77
|
+
}
|
|
78
|
+
function isOptionalCatchAll(segment) {
|
|
79
|
+
return segment.startsWith("[[...") && segment.endsWith("]]");
|
|
80
|
+
}
|
|
81
|
+
function isRequiredCatchAll(segment) {
|
|
82
|
+
return segment.startsWith("[...") && segment.endsWith("]") && !segment.startsWith("[[");
|
|
83
|
+
}
|
|
84
|
+
function isDynamicSegment(segment) {
|
|
85
|
+
return segment.startsWith("[") && segment.endsWith("]") && !isOptionalCatchAll(segment) && !isRequiredCatchAll(segment);
|
|
86
|
+
}
|
|
87
|
+
function normalizeDynamicSegment(segment) {
|
|
88
|
+
const paramName = segment.slice(1, -1);
|
|
89
|
+
return `:${paramName}`;
|
|
90
|
+
}
|
|
91
|
+
function normalizeRoutePath(segments) {
|
|
92
|
+
const urlSegments = segments.filter((seg) => !isRouteGroup(seg) && !isOptionalCatchAll(seg)).map((seg) => {
|
|
93
|
+
if (isDynamicSegment(seg)) {
|
|
94
|
+
return normalizeDynamicSegment(seg);
|
|
95
|
+
}
|
|
96
|
+
if (isRequiredCatchAll(seg)) {
|
|
97
|
+
const paramName = seg.slice(4, -1);
|
|
98
|
+
return `:${paramName}+`;
|
|
99
|
+
}
|
|
100
|
+
return seg;
|
|
101
|
+
});
|
|
102
|
+
if (urlSegments.length === 0) return "/";
|
|
103
|
+
return `/${urlSegments.join("/")}`;
|
|
104
|
+
}
|
|
105
|
+
function isPageFile(filename) {
|
|
106
|
+
return filename === "page.tsx" || filename === "page.js";
|
|
107
|
+
}
|
|
108
|
+
function scanAppDirectory(dirPath, segments = []) {
|
|
109
|
+
const routes = [];
|
|
110
|
+
try {
|
|
111
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
112
|
+
for (const entry of entries) {
|
|
113
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
114
|
+
if (entry.isDirectory()) {
|
|
115
|
+
if (entry.name.startsWith("_") || entry.name === "api") {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
routes.push(...scanAppDirectory(fullPath, [...segments, entry.name]));
|
|
119
|
+
} else if (entry.isFile() && isPageFile(entry.name)) {
|
|
120
|
+
const metadata = extractMetadata(fullPath);
|
|
121
|
+
routes.push({
|
|
122
|
+
path: normalizeRoutePath(segments),
|
|
123
|
+
title: metadata.title,
|
|
124
|
+
description: metadata.description
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
} catch (error) {
|
|
129
|
+
console.error(`Error scanning directory ${dirPath}:`, error);
|
|
130
|
+
}
|
|
131
|
+
return routes;
|
|
132
|
+
}
|
|
133
|
+
var VALID_PAGE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".jsx", ".ts", ".tsx", ".md", ".mdx"]);
|
|
134
|
+
var SPECIAL_PAGE_FILENAMES = /* @__PURE__ */ new Set([
|
|
135
|
+
"_app",
|
|
136
|
+
"_document",
|
|
137
|
+
"_error",
|
|
138
|
+
"404",
|
|
139
|
+
"500",
|
|
140
|
+
"middleware",
|
|
141
|
+
"_middleware"
|
|
142
|
+
]);
|
|
143
|
+
function scanPagesDirectory(dirPath, segments = []) {
|
|
144
|
+
const routes = [];
|
|
145
|
+
try {
|
|
146
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
147
|
+
for (const entry of entries) {
|
|
148
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
149
|
+
if (entry.isDirectory()) {
|
|
150
|
+
if (entry.name.startsWith("_") || entry.name === "api") {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
routes.push(...scanPagesDirectory(fullPath, [...segments, entry.name]));
|
|
154
|
+
} else if (entry.isFile()) {
|
|
155
|
+
const extension = path.extname(entry.name);
|
|
156
|
+
if (!VALID_PAGE_EXTENSIONS.has(extension)) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const baseName = entry.name.slice(0, -extension.length);
|
|
160
|
+
if (!baseName || SPECIAL_PAGE_FILENAMES.has(baseName) || baseName.startsWith("_")) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const routeSegments = buildPagesSegments(segments, baseName);
|
|
164
|
+
const metadata = extractMetadata(fullPath);
|
|
165
|
+
routes.push({
|
|
166
|
+
path: normalizeRoutePath(routeSegments),
|
|
167
|
+
title: metadata.title,
|
|
168
|
+
description: metadata.description
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
} catch (error) {
|
|
173
|
+
console.error(`Error scanning directory ${dirPath}:`, error);
|
|
174
|
+
}
|
|
175
|
+
return routes;
|
|
176
|
+
}
|
|
177
|
+
function buildPagesSegments(segments, baseName) {
|
|
178
|
+
const routeSegments = [...segments];
|
|
179
|
+
if (baseName !== "index") {
|
|
180
|
+
routeSegments.push(baseName);
|
|
181
|
+
}
|
|
182
|
+
return routeSegments;
|
|
183
|
+
}
|
|
184
|
+
function normalizeScanOptions(options) {
|
|
185
|
+
return {
|
|
186
|
+
directoryType: options?.directoryType ?? "app"
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function scanRoutes(directory, options) {
|
|
190
|
+
const normalizedOptions = normalizeScanOptions(options);
|
|
191
|
+
const targetDir = path.resolve(process.cwd(), directory);
|
|
192
|
+
if (!fs.existsSync(targetDir)) {
|
|
193
|
+
throw new Error(`App directory not found: ${targetDir}`);
|
|
194
|
+
}
|
|
195
|
+
const routes = normalizedOptions.directoryType === "pages" ? scanPagesDirectory(targetDir, []) : scanAppDirectory(targetDir, []);
|
|
196
|
+
return routes.sort((a, b) => a.path.localeCompare(b.path));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// src/server/createNextYakHandler.ts
|
|
200
|
+
var DEFAULT_MANIFEST_PATHS = [
|
|
201
|
+
"./src/yak-routes-manifest.json",
|
|
202
|
+
"./yak-routes-manifest.json",
|
|
203
|
+
"./public/yak-routes-manifest.json"
|
|
204
|
+
];
|
|
205
|
+
function loadRouteManifest(manifestPath) {
|
|
206
|
+
const pathsToTry = manifestPath ? [manifestPath] : [...DEFAULT_MANIFEST_PATHS];
|
|
207
|
+
for (const relativePath of pathsToTry) {
|
|
208
|
+
try {
|
|
209
|
+
const fullPath = path2.resolve(process.cwd(), relativePath);
|
|
210
|
+
if (fs2.existsSync(fullPath)) {
|
|
211
|
+
const content = fs2.readFileSync(fullPath, "utf-8");
|
|
212
|
+
return JSON.parse(content);
|
|
213
|
+
}
|
|
214
|
+
} catch {
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
function loadRoutes(manifestPath) {
|
|
220
|
+
const manifest = loadRouteManifest(manifestPath);
|
|
221
|
+
if (!manifest) {
|
|
222
|
+
const pathsChecked = manifestPath ? manifestPath : [...DEFAULT_MANIFEST_PATHS].join(", ");
|
|
223
|
+
throw new Error(
|
|
224
|
+
`Route manifest not found. Checked: ${pathsChecked}
|
|
225
|
+
|
|
226
|
+
In production environments (like Vercel), route scanning requires a pre-built manifest.
|
|
227
|
+
Generate it at build time by adding to your build script:
|
|
228
|
+
|
|
229
|
+
yak-nextjs generate-manifest
|
|
230
|
+
|
|
231
|
+
Or provide routes explicitly in your handler configuration.`
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
return manifest.routes;
|
|
235
|
+
}
|
|
236
|
+
function createNextYakHandler(config) {
|
|
237
|
+
return (0, import_server.createYakHandler)({
|
|
238
|
+
routes: resolveRouteSources(config),
|
|
239
|
+
tools: resolveToolSources(config)
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
async function tryLoadRoutes(appDir) {
|
|
243
|
+
const targetDir = path2.resolve(process.cwd(), appDir);
|
|
244
|
+
if (fs2.existsSync(targetDir)) {
|
|
245
|
+
return scanRoutes(appDir);
|
|
246
|
+
}
|
|
247
|
+
const manifest = loadRouteManifest();
|
|
248
|
+
if (manifest) {
|
|
249
|
+
return manifest.routes;
|
|
250
|
+
}
|
|
251
|
+
throw new Error(
|
|
252
|
+
`Route manifest not found.
|
|
253
|
+
|
|
254
|
+
Generate the manifest at build time by adding to package.json:
|
|
255
|
+
"prebuild": "yak-nextjs generate-manifest"
|
|
256
|
+
|
|
257
|
+
The manifest will be created at ./src/yak-routes-manifest.json and automatically
|
|
258
|
+
bundled with your serverless function.
|
|
259
|
+
|
|
260
|
+
Alternatively, provide routes explicitly:
|
|
261
|
+
createNextYakHandler({ routes: [{ path: "/", title: "Home" }] })`
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
function resolveRouteSources(config) {
|
|
265
|
+
if (config.routes) {
|
|
266
|
+
return config.routes;
|
|
267
|
+
}
|
|
268
|
+
if (config.getRoutes) {
|
|
269
|
+
return config.getRoutes;
|
|
270
|
+
}
|
|
271
|
+
return async () => {
|
|
272
|
+
const routes = await tryLoadRoutes(config.appDir ?? "./src/app");
|
|
273
|
+
return applyRouteFilters(routes, config.routeFilter);
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function applyRouteFilters(routes, filter) {
|
|
277
|
+
if (!filter) {
|
|
278
|
+
return routes;
|
|
279
|
+
}
|
|
280
|
+
const { include = [], exclude = [] } = filter;
|
|
281
|
+
return routes.filter((route) => {
|
|
282
|
+
const path3 = route.path;
|
|
283
|
+
if (include.length > 0 && !include.some((pattern) => matches(pattern, path3))) {
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
if (exclude.some((pattern) => matches(pattern, path3))) {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
return true;
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
function matches(pattern, value) {
|
|
293
|
+
pattern.lastIndex = 0;
|
|
294
|
+
return pattern.test(value);
|
|
295
|
+
}
|
|
296
|
+
function resolveToolSources(config) {
|
|
297
|
+
if (config.tools) {
|
|
298
|
+
return config.tools;
|
|
299
|
+
}
|
|
300
|
+
if (config.getTools) {
|
|
301
|
+
return {
|
|
302
|
+
getTools: config.getTools,
|
|
303
|
+
executeTool: config.executeTool
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
if (config.executeTool) {
|
|
307
|
+
throw new Error("'executeTool' was provided without a matching tool manifest source");
|
|
308
|
+
}
|
|
309
|
+
return void 0;
|
|
310
|
+
}
|
|
311
|
+
function createNextYakConfigHandler(config) {
|
|
312
|
+
return (0, import_server.createYakConfigHandler)({
|
|
313
|
+
routes: resolveRouteSources(config),
|
|
314
|
+
tools: config.tools ?? (config.getTools ? { getTools: config.getTools } : void 0)
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
function createNextYakToolsHandler(config) {
|
|
318
|
+
if (!config.tools && !config.getTools) {
|
|
319
|
+
throw new Error("createNextYakToolsHandler requires either 'tools' or 'getTools'");
|
|
320
|
+
}
|
|
321
|
+
const toolSource = config.tools ?? {
|
|
322
|
+
// getTools is guaranteed to be defined here since we check above
|
|
323
|
+
getTools: config.getTools,
|
|
324
|
+
executeTool: config.executeTool
|
|
325
|
+
};
|
|
326
|
+
return (0, import_server.createYakToolsHandler)({ tools: toolSource });
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// src/server/route-manifest-adapter.ts
|
|
330
|
+
function matchesPattern(pattern, routePath) {
|
|
331
|
+
if (pattern.endsWith("/*")) {
|
|
332
|
+
const prefix = pattern.slice(0, -1);
|
|
333
|
+
return routePath === prefix.slice(0, -1) || routePath.startsWith(prefix);
|
|
334
|
+
}
|
|
335
|
+
return pattern === routePath;
|
|
336
|
+
}
|
|
337
|
+
function filterRoutes(routes, allowedRoutes, disallowedRoutes) {
|
|
338
|
+
return routes.filter((route) => {
|
|
339
|
+
const path3 = route.path;
|
|
340
|
+
if (allowedRoutes && allowedRoutes.length > 0) {
|
|
341
|
+
const isAllowed = allowedRoutes.some((pattern) => matchesPattern(pattern, path3));
|
|
342
|
+
if (!isAllowed) {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (disallowedRoutes && disallowedRoutes.length > 0) {
|
|
347
|
+
const isDisallowed = disallowedRoutes.some((pattern) => matchesPattern(pattern, path3));
|
|
348
|
+
if (isDisallowed) {
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return true;
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
function createRouteManifestAdapter(config) {
|
|
356
|
+
const { routes, id = "manifest", allowedRoutes, disallowedRoutes } = config;
|
|
357
|
+
return {
|
|
358
|
+
id,
|
|
359
|
+
getRoutes: async () => {
|
|
360
|
+
return filterRoutes(routes, allowedRoutes, disallowedRoutes);
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
//# sourceMappingURL=index.server.cjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 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": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,MAAoB;AACpB,IAAAC,QAAsB;AAStB,oBAIO;;;ACdP,SAAoB;AACpB,WAAsB;AAYtB,SAAS,gBAAgB,UAA4D;AACnF,MAAI;AACF,UAAM,UAAa,gBAAa,UAAU,OAAO;AAGjD,UAAM,gBAAgB,QAAQ;AAAA,MAC5B;AAAA,IACF;AAEA,QAAI,CAAC,eAAe;AAClB,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,gBAAgB,cAAc,CAAC;AACrC,UAAM,SAAmD,CAAC;AAG1D,UAAM,aAAa,eAAe,MAAM,iCAAiC;AACzE,QAAI,aAAa,CAAC,GAAG;AACnB,aAAO,QAAQ,WAAW,CAAC;AAAA,IAC7B;AAGA,UAAM,YAAY,eAAe,MAAM,uCAAuC;AAC9E,QAAI,YAAY,CAAC,GAAG;AAClB,aAAO,cAAc,UAAU,CAAC;AAAA,IAClC;AAEA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAKA,SAAS,aAAa,SAA0B;AAC9C,SAAO,QAAQ,WAAW,GAAG,KAAK,QAAQ,SAAS,GAAG;AACxD;AAMA,SAAS,mBAAmB,SAA0B;AACpD,SAAO,QAAQ,WAAW,OAAO,KAAK,QAAQ,SAAS,IAAI;AAC7D;AAMA,SAAS,mBAAmB,SAA0B;AACpD,SAAO,QAAQ,WAAW,MAAM,KAAK,QAAQ,SAAS,GAAG,KAAK,CAAC,QAAQ,WAAW,IAAI;AACxF;AAKA,SAAS,iBAAiB,SAA0B;AAClD,SACE,QAAQ,WAAW,GAAG,KACtB,QAAQ,SAAS,GAAG,KACpB,CAAC,mBAAmB,OAAO,KAC3B,CAAC,mBAAmB,OAAO;AAE/B;AAMA,SAAS,wBAAwB,SAAyB;AAExD,QAAM,YAAY,QAAQ,MAAM,GAAG,EAAE;AACrC,SAAO,IAAI,SAAS;AACtB;AAKA,SAAS,mBAAmB,UAA4B;AAGtD,QAAM,cAAc,SACjB,OAAO,CAAC,QAAQ,CAAC,aAAa,GAAG,KAAK,CAAC,mBAAmB,GAAG,CAAC,EAC9D,IAAI,CAAC,QAAQ;AACZ,QAAI,iBAAiB,GAAG,GAAG;AACzB,aAAO,wBAAwB,GAAG;AAAA,IACpC;AACA,QAAI,mBAAmB,GAAG,GAAG;AAE3B,YAAM,YAAY,IAAI,MAAM,GAAG,EAAE;AACjC,aAAO,IAAI,SAAS;AAAA,IACtB;AACA,WAAO;AAAA,EACT,CAAC;AACH,MAAI,YAAY,WAAW,EAAG,QAAO;AACrC,SAAO,IAAI,YAAY,KAAK,GAAG,CAAC;AAClC;AAKA,SAAS,WAAW,UAA2B;AAC7C,SAAO,aAAa,cAAc,aAAa;AACjD;AAKA,SAAS,iBAAiB,SAAiB,WAAqB,CAAC,GAAgB;AAC/E,QAAM,SAAsB,CAAC;AAE7B,MAAI;AACF,UAAM,UAAa,eAAY,SAAS,EAAE,eAAe,KAAK,CAAC;AAE/D,eAAW,SAAS,SAAS;AAC3B,YAAM,WAAgB,UAAK,SAAS,MAAM,IAAI;AAE9C,UAAI,MAAM,YAAY,GAAG;AAEvB,YAAI,MAAM,KAAK,WAAW,GAAG,KAAK,MAAM,SAAS,OAAO;AACtD;AAAA,QACF;AAGA,eAAO,KAAK,GAAG,iBAAiB,UAAU,CAAC,GAAG,UAAU,MAAM,IAAI,CAAC,CAAC;AAAA,MACtE,WAAW,MAAM,OAAO,KAAK,WAAW,MAAM,IAAI,GAAG;AACnD,cAAM,WAAW,gBAAgB,QAAQ;AAEzC,eAAO,KAAK;AAAA,UACV,MAAM,mBAAmB,QAAQ;AAAA,UACjC,OAAO,SAAS;AAAA,UAChB,aAAa,SAAS;AAAA,QACxB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,4BAA4B,OAAO,KAAK,KAAK;AAAA,EAC7D;AAEA,SAAO;AACT;AAKA,IAAM,wBAAwB,oBAAI,IAAI,CAAC,OAAO,QAAQ,OAAO,QAAQ,OAAO,MAAM,CAAC;AACnF,IAAM,yBAAyB,oBAAI,IAAI;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAAS,mBAAmB,SAAiB,WAAqB,CAAC,GAAgB;AACjF,QAAM,SAAsB,CAAC;AAE7B,MAAI;AACF,UAAM,UAAa,eAAY,SAAS,EAAE,eAAe,KAAK,CAAC;AAE/D,eAAW,SAAS,SAAS;AAC3B,YAAM,WAAgB,UAAK,SAAS,MAAM,IAAI;AAE9C,UAAI,MAAM,YAAY,GAAG;AAEvB,YAAI,MAAM,KAAK,WAAW,GAAG,KAAK,MAAM,SAAS,OAAO;AACtD;AAAA,QACF;AAEA,eAAO,KAAK,GAAG,mBAAmB,UAAU,CAAC,GAAG,UAAU,MAAM,IAAI,CAAC,CAAC;AAAA,MACxE,WAAW,MAAM,OAAO,GAAG;AACzB,cAAM,YAAiB,aAAQ,MAAM,IAAI;AACzC,YAAI,CAAC,sBAAsB,IAAI,SAAS,GAAG;AACzC;AAAA,QACF;AAEA,cAAM,WAAW,MAAM,KAAK,MAAM,GAAG,CAAC,UAAU,MAAM;AACtD,YAAI,CAAC,YAAY,uBAAuB,IAAI,QAAQ,KAAK,SAAS,WAAW,GAAG,GAAG;AACjF;AAAA,QACF;AAEA,cAAM,gBAAgB,mBAAmB,UAAU,QAAQ;AAC3D,cAAM,WAAW,gBAAgB,QAAQ;AAEzC,eAAO,KAAK;AAAA,UACV,MAAM,mBAAmB,aAAa;AAAA,UACtC,OAAO,SAAS;AAAA,UAChB,aAAa,SAAS;AAAA,QACxB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,4BAA4B,OAAO,KAAK,KAAK;AAAA,EAC7D;AAEA,SAAO;AACT;AAEA,SAAS,mBAAmB,UAAoB,UAA4B;AAC1E,QAAM,gBAAgB,CAAC,GAAG,QAAQ;AAClC,MAAI,aAAa,SAAS;AACxB,kBAAc,KAAK,QAAQ;AAAA,EAC7B;AACA,SAAO;AACT;AAMA,SAAS,qBAAqB,SAAoD;AAChF,SAAO;AAAA,IACL,eAAe,SAAS,iBAAiB;AAAA,EAC3C;AACF;AAKO,SAAS,WAAW,WAAmB,SAA0C;AACtF,QAAM,oBAAoB,qBAAqB,OAAO;AACtD,QAAM,YAAiB,aAAQ,QAAQ,IAAI,GAAG,SAAS;AAEvD,MAAI,CAAI,cAAW,SAAS,GAAG;AAC7B,UAAM,IAAI,MAAM,4BAA4B,SAAS,EAAE;AAAA,EACzD;AAEA,QAAM,SACJ,kBAAkB,kBAAkB,UAChC,mBAAmB,WAAW,CAAC,CAAC,IAChC,iBAAiB,WAAW,CAAC,CAAC;AAEpC,SAAO,OAAO,KAAK,CAAC,GAAc,MAAiB,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AACjF;;;ADlOA,IAAM,yBAAyB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AACF;AAcO,SAAS,kBAAkB,cAA6C;AAC7E,QAAM,aAAa,eAAe,CAAC,YAAY,IAAI,CAAC,GAAG,sBAAsB;AAE7E,aAAW,gBAAgB,YAAY;AACrC,QAAI;AACF,YAAM,WAAgB,cAAQ,QAAQ,IAAI,GAAG,YAAY;AACzD,UAAO,eAAW,QAAQ,GAAG;AAC3B,cAAM,UAAa,iBAAa,UAAU,OAAO;AACjD,eAAO,KAAK,MAAM,OAAO;AAAA,MAC3B;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO;AACT;AAMO,SAAS,WAAW,cAAoC;AAC7D,QAAM,WAAW,kBAAkB,YAAY;AAC/C,MAAI,CAAC,UAAU;AACb,UAAM,eAAe,eAAe,eAAe,CAAC,GAAG,sBAAsB,EAAE,KAAK,IAAI;AACxF,UAAM,IAAI;AAAA,MACR,sCAAsC,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAKpD;AAAA,EACF;AACA,SAAO,SAAS;AAClB;AAqBO,SAAS,qBAAqB,QAA8B;AACjE,aAAO,gCAAiB;AAAA,IACtB,QAAQ,oBAAoB,MAAM;AAAA,IAClC,OAAO,mBAAmB,MAAM;AAAA,EAClC,CAAC;AACH;AAQA,eAAe,cAAc,QAAsC;AACjE,QAAM,YAAiB,cAAQ,QAAQ,IAAI,GAAG,MAAM;AAGpD,MAAO,eAAW,SAAS,GAAG;AAC5B,WAAO,WAAW,MAAM;AAAA,EAC1B;AAGA,QAAM,WAAW,kBAAkB;AACnC,MAAI,UAAU;AACZ,WAAO,SAAS;AAAA,EAClB;AAGA,QAAM,IAAI;AAAA,IACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOF;AACF;AAEA,SAAS,oBAAoB,QAAgD;AAC3E,MAAI,OAAO,QAAQ;AACjB,WAAO,OAAO;AAAA,EAChB;AACA,MAAI,OAAO,WAAW;AACpB,WAAO,OAAO;AAAA,EAChB;AACA,SAAO,YAAY;AACjB,UAAM,SAAS,MAAM,cAAc,OAAO,UAAU,WAAW;AAC/D,WAAO,kBAAkB,QAAQ,OAAO,WAAW;AAAA,EACrD;AACF;AAEA,SAAS,kBAAkB,QAAqB,QAA0C;AACxF,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AAEA,QAAM,EAAE,UAAU,CAAC,GAAG,UAAU,CAAC,EAAE,IAAI;AAEvC,SAAO,OAAO,OAAO,CAAC,UAAU;AAC9B,UAAMC,QAAO,MAAM;AAEnB,QAAI,QAAQ,SAAS,KAAK,CAAC,QAAQ,KAAK,CAAC,YAAY,QAAQ,SAASA,KAAI,CAAC,GAAG;AAC5E,aAAO;AAAA,IACT;AAEA,QAAI,QAAQ,KAAK,CAAC,YAAY,QAAQ,SAASA,KAAI,CAAC,GAAG;AACrD,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT,CAAC;AACH;AAEA,SAAS,QAAQ,SAAiB,OAAwB;AACxD,UAAQ,YAAY;AACpB,SAAO,QAAQ,KAAK,KAAK;AAC3B;AAEA,SAAS,mBAAmB,QAA2D;AACrF,MAAI,OAAO,OAAO;AAChB,WAAO,OAAO;AAAA,EAChB;AACA,MAAI,OAAO,UAAU;AACnB,WAAO;AAAA,MACL,UAAU,OAAO;AAAA,MACjB,aAAa,OAAO;AAAA,IACtB;AAAA,EACF;AACA,MAAI,OAAO,aAAa;AACtB,UAAM,IAAI,MAAM,oEAAoE;AAAA,EACtF;AACA,SAAO;AACT;AAWO,SAAS,2BAA2B,QAAoC;AAC7E,aAAO,sCAAuB;AAAA,IAC5B,QAAQ,oBAAoB,MAA8B;AAAA,IAC1D,OAAO,OAAO,UAAU,OAAO,WAAW,EAAE,UAAU,OAAO,SAAS,IAAI;AAAA,EAC5E,CAAC;AACH;AAQO,SAAS,0BAA0B,QAAmC;AAC3E,MAAI,CAAC,OAAO,SAAS,CAAC,OAAO,UAAU;AACrC,UAAM,IAAI,MAAM,iEAAiE;AAAA,EACnF;AAEA,QAAM,aAA8B,OAAO,SAAS;AAAA;AAAA,IAElD,UAAU,OAAO;AAAA,IACjB,aAAa,OAAO;AAAA,EACtB;AAEA,aAAO,qCAAsB,EAAE,OAAO,WAAW,CAAC;AACpD;;;AErLA,SAAS,eAAe,SAAiB,WAA4B;AACnE,MAAI,QAAQ,SAAS,IAAI,GAAG;AAC1B,UAAM,SAAS,QAAQ,MAAM,GAAG,EAAE;AAClC,WAAO,cAAc,OAAO,MAAM,GAAG,EAAE,KAAK,UAAU,WAAW,MAAM;AAAA,EACzE;AACA,SAAO,YAAY;AACrB;AAKA,SAAS,aACP,QACA,eACA,kBACa;AACb,SAAO,OAAO,OAAO,CAAC,UAAU;AAC9B,UAAMC,QAAO,MAAM;AAGnB,QAAI,iBAAiB,cAAc,SAAS,GAAG;AAC7C,YAAM,YAAY,cAAc,KAAK,CAAC,YAAY,eAAe,SAASA,KAAI,CAAC;AAC/E,UAAI,CAAC,WAAW;AACd,eAAO;AAAA,MACT;AAAA,IACF;AAGA,QAAI,oBAAoB,iBAAiB,SAAS,GAAG;AACnD,YAAM,eAAe,iBAAiB,KAAK,CAAC,YAAY,eAAe,SAASA,KAAI,CAAC;AACrF,UAAI,cAAc;AAChB,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT,CAAC;AACH;AAkCO,SAAS,2BAA2B,QAAiD;AAC1F,QAAM,EAAE,QAAQ,KAAK,YAAY,eAAe,iBAAiB,IAAI;AAErE,SAAO;AAAA,IACL;AAAA,IACA,WAAW,YAAY;AACrB,aAAO,aAAa,QAAQ,eAAe,gBAAgB;AAAA,IAC7D;AAAA,EACF;AACF;",
|
|
6
|
+
"names": ["fs", "path", "path", "path"]
|
|
7
|
+
}
|