@yak-io/nextjs 0.1.1 → 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
CHANGED
|
@@ -164,7 +164,7 @@ 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`)
|
|
@@ -197,19 +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 ./public/routes.json"
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
The handler checks these locations automatically:
|
|
211
|
-
1. `./yak-routes-manifest.json`
|
|
212
|
-
2. `./public/yak-routes-manifest.json`
|
|
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.
|
|
213
203
|
|
|
214
204
|
### Alternative: explicit routes
|
|
215
205
|
|
|
@@ -224,24 +214,14 @@ export const { GET, POST } = createNextYakHandler({
|
|
|
224
214
|
});
|
|
225
215
|
```
|
|
226
216
|
|
|
227
|
-
### Loading a custom manifest location
|
|
228
|
-
|
|
229
|
-
```ts
|
|
230
|
-
import { createNextYakHandler, loadRoutes } from "@yak-io/nextjs/server";
|
|
231
|
-
|
|
232
|
-
export const { GET, POST } = createNextYakHandler({
|
|
233
|
-
routes: loadRoutes("./custom/manifest.json"),
|
|
234
|
-
});
|
|
235
|
-
```
|
|
236
|
-
|
|
237
217
|
## API surface (server)
|
|
238
218
|
|
|
239
219
|
`@yak-io/nextjs/server` exports:
|
|
240
220
|
|
|
241
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.
|
|
242
|
-
- `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.
|
|
243
223
|
- `loadRoutes(path?: string)` – load routes from a manifest, throws with helpful error if not found.
|
|
244
|
-
- `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.
|
|
245
225
|
- `createNextYakConfigHandler(config)` – GET-only convenience wrapper.
|
|
246
226
|
- `createNextYakToolsHandler(config)` – POST-only wrapper.
|
|
247
227
|
- Re-exported types from `@yak-io/javascript/server` (RouteInfo, RouteManifest, ToolDefinition, ToolManifest, ToolExecutor, ChatConfig, RouteSourceInput, ToolSourceInput, etc.).
|
|
@@ -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,12 +1,11 @@
|
|
|
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
|
-
* yak-nextjs generate-manifest
|
|
8
|
+
* yak-nextjs generate-manifest
|
|
10
9
|
*
|
|
11
10
|
* @param manifestPath - Path to the manifest JSON file (default: tries common locations)
|
|
12
11
|
* @returns Route manifest or null if not found
|
|
@@ -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,20 +3,24 @@ 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
|
|
6
|
+
* Default paths to check for pre-built route manifests (JSON files).
|
|
7
|
+
* These are read via fs.readFileSync at runtime.
|
|
7
8
|
*/
|
|
8
9
|
const DEFAULT_MANIFEST_PATHS = [
|
|
9
|
-
"./yak-routes-manifest.json",
|
|
10
10
|
"./public/yak-routes-manifest.json",
|
|
11
|
+
"./yak-routes-manifest.json",
|
|
11
12
|
];
|
|
12
13
|
/**
|
|
13
|
-
*
|
|
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).
|
|
14
19
|
*
|
|
15
|
-
* This
|
|
16
|
-
* is not available (e.g., Vercel serverless functions).
|
|
20
|
+
* This works in traditional Node.js deployments where the filesystem is available.
|
|
17
21
|
*
|
|
18
22
|
* Generate the manifest at build time using:
|
|
19
|
-
* yak-nextjs generate-manifest
|
|
23
|
+
* yak-nextjs generate-manifest
|
|
20
24
|
*
|
|
21
25
|
* @param manifestPath - Path to the manifest JSON file (default: tries common locations)
|
|
22
26
|
* @returns Route manifest or null if not found
|
|
@@ -37,6 +41,59 @@ export function loadRouteManifest(manifestPath) {
|
|
|
37
41
|
}
|
|
38
42
|
return null;
|
|
39
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
|
+
}
|
|
40
97
|
/**
|
|
41
98
|
* Load routes from a pre-built manifest.
|
|
42
99
|
* Throws if manifest is not found.
|
|
@@ -44,11 +101,13 @@ export function loadRouteManifest(manifestPath) {
|
|
|
44
101
|
export function loadRoutes(manifestPath) {
|
|
45
102
|
const manifest = loadRouteManifest(manifestPath);
|
|
46
103
|
if (!manifest) {
|
|
47
|
-
const pathsChecked = manifestPath
|
|
104
|
+
const pathsChecked = manifestPath
|
|
105
|
+
? manifestPath
|
|
106
|
+
: [...DEFAULT_MANIFEST_PATHS].join(", ");
|
|
48
107
|
throw new Error(`Route manifest not found. Checked: ${pathsChecked}\n\n` +
|
|
49
108
|
`In production environments (like Vercel), route scanning requires a pre-built manifest.\n` +
|
|
50
109
|
`Generate it at build time by adding to your build script:\n\n` +
|
|
51
|
-
` yak-nextjs generate-manifest
|
|
110
|
+
` yak-nextjs generate-manifest\n\n` +
|
|
52
111
|
`Or provide routes explicitly in your handler configuration.`);
|
|
53
112
|
}
|
|
54
113
|
return manifest.routes;
|
|
@@ -63,30 +122,36 @@ export function createNextYakHandler(config) {
|
|
|
63
122
|
});
|
|
64
123
|
}
|
|
65
124
|
/**
|
|
66
|
-
* Attempt to
|
|
125
|
+
* Attempt to load routes from filesystem or fetch from public URL.
|
|
67
126
|
*
|
|
68
127
|
* In development: scans the app directory directly
|
|
69
|
-
* 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
|
|
70
129
|
*/
|
|
71
|
-
function tryLoadRoutes(appDir) {
|
|
130
|
+
async function tryLoadRoutes(appDir) {
|
|
72
131
|
const targetDir = path.resolve(process.cwd(), appDir);
|
|
73
132
|
// If the app directory exists, scan it directly (development mode)
|
|
74
133
|
if (fs.existsSync(targetDir)) {
|
|
75
134
|
return scanRoutes(appDir);
|
|
76
135
|
}
|
|
77
|
-
// 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
|
|
78
137
|
const manifest = loadRouteManifest();
|
|
79
138
|
if (manifest) {
|
|
80
139
|
return manifest.routes;
|
|
81
140
|
}
|
|
141
|
+
// Try fetching from public URL (works on Vercel/serverless)
|
|
142
|
+
const fetchedManifest = await fetchRouteManifest();
|
|
143
|
+
if (fetchedManifest) {
|
|
144
|
+
return fetchedManifest.routes;
|
|
145
|
+
}
|
|
82
146
|
// Neither source nor manifest available - provide helpful error
|
|
83
147
|
throw new Error(`App directory not found: ${targetDir}\n\n` +
|
|
84
148
|
`This typically happens in production (Vercel, etc.) where source files aren't deployed.\n\n` +
|
|
85
149
|
`Solutions:\n` +
|
|
86
|
-
`1. Generate a route manifest at build time:\n` +
|
|
87
|
-
` 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` +
|
|
88
153
|
`2. Provide routes explicitly:\n` +
|
|
89
|
-
` createNextYakHandler({ routes: [...] })\n\n` +
|
|
154
|
+
` createNextYakHandler({ routes: [{ path: "/", title: "Home" }, ...] })\n\n` +
|
|
90
155
|
`3. Use getRoutes callback:\n` +
|
|
91
156
|
` createNextYakHandler({ getRoutes: async () => [...] })`);
|
|
92
157
|
}
|
|
@@ -97,7 +162,10 @@ function resolveRouteSources(config) {
|
|
|
97
162
|
if (config.getRoutes) {
|
|
98
163
|
return config.getRoutes;
|
|
99
164
|
}
|
|
100
|
-
return async () =>
|
|
165
|
+
return async () => {
|
|
166
|
+
const routes = await tryLoadRoutes(config.appDir ?? "./src/app");
|
|
167
|
+
return applyRouteFilters(routes, config.routeFilter);
|
|
168
|
+
};
|
|
101
169
|
}
|
|
102
170
|
function applyRouteFilters(routes, filter) {
|
|
103
171
|
if (!filter) {
|