@yak-io/nextjs 0.1.2 → 0.1.3
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/README.md +6 -29
- package/dist/cli/generate-manifest.d.ts +1 -1
- package/dist/cli/generate-manifest.js +4 -3
- package/dist/server/createNextYakHandler.d.ts +2 -3
- package/dist/server/createNextYakHandler.d.ts.map +1 -1
- package/dist/server/createNextYakHandler.js +82 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -164,12 +164,12 @@ Behind the scenes `@yak-io/javascript` merges every `RouteSource` into a single
|
|
|
164
164
|
yak-nextjs generate-manifest
|
|
165
165
|
```
|
|
166
166
|
|
|
167
|
-
Use the CLI
|
|
167
|
+
Use the CLI to pre-compute a manifest at build time. The JSON matches the runtime manifest structure exposed by the handlers.
|
|
168
168
|
|
|
169
169
|
**Options:**
|
|
170
170
|
- `--app-dir <path>` – Path to Next.js app directory (default: `./src/app`)
|
|
171
171
|
- `--pages-dir <path>` – Path to Next.js pages directory (optional, scanned in addition to app-dir)
|
|
172
|
-
- `--output <path>` – Output file path (default: `./yak-routes-manifest.json`)
|
|
172
|
+
- `--output <path>` – Output file path (default: `./public/yak-routes-manifest.json`)
|
|
173
173
|
|
|
174
174
|
**Examples:**
|
|
175
175
|
```bash
|
|
@@ -197,22 +197,9 @@ Add a `prebuild` script to generate the manifest before Next.js builds:
|
|
|
197
197
|
}
|
|
198
198
|
```
|
|
199
199
|
|
|
200
|
-
|
|
200
|
+
That's it! The manifest is generated to `./public/yak-routes-manifest.json` and the handler automatically fetches it at runtime on serverless platforms like Vercel. No code changes needed.
|
|
201
201
|
|
|
202
|
-
|
|
203
|
-
{
|
|
204
|
-
"scripts": {
|
|
205
|
-
"prebuild": "yak-nextjs generate-manifest --app-dir ./app --output ./routes.json"
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
> **Important:** The manifest must be in the project root (not `public/`). On Vercel and other serverless platforms, files in `public/` are served via CDN and are not available on the function's filesystem.
|
|
211
|
-
|
|
212
|
-
The handler checks these locations automatically:
|
|
213
|
-
1. `./yak-routes-manifest.json`
|
|
214
|
-
2. `./.next/yak-routes-manifest.json`
|
|
215
|
-
3. `./public/yak-routes-manifest.json` (only works in non-serverless environments)
|
|
202
|
+
The handler automatically detects the deployment environment (Vercel via `VERCEL_URL`, Netlify via `URL`, or custom `NEXT_PUBLIC_BASE_URL`) and fetches the manifest from the public folder.
|
|
216
203
|
|
|
217
204
|
### Alternative: explicit routes
|
|
218
205
|
|
|
@@ -227,24 +214,14 @@ export const { GET, POST } = createNextYakHandler({
|
|
|
227
214
|
});
|
|
228
215
|
```
|
|
229
216
|
|
|
230
|
-
### Loading a custom manifest location
|
|
231
|
-
|
|
232
|
-
```ts
|
|
233
|
-
import { createNextYakHandler, loadRoutes } from "@yak-io/nextjs/server";
|
|
234
|
-
|
|
235
|
-
export const { GET, POST } = createNextYakHandler({
|
|
236
|
-
routes: loadRoutes("./custom/manifest.json"),
|
|
237
|
-
});
|
|
238
|
-
```
|
|
239
|
-
|
|
240
217
|
## API surface (server)
|
|
241
218
|
|
|
242
219
|
`@yak-io/nextjs/server` exports:
|
|
243
220
|
|
|
244
221
|
- `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.
|
|
245
|
-
- `loadRouteManifest(path?: string)` – load a pre-built JSON manifest, returns `null` if not found.
|
|
222
|
+
- `loadRouteManifest(path?: string)` – load a pre-built JSON manifest via filesystem read, returns `null` if not found.
|
|
246
223
|
- `loadRoutes(path?: string)` – load routes from a manifest, throws with helpful error if not found.
|
|
247
|
-
- `createNextYakHandler(config)` – unified GET + POST handler
|
|
224
|
+
- `createNextYakHandler(config)` – unified GET + POST handler. When `routes`/`getRoutes` are omitted it auto-scans `./src/app` (override with `appDir`). In production, automatically fetches manifest from public folder.
|
|
248
225
|
- `createNextYakConfigHandler(config)` – GET-only convenience wrapper.
|
|
249
226
|
- `createNextYakToolsHandler(config)` – POST-only wrapper.
|
|
250
227
|
- Re-exported types from `@yak-io/javascript/server` (RouteInfo, RouteManifest, ToolDefinition, ToolManifest, ToolExecutor, ChatConfig, RouteSourceInput, ToolSourceInput, etc.).
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* Options:
|
|
12
12
|
* --app-dir <path> Path to Next.js app directory (default: ./src/app)
|
|
13
13
|
* --pages-dir <path> Path to Next.js pages directory (optional, scanned in addition to app-dir)
|
|
14
|
-
* --output <path> Output file path (default: ./yak-routes-manifest.json)
|
|
14
|
+
* --output <path> Output file path (default: ./public/yak-routes-manifest.json)
|
|
15
15
|
*/
|
|
16
16
|
export {};
|
|
17
17
|
//# sourceMappingURL=generate-manifest.d.ts.map
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* Options:
|
|
12
12
|
* --app-dir <path> Path to Next.js app directory (default: ./src/app)
|
|
13
13
|
* --pages-dir <path> Path to Next.js pages directory (optional, scanned in addition to app-dir)
|
|
14
|
-
* --output <path> Output file path (default: ./yak-routes-manifest.json)
|
|
14
|
+
* --output <path> Output file path (default: ./public/yak-routes-manifest.json)
|
|
15
15
|
*/
|
|
16
16
|
import * as fs from "node:fs";
|
|
17
17
|
import * as path from "node:path";
|
|
@@ -201,7 +201,7 @@ function parseArgs() {
|
|
|
201
201
|
const args = process.argv.slice(2);
|
|
202
202
|
let appDir = "./src/app";
|
|
203
203
|
let pagesDir = undefined;
|
|
204
|
-
let output = "./yak-routes-manifest.json";
|
|
204
|
+
let output = "./public/yak-routes-manifest.json";
|
|
205
205
|
let help = false;
|
|
206
206
|
for (let i = 0; i < args.length; i++) {
|
|
207
207
|
const arg = args[i];
|
|
@@ -238,7 +238,7 @@ Usage:
|
|
|
238
238
|
Options:
|
|
239
239
|
--app-dir <path> Path to Next.js app directory (default: ./src/app)
|
|
240
240
|
--pages-dir <path> Path to Next.js pages directory (optional)
|
|
241
|
-
--output <path> Output file path (default: ./yak-routes-manifest.json)
|
|
241
|
+
--output <path> Output file path (default: ./public/yak-routes-manifest.json)
|
|
242
242
|
--help, -h Show this help message
|
|
243
243
|
|
|
244
244
|
Examples:
|
|
@@ -290,6 +290,7 @@ function main() {
|
|
|
290
290
|
generated_at: new Date().toISOString(),
|
|
291
291
|
};
|
|
292
292
|
const outputPath = path.resolve(process.cwd(), output);
|
|
293
|
+
// Write JSON manifest
|
|
293
294
|
fs.writeFileSync(outputPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
294
295
|
console.log(`✅ Generated manifest with ${uniqueRoutes.length} page routes`);
|
|
295
296
|
console.log(` Output: ${outputPath}`);
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { RouteSourceInput, ToolSourceInput, RouteInfo, RouteManifest, ToolManifest, ToolExecutor } from "@yak-io/javascript/server";
|
|
2
2
|
/**
|
|
3
|
-
* Load a pre-built route manifest from disk.
|
|
3
|
+
* Load a pre-built route manifest from disk (JSON file).
|
|
4
4
|
*
|
|
5
|
-
* This
|
|
6
|
-
* is not available (e.g., Vercel serverless functions).
|
|
5
|
+
* This works in traditional Node.js deployments where the filesystem is available.
|
|
7
6
|
*
|
|
8
7
|
* Generate the manifest at build time using:
|
|
9
8
|
* yak-nextjs generate-manifest
|
|
@@ -1 +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,aAAa,EACb,YAAY,EACZ,YAAY,EACb,MAAM,2BAA2B,CAAC;
|
|
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,aAAa,EACb,YAAY,EACZ,YAAY,EACb,MAAM,2BAA2B,CAAC;AAiBnC;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAgB7E;AAgED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE,CAe7D;AAED,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;AAmGD,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"}
|
|
@@ -3,22 +3,21 @@ import * as fs from "node:fs";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { scanRoutes } from "./scan-routes.js";
|
|
5
5
|
/**
|
|
6
|
-
* Default paths to check for pre-built route manifests.
|
|
7
|
-
*
|
|
8
|
-
* On Vercel/serverless, files in `public/` are served via CDN and NOT available
|
|
9
|
-
* on the function's filesystem. The manifest must be in the project root or
|
|
10
|
-
* another location that gets bundled with the serverless function.
|
|
6
|
+
* Default paths to check for pre-built route manifests (JSON files).
|
|
7
|
+
* These are read via fs.readFileSync at runtime.
|
|
11
8
|
*/
|
|
12
9
|
const DEFAULT_MANIFEST_PATHS = [
|
|
10
|
+
"./public/yak-routes-manifest.json",
|
|
13
11
|
"./yak-routes-manifest.json",
|
|
14
|
-
"./.next/yak-routes-manifest.json",
|
|
15
|
-
"./public/yak-routes-manifest.json", // Only works in non-serverless environments
|
|
16
12
|
];
|
|
17
13
|
/**
|
|
18
|
-
*
|
|
14
|
+
* Public URL path where the manifest is served from (when in public/).
|
|
15
|
+
*/
|
|
16
|
+
const PUBLIC_MANIFEST_URL = "/yak-routes-manifest.json";
|
|
17
|
+
/**
|
|
18
|
+
* Load a pre-built route manifest from disk (JSON file).
|
|
19
19
|
*
|
|
20
|
-
* This
|
|
21
|
-
* is not available (e.g., Vercel serverless functions).
|
|
20
|
+
* This works in traditional Node.js deployments where the filesystem is available.
|
|
22
21
|
*
|
|
23
22
|
* Generate the manifest at build time using:
|
|
24
23
|
* yak-nextjs generate-manifest
|
|
@@ -42,6 +41,59 @@ export function loadRouteManifest(manifestPath) {
|
|
|
42
41
|
}
|
|
43
42
|
return null;
|
|
44
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Fetch the route manifest from the public URL.
|
|
46
|
+
* Used in serverless environments where filesystem access is not available.
|
|
47
|
+
*/
|
|
48
|
+
async function fetchRouteManifest() {
|
|
49
|
+
// Try to determine the base URL for fetching
|
|
50
|
+
const baseUrl = getBaseUrl();
|
|
51
|
+
if (!baseUrl) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const url = new URL(PUBLIC_MANIFEST_URL, baseUrl);
|
|
56
|
+
const response = await fetch(url.toString(), {
|
|
57
|
+
headers: { "Accept": "application/json" },
|
|
58
|
+
cache: "no-store",
|
|
59
|
+
});
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const manifest = await response.json();
|
|
64
|
+
if (manifest?.routes) {
|
|
65
|
+
return manifest;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Fetch failed
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get the base URL for the current deployment.
|
|
75
|
+
* Supports Vercel, Netlify, and custom configurations.
|
|
76
|
+
*/
|
|
77
|
+
function getBaseUrl() {
|
|
78
|
+
// Vercel provides VERCEL_URL (without protocol)
|
|
79
|
+
if (process.env.VERCEL_URL) {
|
|
80
|
+
const protocol = process.env.VERCEL_ENV === "development" ? "http" : "https";
|
|
81
|
+
return `${protocol}://${process.env.VERCEL_URL}`;
|
|
82
|
+
}
|
|
83
|
+
// Netlify provides URL
|
|
84
|
+
if (process.env.URL) {
|
|
85
|
+
return process.env.URL;
|
|
86
|
+
}
|
|
87
|
+
// Custom base URL
|
|
88
|
+
if (process.env.NEXT_PUBLIC_BASE_URL) {
|
|
89
|
+
return process.env.NEXT_PUBLIC_BASE_URL;
|
|
90
|
+
}
|
|
91
|
+
// For local development
|
|
92
|
+
if (process.env.NODE_ENV === "development") {
|
|
93
|
+
return "http://localhost:3000";
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
45
97
|
/**
|
|
46
98
|
* Load routes from a pre-built manifest.
|
|
47
99
|
* Throws if manifest is not found.
|
|
@@ -49,7 +101,9 @@ export function loadRouteManifest(manifestPath) {
|
|
|
49
101
|
export function loadRoutes(manifestPath) {
|
|
50
102
|
const manifest = loadRouteManifest(manifestPath);
|
|
51
103
|
if (!manifest) {
|
|
52
|
-
const pathsChecked = manifestPath
|
|
104
|
+
const pathsChecked = manifestPath
|
|
105
|
+
? manifestPath
|
|
106
|
+
: [...DEFAULT_MANIFEST_PATHS].join(", ");
|
|
53
107
|
throw new Error(`Route manifest not found. Checked: ${pathsChecked}\n\n` +
|
|
54
108
|
`In production environments (like Vercel), route scanning requires a pre-built manifest.\n` +
|
|
55
109
|
`Generate it at build time by adding to your build script:\n\n` +
|
|
@@ -68,30 +122,36 @@ export function createNextYakHandler(config) {
|
|
|
68
122
|
});
|
|
69
123
|
}
|
|
70
124
|
/**
|
|
71
|
-
* Attempt to
|
|
125
|
+
* Attempt to load routes from filesystem or fetch from public URL.
|
|
72
126
|
*
|
|
73
127
|
* In development: scans the app directory directly
|
|
74
|
-
* In production (Vercel, etc.): falls back to pre-built manifest
|
|
128
|
+
* In production (Vercel, etc.): falls back to pre-built manifest via filesystem or HTTP fetch
|
|
75
129
|
*/
|
|
76
|
-
function tryLoadRoutes(appDir) {
|
|
130
|
+
async function tryLoadRoutes(appDir) {
|
|
77
131
|
const targetDir = path.resolve(process.cwd(), appDir);
|
|
78
132
|
// If the app directory exists, scan it directly (development mode)
|
|
79
133
|
if (fs.existsSync(targetDir)) {
|
|
80
134
|
return scanRoutes(appDir);
|
|
81
135
|
}
|
|
82
|
-
// In production, the source directory doesn't exist - try loading from manifest
|
|
136
|
+
// In production, the source directory doesn't exist - try loading from manifest file
|
|
83
137
|
const manifest = loadRouteManifest();
|
|
84
138
|
if (manifest) {
|
|
85
139
|
return manifest.routes;
|
|
86
140
|
}
|
|
141
|
+
// Try fetching from public URL (works on Vercel/serverless)
|
|
142
|
+
const fetchedManifest = await fetchRouteManifest();
|
|
143
|
+
if (fetchedManifest) {
|
|
144
|
+
return fetchedManifest.routes;
|
|
145
|
+
}
|
|
87
146
|
// Neither source nor manifest available - provide helpful error
|
|
88
147
|
throw new Error(`App directory not found: ${targetDir}\n\n` +
|
|
89
148
|
`This typically happens in production (Vercel, etc.) where source files aren't deployed.\n\n` +
|
|
90
149
|
`Solutions:\n` +
|
|
91
|
-
`1. Generate a route manifest at build time:\n` +
|
|
92
|
-
` Add to
|
|
150
|
+
`1. Generate a route manifest at build time (recommended):\n` +
|
|
151
|
+
` Add to package.json: "prebuild": "yak-nextjs generate-manifest"\n` +
|
|
152
|
+
` The manifest will be placed in public/ and automatically loaded.\n\n` +
|
|
93
153
|
`2. Provide routes explicitly:\n` +
|
|
94
|
-
` createNextYakHandler({ routes: [...] })\n\n` +
|
|
154
|
+
` createNextYakHandler({ routes: [{ path: "/", title: "Home" }, ...] })\n\n` +
|
|
95
155
|
`3. Use getRoutes callback:\n` +
|
|
96
156
|
` createNextYakHandler({ getRoutes: async () => [...] })`);
|
|
97
157
|
}
|
|
@@ -102,7 +162,10 @@ function resolveRouteSources(config) {
|
|
|
102
162
|
if (config.getRoutes) {
|
|
103
163
|
return config.getRoutes;
|
|
104
164
|
}
|
|
105
|
-
return async () =>
|
|
165
|
+
return async () => {
|
|
166
|
+
const routes = await tryLoadRoutes(config.appDir ?? "./src/app");
|
|
167
|
+
return applyRouteFilters(routes, config.routeFilter);
|
|
168
|
+
};
|
|
106
169
|
}
|
|
107
170
|
function applyRouteFilters(routes, filter) {
|
|
108
171
|
if (!filter) {
|