@yak-io/nextjs 0.1.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/LICENSE +36 -0
- package/README.md +223 -0
- package/dist/cli/generate-manifest.d.ts +16 -0
- package/dist/cli/generate-manifest.d.ts.map +1 -0
- package/dist/cli/generate-manifest.js +216 -0
- package/dist/client/YakProvider.d.ts +15 -0
- package/dist/client/YakProvider.d.ts.map +1 -0
- package/dist/client/YakProvider.js +57 -0
- package/dist/client/useYak.d.ts +4 -0
- package/dist/client/useYak.d.ts.map +1 -0
- package/dist/client/useYak.js +4 -0
- package/dist/index.client.d.ts +6 -0
- package/dist/index.client.d.ts.map +1 -0
- package/dist/index.client.js +4 -0
- package/dist/index.server.d.ts +2 -0
- package/dist/index.server.d.ts.map +1 -0
- package/dist/index.server.js +2 -0
- package/dist/internal/logger.d.ts +11 -0
- package/dist/internal/logger.d.ts.map +1 -0
- package/dist/internal/logger.js +23 -0
- package/dist/server/createNextYakConfigHandler.d.ts +3 -0
- package/dist/server/createNextYakConfigHandler.d.ts.map +1 -0
- package/dist/server/createNextYakConfigHandler.js +1 -0
- package/dist/server/createNextYakHandler.d.ts +38 -0
- package/dist/server/createNextYakHandler.d.ts.map +1 -0
- package/dist/server/createNextYakHandler.js +71 -0
- package/dist/server/createNextYakToolsHandler.d.ts +3 -0
- package/dist/server/createNextYakToolsHandler.d.ts.map +1 -0
- package/dist/server/createNextYakToolsHandler.js +1 -0
- package/dist/server/index.d.ts +6 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +2 -0
- package/dist/server/scan-routes.d.ts +9 -0
- package/dist/server/scan-routes.d.ts.map +1 -0
- package/dist/server/scan-routes.js +197 -0
- package/dist/types/config.d.ts +12 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +1 -0
- package/dist/types/messaging.d.ts +71 -0
- package/dist/types/messaging.d.ts.map +1 -0
- package/dist/types/messaging.js +1 -0
- package/dist/types/routes.d.ts +21 -0
- package/dist/types/routes.d.ts.map +1 -0
- package/dist/types/routes.js +1 -0
- package/dist/types/tools.d.ts +52 -0
- package/dist/types/tools.d.ts.map +1 -0
- package/dist/types/tools.js +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { RouteSourceInput, ToolSourceInput, RouteInfo, ToolManifest, ToolExecutor } from "@yak-io/javascript/server";
|
|
2
|
+
export type NextYakRouteFilter = {
|
|
3
|
+
include?: RegExp[];
|
|
4
|
+
exclude?: RegExp[];
|
|
5
|
+
};
|
|
6
|
+
export type NextYakHandlerConfig = {
|
|
7
|
+
appDir?: string;
|
|
8
|
+
routeFilter?: NextYakRouteFilter;
|
|
9
|
+
routes?: RouteSourceInput;
|
|
10
|
+
getRoutes?: () => Promise<RouteInfo[]>;
|
|
11
|
+
tools?: ToolSourceInput;
|
|
12
|
+
pagesDir?: string | false;
|
|
13
|
+
getTools?: () => Promise<ToolManifest>;
|
|
14
|
+
executeTool?: ToolExecutor;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Create a unified Next.js App Router handler backed by the core primitives
|
|
18
|
+
*/
|
|
19
|
+
export declare function createNextYakHandler(config: NextYakHandlerConfig): {
|
|
20
|
+
GET: (_req: Request) => Promise<Response>;
|
|
21
|
+
POST: (req: Request) => Promise<Response>;
|
|
22
|
+
};
|
|
23
|
+
export type NextYakConfigHandlerConfig = {
|
|
24
|
+
appDir?: string;
|
|
25
|
+
routeFilter?: NextYakRouteFilter;
|
|
26
|
+
routes?: RouteSourceInput;
|
|
27
|
+
getRoutes?: () => Promise<RouteInfo[]>;
|
|
28
|
+
tools?: ToolSourceInput;
|
|
29
|
+
getTools?: () => Promise<ToolManifest>;
|
|
30
|
+
};
|
|
31
|
+
export declare function createNextYakConfigHandler(config: NextYakConfigHandlerConfig): (_req: Request) => Promise<Response>;
|
|
32
|
+
export type NextYakToolsHandlerConfig = {
|
|
33
|
+
tools?: ToolSourceInput;
|
|
34
|
+
getTools?: () => Promise<ToolManifest>;
|
|
35
|
+
executeTool: ToolExecutor;
|
|
36
|
+
};
|
|
37
|
+
export declare function createNextYakToolsHandler(config: NextYakToolsHandlerConfig): (req: Request) => Promise<Response>;
|
|
38
|
+
//# sourceMappingURL=createNextYakHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createNextYakHandler.d.ts","sourceRoot":"","sources":["../../src/server/createNextYakHandler.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,YAAY,EACZ,YAAY,EACb,MAAM,2BAA2B,CAAC;AAGnC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACvC,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC;IACvC,WAAW,CAAC,EAAE,YAAY,CAAC;CAC5B,CAAC;AAEF;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,oBAAoB;;;EAKhE;AA2DD,MAAM,MAAM,0BAA0B,GAAG;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACvC,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC;CACxC,CAAC;AAEF,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,0BAA0B,wCAK5E;AAED,MAAM,MAAM,yBAAyB,GAAG;IACtC,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC;IACvC,WAAW,EAAE,YAAY,CAAC;CAC3B,CAAC;AAEF,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,yBAAyB,uCAW1E"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { createYakHandler, createYakConfigHandler, createYakToolsHandler, } from "@yak-io/javascript/server";
|
|
2
|
+
import { scanRoutes } from "./scan-routes.js";
|
|
3
|
+
/**
|
|
4
|
+
* Create a unified Next.js App Router handler backed by the core primitives
|
|
5
|
+
*/
|
|
6
|
+
export function createNextYakHandler(config) {
|
|
7
|
+
return createYakHandler({
|
|
8
|
+
routes: resolveRouteSources(config),
|
|
9
|
+
tools: resolveToolSources(config),
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
function resolveRouteSources(config) {
|
|
13
|
+
if (config.routes) {
|
|
14
|
+
return config.routes;
|
|
15
|
+
}
|
|
16
|
+
if (config.getRoutes) {
|
|
17
|
+
return config.getRoutes;
|
|
18
|
+
}
|
|
19
|
+
return async () => applyRouteFilters(scanRoutes(config.appDir ?? "./src/app"), config.routeFilter);
|
|
20
|
+
}
|
|
21
|
+
function applyRouteFilters(routes, filter) {
|
|
22
|
+
if (!filter) {
|
|
23
|
+
return routes;
|
|
24
|
+
}
|
|
25
|
+
const { include = [], exclude = [] } = filter;
|
|
26
|
+
return routes.filter(route => {
|
|
27
|
+
const path = route.path;
|
|
28
|
+
if (include.length > 0 && !include.some(pattern => matches(pattern, path))) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
if (exclude.some(pattern => matches(pattern, path))) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function matches(pattern, value) {
|
|
38
|
+
pattern.lastIndex = 0;
|
|
39
|
+
return pattern.test(value);
|
|
40
|
+
}
|
|
41
|
+
function resolveToolSources(config) {
|
|
42
|
+
if (config.tools) {
|
|
43
|
+
return config.tools;
|
|
44
|
+
}
|
|
45
|
+
if (config.getTools) {
|
|
46
|
+
return {
|
|
47
|
+
getTools: config.getTools,
|
|
48
|
+
executeTool: config.executeTool,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (config.executeTool) {
|
|
52
|
+
throw new Error("'executeTool' was provided without a matching tool manifest source");
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
export function createNextYakConfigHandler(config) {
|
|
57
|
+
return createYakConfigHandler({
|
|
58
|
+
routes: resolveRouteSources(config),
|
|
59
|
+
tools: config.tools ?? (config.getTools ? { getTools: config.getTools } : undefined),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
export function createNextYakToolsHandler(config) {
|
|
63
|
+
if (!config.tools && !config.getTools) {
|
|
64
|
+
throw new Error("createNextYakToolsHandler requires either 'tools' or 'getTools'");
|
|
65
|
+
}
|
|
66
|
+
const toolSource = config.tools ?? {
|
|
67
|
+
getTools: config.getTools,
|
|
68
|
+
executeTool: config.executeTool,
|
|
69
|
+
};
|
|
70
|
+
return createYakToolsHandler({ tools: toolSource });
|
|
71
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createNextYakToolsHandler.d.ts","sourceRoot":"","sources":["../../src/server/createNextYakToolsHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AACtE,YAAY,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createNextYakToolsHandler } from "./createNextYakHandler.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { scanRoutes } from "./scan-routes.js";
|
|
2
|
+
export type { ScanRoutesOptions } from "./scan-routes.js";
|
|
3
|
+
export { createNextYakHandler, createNextYakConfigHandler, createNextYakToolsHandler, } from "./createNextYakHandler.js";
|
|
4
|
+
export type { NextYakHandlerConfig, NextYakConfigHandlerConfig, NextYakToolsHandlerConfig, NextYakRouteFilter, } from "./createNextYakHandler.js";
|
|
5
|
+
export type { ToolExecutor, ToolCallPayload, ToolCallResult, ToolDefinition, ToolManifest, RouteInfo, RouteManifest, ChatConfig, } from "@yak-io/javascript/server";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,OAAO,EACN,oBAAoB,EACpB,0BAA0B,EAC1B,yBAAyB,GACzB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACX,oBAAoB,EACpB,0BAA0B,EAC1B,yBAAyB,EACzB,kBAAkB,GAClB,MAAM,2BAA2B,CAAC;AAEnC,YAAY,EACX,YAAY,EACZ,eAAe,EACf,cAAc,EACd,cAAc,EACd,YAAY,EACZ,SAAS,EACT,aAAa,EACb,UAAU,GACV,MAAM,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RouteInfo } from "@yak-io/javascript/server";
|
|
2
|
+
export type ScanRoutesOptions = {
|
|
3
|
+
directoryType?: "app" | "pages";
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Scan a Next.js app or pages directory and return page route information
|
|
7
|
+
*/
|
|
8
|
+
export declare function scanRoutes(directory: string, options?: ScanRoutesOptions): RouteInfo[];
|
|
9
|
+
//# sourceMappingURL=scan-routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan-routes.d.ts","sourceRoot":"","sources":["../../src/server/scan-routes.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAE3D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,aAAa,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;CACjC,CAAC;AA8NF;;GAEG;AACH,wBAAgB,UAAU,CACxB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,SAAS,EAAE,CAcb"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Extract static metadata (title and description) from a Next.js page file.
|
|
5
|
+
* Parses `export const metadata = { title: "...", description: "..." }` patterns.
|
|
6
|
+
* Does not support dynamic generateMetadata functions.
|
|
7
|
+
*/
|
|
8
|
+
function extractMetadata(filePath) {
|
|
9
|
+
try {
|
|
10
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
11
|
+
// Match `export const metadata` object
|
|
12
|
+
const metadataMatch = content.match(/export\s+const\s+metadata\s*(?::\s*Metadata\s*)?=\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}/s);
|
|
13
|
+
if (!metadataMatch) {
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
const metadataBlock = metadataMatch[1];
|
|
17
|
+
const result = {};
|
|
18
|
+
// Extract title - handle both single and double quotes
|
|
19
|
+
const titleMatch = metadataBlock?.match(/title\s*:\s*["'`]([^"'`]+)["'`]/);
|
|
20
|
+
if (titleMatch?.[1]) {
|
|
21
|
+
result.title = titleMatch[1];
|
|
22
|
+
}
|
|
23
|
+
// Extract description - handle both single and double quotes
|
|
24
|
+
const descMatch = metadataBlock?.match(/description\s*:\s*["'`]([^"'`]+)["'`]/);
|
|
25
|
+
if (descMatch?.[1]) {
|
|
26
|
+
result.description = descMatch[1];
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return {};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Check if a path segment is a route group (organizational only, not part of URL)
|
|
36
|
+
*/
|
|
37
|
+
function isRouteGroup(segment) {
|
|
38
|
+
return segment.startsWith("(") && segment.endsWith(")");
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Check if a path segment is an optional catch-all (e.g., [[...slug]])
|
|
42
|
+
* These segments match the base path without any additional segments
|
|
43
|
+
*/
|
|
44
|
+
function isOptionalCatchAll(segment) {
|
|
45
|
+
return segment.startsWith("[[...") && segment.endsWith("]]");
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Check if a path segment is a required catch-all (e.g., [...slug])
|
|
49
|
+
* These segments require at least one additional path segment
|
|
50
|
+
*/
|
|
51
|
+
function isRequiredCatchAll(segment) {
|
|
52
|
+
return segment.startsWith("[...") && segment.endsWith("]") && !segment.startsWith("[[");
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Check if a path segment is a dynamic segment (e.g., [id])
|
|
56
|
+
*/
|
|
57
|
+
function isDynamicSegment(segment) {
|
|
58
|
+
return segment.startsWith("[") && segment.endsWith("]") && !isOptionalCatchAll(segment) && !isRequiredCatchAll(segment);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Normalize a dynamic segment for display
|
|
62
|
+
* e.g., [id] → :id, [postId] → :postId
|
|
63
|
+
*/
|
|
64
|
+
function normalizeDynamicSegment(segment) {
|
|
65
|
+
// Extract the parameter name from [name] format
|
|
66
|
+
const paramName = segment.slice(1, -1);
|
|
67
|
+
return `:${paramName}`;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Normalize a route path for display (excludes route groups and handles dynamic segments)
|
|
71
|
+
*/
|
|
72
|
+
function normalizeRoutePath(segments) {
|
|
73
|
+
// Filter out route groups - they don't appear in URLs
|
|
74
|
+
// Filter out optional catch-all segments - they match the base path
|
|
75
|
+
const urlSegments = segments
|
|
76
|
+
.filter(seg => !isRouteGroup(seg) && !isOptionalCatchAll(seg))
|
|
77
|
+
.map(seg => {
|
|
78
|
+
if (isDynamicSegment(seg)) {
|
|
79
|
+
return normalizeDynamicSegment(seg);
|
|
80
|
+
}
|
|
81
|
+
if (isRequiredCatchAll(seg)) {
|
|
82
|
+
// For required catch-all, extract the name: [...slug] → :slug+
|
|
83
|
+
const paramName = seg.slice(4, -1);
|
|
84
|
+
return `:${paramName}+`;
|
|
85
|
+
}
|
|
86
|
+
return seg;
|
|
87
|
+
});
|
|
88
|
+
if (urlSegments.length === 0)
|
|
89
|
+
return "/";
|
|
90
|
+
return "/" + urlSegments.join("/");
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Check if file is a page file
|
|
94
|
+
*/
|
|
95
|
+
function isPageFile(filename) {
|
|
96
|
+
return filename === "page.tsx" || filename === "page.js";
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Recursively scan a directory for Next.js page routes
|
|
100
|
+
*/
|
|
101
|
+
function scanAppDirectory(dirPath, segments = []) {
|
|
102
|
+
const routes = [];
|
|
103
|
+
try {
|
|
104
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
105
|
+
for (const entry of entries) {
|
|
106
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
107
|
+
if (entry.isDirectory()) {
|
|
108
|
+
// Skip special Next.js directories and api routes
|
|
109
|
+
if (entry.name.startsWith("_") || entry.name === "api") {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
// Recursively scan subdirectories
|
|
113
|
+
routes.push(...scanAppDirectory(fullPath, [...segments, entry.name]));
|
|
114
|
+
}
|
|
115
|
+
else if (entry.isFile() && isPageFile(entry.name)) {
|
|
116
|
+
const metadata = extractMetadata(fullPath);
|
|
117
|
+
routes.push({
|
|
118
|
+
path: normalizeRoutePath(segments),
|
|
119
|
+
title: metadata.title,
|
|
120
|
+
description: metadata.description,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
console.error(`Error scanning directory ${dirPath}:`, error);
|
|
127
|
+
}
|
|
128
|
+
return routes;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* File discovery helpers for legacy `pages/` directories
|
|
132
|
+
*/
|
|
133
|
+
const VALID_PAGE_EXTENSIONS = new Set([".js", ".jsx", ".ts", ".tsx", ".md", ".mdx"]);
|
|
134
|
+
const SPECIAL_PAGE_FILENAMES = new Set(["_app", "_document", "_error", "404", "500", "middleware", "_middleware"]);
|
|
135
|
+
function scanPagesDirectory(dirPath, segments = []) {
|
|
136
|
+
const routes = [];
|
|
137
|
+
try {
|
|
138
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
139
|
+
for (const entry of entries) {
|
|
140
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
141
|
+
if (entry.isDirectory()) {
|
|
142
|
+
// Skip special directories and api routes
|
|
143
|
+
if (entry.name.startsWith("_") || entry.name === "api") {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
routes.push(...scanPagesDirectory(fullPath, [...segments, entry.name]));
|
|
147
|
+
}
|
|
148
|
+
else if (entry.isFile()) {
|
|
149
|
+
const extension = path.extname(entry.name);
|
|
150
|
+
if (!VALID_PAGE_EXTENSIONS.has(extension)) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const baseName = entry.name.slice(0, -extension.length);
|
|
154
|
+
if (!baseName || SPECIAL_PAGE_FILENAMES.has(baseName) || baseName.startsWith("_")) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const routeSegments = buildPagesSegments(segments, baseName);
|
|
158
|
+
const metadata = extractMetadata(fullPath);
|
|
159
|
+
routes.push({
|
|
160
|
+
path: normalizeRoutePath(routeSegments),
|
|
161
|
+
title: metadata.title,
|
|
162
|
+
description: metadata.description,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
console.error(`Error scanning directory ${dirPath}:`, error);
|
|
169
|
+
}
|
|
170
|
+
return routes;
|
|
171
|
+
}
|
|
172
|
+
function buildPagesSegments(segments, baseName) {
|
|
173
|
+
const routeSegments = [...segments];
|
|
174
|
+
if (baseName !== "index") {
|
|
175
|
+
routeSegments.push(baseName);
|
|
176
|
+
}
|
|
177
|
+
return routeSegments;
|
|
178
|
+
}
|
|
179
|
+
function normalizeScanOptions(options) {
|
|
180
|
+
return {
|
|
181
|
+
directoryType: options?.directoryType ?? "app",
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Scan a Next.js app or pages directory and return page route information
|
|
186
|
+
*/
|
|
187
|
+
export function scanRoutes(directory, options) {
|
|
188
|
+
const normalizedOptions = normalizeScanOptions(options);
|
|
189
|
+
const targetDir = path.resolve(process.cwd(), directory);
|
|
190
|
+
if (!fs.existsSync(targetDir)) {
|
|
191
|
+
throw new Error(`App directory not found: ${targetDir}`);
|
|
192
|
+
}
|
|
193
|
+
const routes = normalizedOptions.directoryType === "pages"
|
|
194
|
+
? scanPagesDirectory(targetDir, [])
|
|
195
|
+
: scanAppDirectory(targetDir, []);
|
|
196
|
+
return routes.sort((a, b) => a.path.localeCompare(b.path));
|
|
197
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ToolManifest } from "./tools.js";
|
|
2
|
+
import type { RouteManifest } from "./routes.js";
|
|
3
|
+
/**
|
|
4
|
+
* Combined configuration for the chatbot including routes and optionally tools
|
|
5
|
+
*/
|
|
6
|
+
export type ChatConfig = {
|
|
7
|
+
/** Route manifest with Next.js routes */
|
|
8
|
+
routes: RouteManifest;
|
|
9
|
+
/** Optional tool manifest (e.g., tRPC procedures) */
|
|
10
|
+
tools?: ToolManifest;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,yCAAyC;IACzC,MAAM,EAAE,aAAa,CAAC;IACtB,qDAAqD;IACrD,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { ToolManifest } from "./tools";
|
|
2
|
+
import type { RouteManifest } from "./routes";
|
|
3
|
+
/**
|
|
4
|
+
* Theme configuration for the chatbot widget
|
|
5
|
+
*/
|
|
6
|
+
export type Theme = {
|
|
7
|
+
position?: "bottom-right" | "bottom-left";
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Page context information from the host page
|
|
11
|
+
*/
|
|
12
|
+
export type PageContext = {
|
|
13
|
+
url: string;
|
|
14
|
+
title: string;
|
|
15
|
+
text: string;
|
|
16
|
+
timestamp: number;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Messages sent FROM the host application TO the iframe
|
|
20
|
+
*/
|
|
21
|
+
export type IframeMessageFromHost = {
|
|
22
|
+
type: "yak:config";
|
|
23
|
+
payload: {
|
|
24
|
+
appId: string;
|
|
25
|
+
theme?: Theme;
|
|
26
|
+
toolManifest?: ToolManifest;
|
|
27
|
+
routeManifest?: RouteManifest;
|
|
28
|
+
};
|
|
29
|
+
} | {
|
|
30
|
+
type: "yak:tool_result";
|
|
31
|
+
payload: {
|
|
32
|
+
id: string;
|
|
33
|
+
ok: true;
|
|
34
|
+
result: unknown;
|
|
35
|
+
};
|
|
36
|
+
} | {
|
|
37
|
+
type: "yak:tool_result";
|
|
38
|
+
payload: {
|
|
39
|
+
id: string;
|
|
40
|
+
ok: false;
|
|
41
|
+
error: string;
|
|
42
|
+
};
|
|
43
|
+
} | {
|
|
44
|
+
type: "yak:page_context";
|
|
45
|
+
payload: PageContext;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Messages sent FROM the iframe TO the host application
|
|
49
|
+
*/
|
|
50
|
+
export type IframeMessageToHost = {
|
|
51
|
+
type: "yak:ready";
|
|
52
|
+
} | {
|
|
53
|
+
type: "yak:tool_call";
|
|
54
|
+
payload: {
|
|
55
|
+
id: string;
|
|
56
|
+
name: string;
|
|
57
|
+
args: unknown;
|
|
58
|
+
};
|
|
59
|
+
} | {
|
|
60
|
+
type: "yak:redirect";
|
|
61
|
+
payload: {
|
|
62
|
+
path: string;
|
|
63
|
+
};
|
|
64
|
+
} | {
|
|
65
|
+
type: "yak:close";
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Union of all message types (for type guards)
|
|
69
|
+
*/
|
|
70
|
+
export type IframeMessage = IframeMessageFromHost | IframeMessageToHost;
|
|
71
|
+
//# sourceMappingURL=messaging.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messaging.d.ts","sourceRoot":"","sources":["../../src/types/messaging.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C;;GAEG;AACH,MAAM,MAAM,KAAK,GAAG;IAClB,QAAQ,CAAC,EAAE,cAAc,GAAG,aAAa,CAAC;CAC3C,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAC7B;IACE,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,KAAK,CAAC;QACd,YAAY,CAAC,EAAE,YAAY,CAAC;QAC5B,aAAa,CAAC,EAAE,aAAa,CAAC;KAC/B,CAAC;CACH,GACD;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GAC/E;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAC9E;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,CAAC;AAEvD;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAC3B;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GAC/E;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACnD;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC;AAE1B;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,qBAAqB,GAAG,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Information about a single route in the Next.js application
|
|
3
|
+
*/
|
|
4
|
+
export type RouteInfo = {
|
|
5
|
+
/** The route path, e.g., "/dashboard" or "/posts/[id]" */
|
|
6
|
+
path: string;
|
|
7
|
+
/** Human-readable title extracted from page metadata */
|
|
8
|
+
title?: string;
|
|
9
|
+
/** Description extracted from page metadata */
|
|
10
|
+
description?: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Manifest of all routes in the Next.js application
|
|
14
|
+
*/
|
|
15
|
+
export type RouteManifest = {
|
|
16
|
+
/** List of all discovered routes */
|
|
17
|
+
routes: RouteInfo[];
|
|
18
|
+
/** ISO timestamp when the manifest was generated */
|
|
19
|
+
generated_at: string;
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/types/routes.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,oCAAoC;IACpC,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,oDAAoD;IACpD,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic JSON Schema type
|
|
3
|
+
*/
|
|
4
|
+
export type JSONSchema = Record<string, unknown>;
|
|
5
|
+
/**
|
|
6
|
+
* Payload for a tool call request from the iframe
|
|
7
|
+
*/
|
|
8
|
+
export type ToolCallPayload = {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
args: unknown;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Successful tool call result
|
|
15
|
+
*/
|
|
16
|
+
export type ToolCallSuccess = {
|
|
17
|
+
ok: true;
|
|
18
|
+
result: unknown;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Failed tool call result
|
|
22
|
+
*/
|
|
23
|
+
export type ToolCallError = {
|
|
24
|
+
ok: false;
|
|
25
|
+
error: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Union type for tool call responses
|
|
29
|
+
*/
|
|
30
|
+
export type ToolCallResult = ToolCallSuccess | ToolCallError;
|
|
31
|
+
/**
|
|
32
|
+
* Definition of a single tool (tRPC procedure) available to the LLM
|
|
33
|
+
*/
|
|
34
|
+
export type ToolDefinition = {
|
|
35
|
+
/** Fully qualified procedure name, e.g. "orders.list" */
|
|
36
|
+
name: string;
|
|
37
|
+
/** Human friendly label */
|
|
38
|
+
displayName?: string;
|
|
39
|
+
/** Description shown to the LLM as a tool description */
|
|
40
|
+
description?: string;
|
|
41
|
+
/** Optional JSON Schema for input args */
|
|
42
|
+
inputSchema?: JSONSchema;
|
|
43
|
+
/** Optional JSON Schema for output shape (not required in v1) */
|
|
44
|
+
outputSchema?: JSONSchema;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Manifest of all tools (tRPC procedures) available to the chatbot
|
|
48
|
+
*/
|
|
49
|
+
export type ToolManifest = {
|
|
50
|
+
tools: ToolDefinition[];
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/types/tools.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;CACf,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,IAAI,CAAC;IACT,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,eAAe,GAAG,aAAa,CAAC;AAE7D;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IAEb,2BAA2B;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,UAAU,CAAC;IAEzB,iEAAiE;IACjE,YAAY,CAAC,EAAE,UAAU,CAAC;CAC3B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,cAAc,EAAE,CAAC;CACzB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yak-io/nextjs",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Next.js SDK for embedding yak chatbot with route manifest generation",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
7
|
+
"author": "Yak <support@yak.io>",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/9f-au/yak.git",
|
|
11
|
+
"directory": "packages/nextjs"
|
|
12
|
+
},
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public",
|
|
15
|
+
"provenance": false
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"yak",
|
|
19
|
+
"chatbot",
|
|
20
|
+
"ai",
|
|
21
|
+
"widget",
|
|
22
|
+
"chat",
|
|
23
|
+
"nextjs",
|
|
24
|
+
"react"
|
|
25
|
+
],
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=18"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"LICENSE"
|
|
32
|
+
],
|
|
33
|
+
"sideEffects": false,
|
|
34
|
+
"exports": {
|
|
35
|
+
"./client": {
|
|
36
|
+
"types": "./dist/index.client.d.ts",
|
|
37
|
+
"import": "./dist/index.client.js"
|
|
38
|
+
},
|
|
39
|
+
"./server": {
|
|
40
|
+
"types": "./dist/index.server.d.ts",
|
|
41
|
+
"import": "./dist/index.server.js"
|
|
42
|
+
},
|
|
43
|
+
"./package.json": "./package.json"
|
|
44
|
+
},
|
|
45
|
+
"bin": {
|
|
46
|
+
"yak-nextjs": "./dist/cli/generate-manifest.js"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@yak-io/javascript": "0.1.0",
|
|
50
|
+
"@yak-io/react": "0.1.0"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"next": "^14.0.0 || ^15.0.0",
|
|
54
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
55
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@types/node": "^24.10.2",
|
|
59
|
+
"@types/react": "^19.2.7",
|
|
60
|
+
"@types/react-dom": "^19.2.0",
|
|
61
|
+
"next": "^16.0.10",
|
|
62
|
+
"react": "^19.2.3",
|
|
63
|
+
"react-dom": "^19.2.3",
|
|
64
|
+
"typescript": "^5.3.0",
|
|
65
|
+
"@repo/typescript-config": "0.0.0"
|
|
66
|
+
},
|
|
67
|
+
"scripts": {
|
|
68
|
+
"build": "tsc",
|
|
69
|
+
"check-types": "tsc --noEmit"
|
|
70
|
+
}
|
|
71
|
+
}
|