@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
package/LICENSE
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
Yak Proprietary License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Yak. All rights reserved.
|
|
4
|
+
|
|
5
|
+
This software and associated documentation files (the "Software") are the
|
|
6
|
+
proprietary property of Yak and are protected by copyright law.
|
|
7
|
+
|
|
8
|
+
GRANT OF LICENSE:
|
|
9
|
+
Subject to the terms of this license and your valid subscription or agreement
|
|
10
|
+
with Yak, you are granted a limited, non-exclusive, non-transferable license
|
|
11
|
+
to use the Software solely for integrating the Yak chatbot widget into your
|
|
12
|
+
applications as intended and documented.
|
|
13
|
+
|
|
14
|
+
RESTRICTIONS:
|
|
15
|
+
You may NOT:
|
|
16
|
+
- Modify, adapt, alter, translate, or create derivative works of the Software
|
|
17
|
+
- Reverse engineer, disassemble, decompile, or otherwise attempt to derive
|
|
18
|
+
the source code of the Software
|
|
19
|
+
- Redistribute, sublicense, lease, rent, or lend the Software to third parties
|
|
20
|
+
- Remove or alter any proprietary notices, labels, or marks on the Software
|
|
21
|
+
- Use the Software for any purpose other than as expressly permitted herein
|
|
22
|
+
|
|
23
|
+
NO WARRANTY:
|
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
25
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
26
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL YAK
|
|
27
|
+
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
|
28
|
+
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
29
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
30
|
+
|
|
31
|
+
TERMINATION:
|
|
32
|
+
This license is effective until terminated. Your rights under this license
|
|
33
|
+
will terminate automatically without notice if you fail to comply with any
|
|
34
|
+
of its terms.
|
|
35
|
+
|
|
36
|
+
For licensing inquiries, contact: support@yak.io
|
package/README.md
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# @yak-io/nextjs
|
|
2
|
+
|
|
3
|
+
Next.js integration layer for the Yak embeddable chat widget. This package focuses on the App Router plumbing (route scanning, CLI helpers, and ergonomic handler factories) while delegating the widget runtime to [`@yak-io/react`](../react) and [`@yak-io/javascript`](../javascript).
|
|
4
|
+
|
|
5
|
+
## Relationship to `@yak-io/javascript` and `@yak-io/react`
|
|
6
|
+
|
|
7
|
+
- **`@yak-io/javascript`** exposes the framework-agnostic client logic (iframe communication, tool execution) and server utilities (`createYakHandler`, `RouteSource`, `ToolSource`, etc.).
|
|
8
|
+
- **`@yak-io/react`** exposes the React provider/widget and hooks.
|
|
9
|
+
- **`@yak-io/nextjs`** re-exports the client entrypoint for convenience and layers Next-specific helpers on top of the core primitives (route scanners, manifest CLI, handler factories that accept either simple callbacks or rich adapters).
|
|
10
|
+
|
|
11
|
+
If you are not on Next.js you can depend on `@yak-io/react` directly and provide your own routing/tool adapters.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add @yak-io/nextjs
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The widget core is pulled in automatically as a dependency.
|
|
20
|
+
|
|
21
|
+
## Quickstart
|
|
22
|
+
|
|
23
|
+
### 1. Client widget
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
// app/layout.tsx
|
|
27
|
+
import { YakProvider, YakWidget } from "@yak-io/nextjs/client";
|
|
28
|
+
|
|
29
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
30
|
+
return (
|
|
31
|
+
<html lang="en">
|
|
32
|
+
<body>
|
|
33
|
+
<YakProvider
|
|
34
|
+
appId={process.env.NEXT_PUBLIC_YAK_APP_ID!}
|
|
35
|
+
theme={{ position: "bottom-right" }}
|
|
36
|
+
>
|
|
37
|
+
{children}
|
|
38
|
+
<YakWidget />
|
|
39
|
+
</YakProvider>
|
|
40
|
+
</body>
|
|
41
|
+
</html>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 2. Unified API route
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
// app/api/yak/[...yak]/route.ts
|
|
50
|
+
import { createNextYakHandler } from "@yak-io/nextjs/server";
|
|
51
|
+
|
|
52
|
+
export const { GET, POST } = createNextYakHandler({
|
|
53
|
+
// Routes auto-scan from ./src/app and ./src/pages
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The handler responds to `GET` with route metadata (and optional tool manifest) and to `POST` with tool execution.
|
|
58
|
+
|
|
59
|
+
### 3. Programmatic control
|
|
60
|
+
|
|
61
|
+
Control the chat widget from your code:
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
// app/my-page/page.tsx
|
|
65
|
+
"use client";
|
|
66
|
+
|
|
67
|
+
import { useYak } from "@yak-io/nextjs/client";
|
|
68
|
+
|
|
69
|
+
export default function MyPage() {
|
|
70
|
+
const chat = useYak();
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<div>
|
|
74
|
+
<button onClick={() => chat.open()}>Open Chat</button>
|
|
75
|
+
<button onClick={() => chat.openWithPrompt("Explain this product")}>
|
|
76
|
+
Product Help
|
|
77
|
+
</button>
|
|
78
|
+
<button onClick={() => chat.openWithPrompt("How do I use this feature?")}>
|
|
79
|
+
Feature Help
|
|
80
|
+
</button>
|
|
81
|
+
{chat.isOpen && <p>Chat is open</p>}
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 4. Add tools (tRPC example)
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { createNextYakHandler } from "@yak-io/nextjs/server";
|
|
91
|
+
import { createTRPCToolAdapter } from "@yak-io/trpc";
|
|
92
|
+
import { appRouter, createContext } from "@/server/trpc";
|
|
93
|
+
|
|
94
|
+
const trpcTools = createTRPCToolAdapter({
|
|
95
|
+
id: "trpc",
|
|
96
|
+
router: appRouter,
|
|
97
|
+
createContext: async ({ req }) => createContext({ req }),
|
|
98
|
+
allowedProcedures: ["orders.list", "orders.detail"],
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
export const { GET, POST } = createNextYakHandler({
|
|
102
|
+
tools: trpcTools,
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Route & tool injection
|
|
107
|
+
|
|
108
|
+
`createNextYakHandler` auto-scans `./src/app` **and** `./src/pages`, skips `api/` folders, and feeds the manifest to Yak. Override the directories or clamp the manifest with regex filters:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import { createNextYakConfigHandler } from "@yak-io/nextjs/server";
|
|
112
|
+
|
|
113
|
+
export const { GET } = createNextYakConfigHandler({
|
|
114
|
+
appDir: "./app",
|
|
115
|
+
pagesDir: "./pages",
|
|
116
|
+
routeFilter: {
|
|
117
|
+
include: [/^\/docs/],
|
|
118
|
+
exclude: [/^\/docs\/draft/],
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Providing `routeFilter.include` means at least one regex must match the path; `routeFilter.exclude` removes any matches. If you set `routes` or `getRoutes` the auto-scanner is bypassed, allowing you to compose your own sources:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
import type { RouteSource } from "@yak-io/javascript/server";
|
|
127
|
+
import {
|
|
128
|
+
createNextYakHandler,
|
|
129
|
+
scanRoutes,
|
|
130
|
+
} from "@yak-io/nextjs/server";
|
|
131
|
+
import { createTRPCToolAdapter } from "@yak-io/trpc";
|
|
132
|
+
|
|
133
|
+
const marketingRoutes: RouteSource = {
|
|
134
|
+
id: "marketing",
|
|
135
|
+
getRoutes: async () => [
|
|
136
|
+
{ path: "/docs", title: "Documentation", description: "Product docs" },
|
|
137
|
+
],
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const graphqlTools = {
|
|
141
|
+
id: "graphql",
|
|
142
|
+
getTools: async () => [{ name: "accounts.lookup", description: "Fetch account" }],
|
|
143
|
+
executeTool: async (name, args) => {
|
|
144
|
+
const res = await fetch("https://graphql.example.com", {
|
|
145
|
+
method: "POST",
|
|
146
|
+
headers: { "Content-Type": "application/json" },
|
|
147
|
+
body: JSON.stringify({ query: buildQueryFor(name), variables: args }),
|
|
148
|
+
});
|
|
149
|
+
return res.json();
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export const { GET, POST } = createNextYakHandler({
|
|
154
|
+
routes: [() => scanRoutes("./src/app"), marketingRoutes],
|
|
155
|
+
tools: [createTRPCToolAdapter(trpcConfig), graphqlTools],
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Behind the scenes `@yak-io/javascript` merges every `RouteSource` into a single manifest and wires each tool definition to the adapter that registered it.
|
|
160
|
+
|
|
161
|
+
## CLI: route manifest generator
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
yak-nextjs generate-manifest --app-dir ./src/app --output ./yak-routes.json
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Use the CLI when you prefer to pre-compute a manifest at build time. The JSON matches the runtime manifest structure exposed by the handlers.
|
|
168
|
+
|
|
169
|
+
## API surface (server)
|
|
170
|
+
|
|
171
|
+
`@yak-io/nextjs/server` exports:
|
|
172
|
+
|
|
173
|
+
- `scanRoutes(directory: string, options?: { directoryType?: "app" | "pages" })` – low-level filesystem scanner (useful for precomputing manifests or composing custom sources). Only captures page routes, extracting `title` and `description` from static metadata exports.
|
|
174
|
+
- `createNextYakHandler(config)` – unified GET + POST handler (wrapping `createYakHandler`). When `routes`/`getRoutes` are omitted it auto-scans `./src/app` and `./src/pages` (override with `appDir`/`pagesDir`, and narrow results via `routeFilter?: { include?: RegExp[]; exclude?: RegExp[] }`).
|
|
175
|
+
- `createNextYakConfigHandler(config)` – GET-only convenience wrapper.
|
|
176
|
+
- `createNextYakToolsHandler(config)` – POST-only wrapper.
|
|
177
|
+
- Re-exported types from `@yak-io/javascript/server` (RouteInfo, RouteManifest, ToolDefinition, ToolManifest, ToolExecutor, ChatConfig, RouteSourceInput, ToolSourceInput, etc.).
|
|
178
|
+
|
|
179
|
+
## Client props
|
|
180
|
+
|
|
181
|
+
`YakProvider` accepts:
|
|
182
|
+
|
|
183
|
+
| Prop | Type | Description |
|
|
184
|
+
| --- | --- | --- |
|
|
185
|
+
| `appId` | `string` | Yak app identifier |
|
|
186
|
+
| `getConfig` | `() => Promise<ChatConfig>` | Config provider (default fetches `/api/yak`) |
|
|
187
|
+
| `onToolCall` | `(name, args) => Promise<unknown>` | Tool call handler (default POSTs to `/api/yak`) |
|
|
188
|
+
| `theme` | `Theme` | Floating button + panel theme (`position`, `colorMode`, colors) |
|
|
189
|
+
| `onRedirect` | `(path: string) => void` | Custom navigation handler |
|
|
190
|
+
| `disableRestartButton` | `boolean` | Hide the restart session button in the header |
|
|
191
|
+
|
|
192
|
+
`YakWidget` renders the launcher + iframe shell. It accepts:
|
|
193
|
+
|
|
194
|
+
| Prop | Type | Description |
|
|
195
|
+
| --- | --- | --- |
|
|
196
|
+
| `iframeClassName` | `string` | Optional CSS class for the iframe |
|
|
197
|
+
| `triggerLabel` | `string` | Text to display next to the logo (default "Ask with AI") |
|
|
198
|
+
|
|
199
|
+
## Using `@yak-io/javascript` directly
|
|
200
|
+
|
|
201
|
+
If you need to integrate with Remix, SvelteKit, or a custom Node runtime you can:
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
import { createYakHandler } from "@yak-io/javascript/server";
|
|
205
|
+
|
|
206
|
+
export const { GET, POST } = createYakHandler({
|
|
207
|
+
routes: [{ id: "remix", getRoutes: listRoutes }],
|
|
208
|
+
tools: [{ id: "graphql", getTools, executeTool }],
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The React provider/widget are also exported from `@yak-io/react`.
|
|
213
|
+
|
|
214
|
+
## Migration notes
|
|
215
|
+
|
|
216
|
+
- Existing imports from `@yak-io/nextjs/client` continue to work, but the actual implementation lives in `@yak-io/react`.
|
|
217
|
+
- `createNextYakHandler` auto-scans `./src/app` and `./src/pages`; configure via `appDir`, `pagesDir`, or regex-based `routeFilter`.
|
|
218
|
+
- `createNextYakHandler` accepts `routes`/`tools` arrays. Legacy `getRoutes/getTools/executeTool` still work and are normalized internally.
|
|
219
|
+
- For custom adapters prefer using `@yak-io/javascript/server` types so your code can be reused outside Next.js.
|
|
220
|
+
|
|
221
|
+
## License
|
|
222
|
+
|
|
223
|
+
Proprietary - see LICENSE file
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Yak Next.js Route Manifest Generator
|
|
4
|
+
*
|
|
5
|
+
* This CLI tool scans a Next.js app directory and generates a manifest
|
|
6
|
+
* of all available page routes that can be injected into the chat context.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* yak-nextjs generate-manifest [options]
|
|
10
|
+
*
|
|
11
|
+
* Options:
|
|
12
|
+
* --app-dir <path> Path to Next.js app directory (default: ./app)
|
|
13
|
+
* --output <path> Output file path (default: ./yak-routes-manifest.json)
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=generate-manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-manifest.d.ts","sourceRoot":"","sources":["../../src/cli/generate-manifest.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;GAYG"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Yak Next.js Route Manifest Generator
|
|
4
|
+
*
|
|
5
|
+
* This CLI tool scans a Next.js app directory and generates a manifest
|
|
6
|
+
* of all available page routes that can be injected into the chat context.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* yak-nextjs generate-manifest [options]
|
|
10
|
+
*
|
|
11
|
+
* Options:
|
|
12
|
+
* --app-dir <path> Path to Next.js app directory (default: ./app)
|
|
13
|
+
* --output <path> Output file path (default: ./yak-routes-manifest.json)
|
|
14
|
+
*/
|
|
15
|
+
import * as fs from "node:fs";
|
|
16
|
+
import * as path from "node:path";
|
|
17
|
+
/**
|
|
18
|
+
* Extract static metadata (title and description) from a Next.js page file.
|
|
19
|
+
* Parses `export const metadata = { title: "...", description: "..." }` patterns.
|
|
20
|
+
* Does not support dynamic generateMetadata functions.
|
|
21
|
+
*/
|
|
22
|
+
function extractMetadata(filePath) {
|
|
23
|
+
try {
|
|
24
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
25
|
+
// Match `export const metadata` object
|
|
26
|
+
const metadataMatch = content.match(/export\s+const\s+metadata\s*(?::\s*Metadata\s*)?=\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}/s);
|
|
27
|
+
if (!metadataMatch) {
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
const metadataBlock = metadataMatch[1];
|
|
31
|
+
const result = {};
|
|
32
|
+
// Extract title - handle both single and double quotes
|
|
33
|
+
const titleMatch = metadataBlock?.match(/title\s*:\s*["'`]([^"'`]+)["'`]/);
|
|
34
|
+
if (titleMatch?.[1]) {
|
|
35
|
+
result.title = titleMatch[1];
|
|
36
|
+
}
|
|
37
|
+
// Extract description - handle both single and double quotes
|
|
38
|
+
const descMatch = metadataBlock?.match(/description\s*:\s*["'`]([^"'`]+)["'`]/);
|
|
39
|
+
if (descMatch?.[1]) {
|
|
40
|
+
result.description = descMatch[1];
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Check if a path segment is a route group (organizational only, not part of URL)
|
|
50
|
+
*/
|
|
51
|
+
function isRouteGroup(segment) {
|
|
52
|
+
return segment.startsWith("(") && segment.endsWith(")");
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Check if a path segment is an optional catch-all (e.g., [[...slug]])
|
|
56
|
+
* These segments match the base path without any additional segments
|
|
57
|
+
*/
|
|
58
|
+
function isOptionalCatchAll(segment) {
|
|
59
|
+
return segment.startsWith("[[...") && segment.endsWith("]]");
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Check if a path segment is a required catch-all (e.g., [...slug])
|
|
63
|
+
* These segments require at least one additional path segment
|
|
64
|
+
*/
|
|
65
|
+
function isRequiredCatchAll(segment) {
|
|
66
|
+
return segment.startsWith("[...") && segment.endsWith("]") && !segment.startsWith("[[");
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Check if a path segment is a dynamic segment (e.g., [id])
|
|
70
|
+
*/
|
|
71
|
+
function isDynamicSegment(segment) {
|
|
72
|
+
return segment.startsWith("[") && segment.endsWith("]") && !isOptionalCatchAll(segment) && !isRequiredCatchAll(segment);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Normalize a dynamic segment for display
|
|
76
|
+
* e.g., [id] → :id, [postId] → :postId
|
|
77
|
+
*/
|
|
78
|
+
function normalizeDynamicSegment(segment) {
|
|
79
|
+
// Extract the parameter name from [name] format
|
|
80
|
+
const paramName = segment.slice(1, -1);
|
|
81
|
+
return `:${paramName}`;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Normalize a route path for display (excludes route groups and handles dynamic segments)
|
|
85
|
+
*/
|
|
86
|
+
function normalizeRoutePath(segments) {
|
|
87
|
+
// Filter out route groups - they don't appear in URLs
|
|
88
|
+
// Filter out optional catch-all segments - they match the base path
|
|
89
|
+
const urlSegments = segments
|
|
90
|
+
.filter(seg => !isRouteGroup(seg) && !isOptionalCatchAll(seg))
|
|
91
|
+
.map(seg => {
|
|
92
|
+
if (isDynamicSegment(seg)) {
|
|
93
|
+
return normalizeDynamicSegment(seg);
|
|
94
|
+
}
|
|
95
|
+
if (isRequiredCatchAll(seg)) {
|
|
96
|
+
// For required catch-all, extract the name: [...slug] → :slug+
|
|
97
|
+
const paramName = seg.slice(4, -1);
|
|
98
|
+
return `:${paramName}+`;
|
|
99
|
+
}
|
|
100
|
+
return seg;
|
|
101
|
+
});
|
|
102
|
+
if (urlSegments.length === 0)
|
|
103
|
+
return "/";
|
|
104
|
+
return "/" + urlSegments.join("/");
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Check if file is a page file
|
|
108
|
+
*/
|
|
109
|
+
function isPageFile(filename) {
|
|
110
|
+
return filename === "page.tsx" || filename === "page.js";
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Recursively scan a directory for Next.js page routes
|
|
114
|
+
*/
|
|
115
|
+
function scanDirectory(dirPath, segments = []) {
|
|
116
|
+
const routes = [];
|
|
117
|
+
try {
|
|
118
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
119
|
+
for (const entry of entries) {
|
|
120
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
121
|
+
if (entry.isDirectory()) {
|
|
122
|
+
// Skip special Next.js directories and api routes
|
|
123
|
+
if (entry.name.startsWith("_") || entry.name === "api") {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
// Recursively scan subdirectories
|
|
127
|
+
routes.push(...scanDirectory(fullPath, [...segments, entry.name]));
|
|
128
|
+
}
|
|
129
|
+
else if (entry.isFile() && isPageFile(entry.name)) {
|
|
130
|
+
const metadata = extractMetadata(fullPath);
|
|
131
|
+
routes.push({
|
|
132
|
+
path: normalizeRoutePath(segments),
|
|
133
|
+
title: metadata.title,
|
|
134
|
+
description: metadata.description,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
console.error(`Error scanning directory ${dirPath}:`, error);
|
|
141
|
+
}
|
|
142
|
+
return routes;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Parse command-line arguments
|
|
146
|
+
*/
|
|
147
|
+
function parseArgs() {
|
|
148
|
+
const args = process.argv.slice(2);
|
|
149
|
+
let appDir = "./app";
|
|
150
|
+
let output = "./yak-routes-manifest.json";
|
|
151
|
+
let help = false;
|
|
152
|
+
for (let i = 0; i < args.length; i++) {
|
|
153
|
+
const arg = args[i];
|
|
154
|
+
if (arg === "--help" || arg === "-h") {
|
|
155
|
+
help = true;
|
|
156
|
+
}
|
|
157
|
+
else if (arg === "--app-dir" && args[i + 1]) {
|
|
158
|
+
appDir = args[i + 1];
|
|
159
|
+
i++;
|
|
160
|
+
}
|
|
161
|
+
else if (arg === "--output" && args[i + 1]) {
|
|
162
|
+
output = args[i + 1];
|
|
163
|
+
i++;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return { appDir, output, help };
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Display help message
|
|
170
|
+
*/
|
|
171
|
+
function showHelp() {
|
|
172
|
+
console.log(`
|
|
173
|
+
Yak Next.js Route Manifest Generator
|
|
174
|
+
|
|
175
|
+
Scans a Next.js app directory and generates a manifest of all available page routes.
|
|
176
|
+
|
|
177
|
+
Usage:
|
|
178
|
+
yak-nextjs generate-manifest [options]
|
|
179
|
+
|
|
180
|
+
Options:
|
|
181
|
+
--app-dir <path> Path to Next.js app directory (default: ./app)
|
|
182
|
+
--output <path> Output file path (default: ./yak-routes-manifest.json)
|
|
183
|
+
--help, -h Show this help message
|
|
184
|
+
|
|
185
|
+
Example:
|
|
186
|
+
yak-nextjs generate-manifest --app-dir ./src/app --output ./public/routes.json
|
|
187
|
+
`);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Main entry point
|
|
191
|
+
*/
|
|
192
|
+
function main() {
|
|
193
|
+
const { appDir, output, help } = parseArgs();
|
|
194
|
+
if (help) {
|
|
195
|
+
showHelp();
|
|
196
|
+
process.exit(0);
|
|
197
|
+
}
|
|
198
|
+
console.log("🔍 Scanning Next.js page routes...");
|
|
199
|
+
console.log(` App directory: ${appDir}`);
|
|
200
|
+
const appDirPath = path.resolve(process.cwd(), appDir);
|
|
201
|
+
if (!fs.existsSync(appDirPath)) {
|
|
202
|
+
console.error(`❌ Error: App directory not found: ${appDirPath}`);
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
const routes = scanDirectory(appDirPath, []);
|
|
206
|
+
const manifest = {
|
|
207
|
+
routes: routes.sort((a, b) => a.path.localeCompare(b.path)),
|
|
208
|
+
generated_at: new Date().toISOString(),
|
|
209
|
+
};
|
|
210
|
+
const outputPath = path.resolve(process.cwd(), output);
|
|
211
|
+
fs.writeFileSync(outputPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
212
|
+
console.log(`✅ Generated manifest with ${routes.length} page routes`);
|
|
213
|
+
console.log(` Output: ${outputPath}`);
|
|
214
|
+
}
|
|
215
|
+
// Run the CLI
|
|
216
|
+
main();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { type YakProviderProps as CoreYakProviderProps } from "@yak-io/react";
|
|
3
|
+
export type YakProviderProps = CoreYakProviderProps;
|
|
4
|
+
/**
|
|
5
|
+
* Next-aware YakProvider that falls back to client-side navigation
|
|
6
|
+
* and provides sensible defaults for getConfig and onToolCall.
|
|
7
|
+
*
|
|
8
|
+
* By default:
|
|
9
|
+
* - `getConfig` fetches from `/api/yak` (GET)
|
|
10
|
+
* - `onToolCall` POSTs to `/api/yak`
|
|
11
|
+
*
|
|
12
|
+
* These defaults assume you've set up the API handler via `createNextYakHandler`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function YakProvider(props: YakProviderProps): React.JSX.Element;
|
|
15
|
+
//# sourceMappingURL=YakProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"YakProvider.d.ts","sourceRoot":"","sources":["../../src/client/YakProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA+B,MAAM,OAAO,CAAC;AAEpD,OAAO,EAEL,KAAK,gBAAgB,IAAI,oBAAoB,EAC9C,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,gBAAgB,GAAG,oBAAoB,CAAC;AAkCpD;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAiCtE"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useMemo } from "react";
|
|
4
|
+
import { useRouter } from "next/navigation";
|
|
5
|
+
import { YakProvider as CoreYakProvider, } from "@yak-io/react";
|
|
6
|
+
function isAbsoluteUrl(path) {
|
|
7
|
+
return /^https?:\/\//i.test(path);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Default getConfig that fetches from /api/yak
|
|
11
|
+
*/
|
|
12
|
+
async function defaultGetConfig() {
|
|
13
|
+
const res = await fetch("/api/yak", { credentials: "include" });
|
|
14
|
+
if (!res.ok) {
|
|
15
|
+
throw new Error(`Failed to fetch config: ${res.status}`);
|
|
16
|
+
}
|
|
17
|
+
return res.json();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Default onToolCall that POSTs to /api/yak
|
|
21
|
+
*/
|
|
22
|
+
async function defaultOnToolCall(name, args) {
|
|
23
|
+
const res = await fetch("/api/yak", {
|
|
24
|
+
method: "POST",
|
|
25
|
+
headers: { "Content-Type": "application/json" },
|
|
26
|
+
credentials: "include",
|
|
27
|
+
body: JSON.stringify({ name, args }),
|
|
28
|
+
});
|
|
29
|
+
const data = await res.json();
|
|
30
|
+
if (!data.ok) {
|
|
31
|
+
throw new Error(data.error ?? "Tool execution failed");
|
|
32
|
+
}
|
|
33
|
+
return data.result;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Next-aware YakProvider that falls back to client-side navigation
|
|
37
|
+
* and provides sensible defaults for getConfig and onToolCall.
|
|
38
|
+
*
|
|
39
|
+
* By default:
|
|
40
|
+
* - `getConfig` fetches from `/api/yak` (GET)
|
|
41
|
+
* - `onToolCall` POSTs to `/api/yak`
|
|
42
|
+
*
|
|
43
|
+
* These defaults assume you've set up the API handler via `createNextYakHandler`.
|
|
44
|
+
*/
|
|
45
|
+
export function YakProvider(props) {
|
|
46
|
+
const router = useRouter();
|
|
47
|
+
const handleRedirect = useCallback((path) => {
|
|
48
|
+
if (isAbsoluteUrl(path)) {
|
|
49
|
+
window.location.assign(path);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
router.push(path);
|
|
53
|
+
}, [router]);
|
|
54
|
+
const getConfig = useMemo(() => props.getConfig ?? defaultGetConfig, [props.getConfig]);
|
|
55
|
+
const onToolCall = useMemo(() => props.onToolCall ?? defaultOnToolCall, [props.onToolCall]);
|
|
56
|
+
return (_jsx(CoreYakProvider, { ...props, getConfig: getConfig, onToolCall: onToolCall, onRedirect: props.onRedirect ?? handleRedirect }));
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useYak.d.ts","sourceRoot":"","sources":["../../src/client/useYak.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAGvC,OAAO,EAAE,MAAM,EAAE,CAAC;AAElB,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from "@yak-io/react";
|
|
2
|
+
export { YakProvider } from "./client/YakProvider.js";
|
|
3
|
+
export type { YakProviderProps } from "./client/YakProvider.js";
|
|
4
|
+
export { useYak } from "./client/useYak.js";
|
|
5
|
+
export type { YakChatAPI } from "./client/useYak.js";
|
|
6
|
+
//# sourceMappingURL=index.client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.client.d.ts","sourceRoot":"","sources":["../src/index.client.ts"],"names":[],"mappings":"AAEA,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,YAAY,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.server.d.ts","sourceRoot":"","sources":["../src/index.server.ts"],"names":[],"mappings":"AACA,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple logger utility - no-op by default
|
|
3
|
+
* Can be replaced with proper logging later
|
|
4
|
+
*/
|
|
5
|
+
export declare const logger: {
|
|
6
|
+
debug: (message: string, ...args: unknown[]) => void;
|
|
7
|
+
info: (message: string, ...args: unknown[]) => void;
|
|
8
|
+
warn: (message: string, ...args: unknown[]) => void;
|
|
9
|
+
error: (message: string, ...args: unknown[]) => void;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/internal/logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,MAAM;qBACA,MAAM,WAAW,OAAO,EAAE,KAAG,IAAI;oBAOlC,MAAM,WAAW,OAAO,EAAE,KAAG,IAAI;oBAMjC,MAAM,WAAW,OAAO,EAAE,KAAG,IAAI;qBAIhC,MAAM,WAAW,OAAO,EAAE,KAAG,IAAI;CAGnD,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple logger utility - no-op by default
|
|
3
|
+
* Can be replaced with proper logging later
|
|
4
|
+
*/
|
|
5
|
+
export const logger = {
|
|
6
|
+
debug: (message, ...args) => {
|
|
7
|
+
// No-op in production, could log in development
|
|
8
|
+
if (process.env.NODE_ENV === "development") {
|
|
9
|
+
console.debug(`[yak/nextjs] ${message}`, ...args);
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
info: (message, ...args) => {
|
|
13
|
+
if (process.env.NODE_ENV === "development") {
|
|
14
|
+
console.info(`[yak/nextjs] ${message}`, ...args);
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
warn: (message, ...args) => {
|
|
18
|
+
console.warn(`[yak/nextjs] ${message}`, ...args);
|
|
19
|
+
},
|
|
20
|
+
error: (message, ...args) => {
|
|
21
|
+
console.error(`[yak/nextjs] ${message}`, ...args);
|
|
22
|
+
},
|
|
23
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createNextYakConfigHandler.d.ts","sourceRoot":"","sources":["../../src/server/createNextYakConfigHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACvE,YAAY,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createNextYakConfigHandler } from "./createNextYakHandler.js";
|