@farm.js/plugin 0.1.0-beta.0 → 0.1.0-beta.10

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.
Files changed (46) hide show
  1. package/package.json +14 -7
  2. package/dist/api/index.d.ts +0 -42
  3. package/dist/api/index.d.ts.map +0 -1
  4. package/dist/api/index.js +0 -567
  5. package/dist/context/index.d.ts +0 -61
  6. package/dist/context/index.d.ts.map +0 -1
  7. package/dist/context/index.js +0 -75
  8. package/dist/index.d.ts +0 -43
  9. package/dist/index.d.ts.map +0 -1
  10. package/dist/index.js +0 -49
  11. package/dist/middleware/index.d.ts +0 -97
  12. package/dist/middleware/index.d.ts.map +0 -1
  13. package/dist/middleware/index.js +0 -469
  14. package/dist/observability/index.d.ts +0 -190
  15. package/dist/observability/index.d.ts.map +0 -1
  16. package/dist/observability/index.js +0 -399
  17. package/dist/rsc/build-paths.d.ts +0 -3
  18. package/dist/rsc/build-paths.d.ts.map +0 -1
  19. package/dist/rsc/build-paths.js +0 -8
  20. package/dist/rsc/entries/client.d.ts +0 -14
  21. package/dist/rsc/entries/client.d.ts.map +0 -1
  22. package/dist/rsc/entries/client.js +0 -283
  23. package/dist/rsc/entries/rsc.d.ts +0 -13
  24. package/dist/rsc/entries/rsc.d.ts.map +0 -1
  25. package/dist/rsc/entries/rsc.js +0 -932
  26. package/dist/rsc/entries/ssr.d.ts +0 -13
  27. package/dist/rsc/entries/ssr.d.ts.map +0 -1
  28. package/dist/rsc/entries/ssr.js +0 -245
  29. package/dist/rsc/index.d.ts +0 -78
  30. package/dist/rsc/index.d.ts.map +0 -1
  31. package/dist/rsc/index.js +0 -1368
  32. package/dist/rsc/nitro-build.d.ts +0 -36
  33. package/dist/rsc/nitro-build.d.ts.map +0 -1
  34. package/dist/rsc/nitro-build.js +0 -396
  35. package/dist/rsc/optimized-boundary.d.ts +0 -20
  36. package/dist/rsc/optimized-boundary.d.ts.map +0 -1
  37. package/dist/rsc/optimized-boundary.js +0 -15
  38. package/dist/rsc/server-fn-transform.d.ts +0 -6
  39. package/dist/rsc/server-fn-transform.d.ts.map +0 -1
  40. package/dist/rsc/server-fn-transform.js +0 -152
  41. package/dist/rsc/types.d.ts +0 -123
  42. package/dist/rsc/types.d.ts.map +0 -1
  43. package/dist/rsc/types.js +0 -1
  44. package/dist/rsc/vite-plugin-nitro.d.ts +0 -33
  45. package/dist/rsc/vite-plugin-nitro.d.ts.map +0 -1
  46. package/dist/rsc/vite-plugin-nitro.js +0 -163
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farm.js/plugin",
3
- "version": "0.1.0-beta.0",
3
+ "version": "0.1.0-beta.10",
4
4
  "description": "Official plugins for Farm.js framework",
5
5
  "license": "MIT",
6
6
  "author": "Farm.js Team",
@@ -61,28 +61,35 @@
61
61
  "nitro": "3.0.1-alpha.0",
62
62
  "picocolors": "^1.0.0",
63
63
  "rsc-html-stream": "^0.0.4",
64
- "@farm.js/core": "0.1.0-beta.0"
64
+ "@farm.js/core": "0.1.0-beta.10"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@types/node": "^20.19.21",
68
68
  "@types/react": "^19.0.0",
69
69
  "@types/react-dom": "^19.0.0",
70
- "@vitejs/plugin-rsc": "^0.5.18",
70
+ "@vitejs/plugin-rsc": "0.5.32",
71
+ "react": "19.2.8",
72
+ "react-dom": "19.2.8",
73
+ "react-server-dom-webpack": "19.2.8",
71
74
  "tsdown": "^0.15.7",
72
75
  "typescript": "^5.5.3",
73
76
  "vite": "^6.0.0",
74
77
  "vitest": "^1.1.0"
75
78
  },
76
79
  "peerDependencies": {
77
- "@vitejs/plugin-rsc": ">=0.5.0",
78
- "react": ">=19.0.0",
79
- "react-dom": ">=19.0.0",
80
+ "@vitejs/plugin-rsc": "0.5.32",
81
+ "react": "19.2.8",
82
+ "react-dom": "19.2.8",
83
+ "react-server-dom-webpack": "19.2.8",
80
84
  "vite": ">=6.0.0",
81
- "@farm.js/core": "0.1.0-beta.0"
85
+ "@farm.js/core": "0.1.0-beta.10"
82
86
  },
83
87
  "peerDependenciesMeta": {
84
88
  "@vitejs/plugin-rsc": {
85
89
  "optional": true
90
+ },
91
+ "react-server-dom-webpack": {
92
+ "optional": true
86
93
  }
87
94
  },
88
95
  "scripts": {
@@ -1,42 +0,0 @@
1
- /**
2
- * Farm.js API Routes Plugin
3
- *
4
- * Standalone API route support that works with or without RSC.
5
- *
6
- * Supports two patterns:
7
- * 1. File-based routing: /src/api/hello/route.ts -> /api/hello
8
- * The path is auto-inferred from file location
9
- *
10
- * 2. Root routes.ts file: /src/routes.ts with explicit paths
11
- * createEndpoint('/api/custom', { method: 'GET' }, handler)
12
- *
13
- * Uses better-call's createRouter for proper Zod validation and parsing.
14
- *
15
- * @example
16
- * ```ts
17
- * import { farmApi } from '@farm.js/plugin/api'
18
- *
19
- * export default defineConfig({
20
- * plugins: [farmApi({ srcDir: 'src' })],
21
- * })
22
- * ```
23
- */
24
- import type { Plugin } from "vite";
25
- export interface FarmApiOptions {
26
- /** Source directory containing the api folder (default: 'src') */
27
- srcDir?: string;
28
- /** Enable debug logging */
29
- debug?: boolean;
30
- }
31
- export interface ApiRoute {
32
- path: string;
33
- filePath: string;
34
- methods: string[];
35
- endpoints: Record<string, any>;
36
- }
37
- /**
38
- * Farm.js API Routes Plugin
39
- */
40
- export default function farmApi(options?: FarmApiOptions): Plugin;
41
- export { farmApi };
42
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,MAAM,CAAC;AAGlD,MAAM,WAAW,cAAc;IAC7B,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,MAAM,CAgmBpE;AAED,OAAO,EAAE,OAAO,EAAE,CAAC"}
package/dist/api/index.js DELETED
@@ -1,567 +0,0 @@
1
- /**
2
- * Farm.js API Routes Plugin
3
- *
4
- * Standalone API route support that works with or without RSC.
5
- *
6
- * Supports two patterns:
7
- * 1. File-based routing: /src/api/hello/route.ts -> /api/hello
8
- * The path is auto-inferred from file location
9
- *
10
- * 2. Root routes.ts file: /src/routes.ts with explicit paths
11
- * createEndpoint('/api/custom', { method: 'GET' }, handler)
12
- *
13
- * Uses better-call's createRouter for proper Zod validation and parsing.
14
- *
15
- * @example
16
- * ```ts
17
- * import { farmApi } from '@farm.js/plugin/api'
18
- *
19
- * export default defineConfig({
20
- * plugins: [farmApi({ srcDir: 'src' })],
21
- * })
22
- * ```
23
- */
24
- import { _withAfterNodeMiddleware } from "@farm.js/core/after";
25
- /**
26
- * Farm.js API Routes Plugin
27
- */
28
- export default function farmApi(options = {}) {
29
- const srcDir = options.srcDir ?? "src";
30
- const debug = options.debug ?? false;
31
- // API routes cache
32
- let apiRoutesCache = new Map();
33
- let apiRouterHandler = null;
34
- let discoveryComplete = false;
35
- let discoveryPromise = null;
36
- let currentServer = null;
37
- // Debug-only logging for discovery messages with [FARM] prefix
38
- const log = (message) => {
39
- if (!debug)
40
- return;
41
- try {
42
- const pc = require("picocolors");
43
- console.log(pc.dim("[") + pc.bold(pc.blue("FARM")) + pc.dim("]") + " " + pc.gray(message));
44
- }
45
- catch {
46
- console.log(`[FARM] ${message}`);
47
- }
48
- };
49
- // Always log API request/response with [FARM] [API] [METHOD] format
50
- const logResponse = (method, urlPath, status, duration) => {
51
- try {
52
- const pc = require("picocolors");
53
- let statusColor = pc.green;
54
- if (status >= 500)
55
- statusColor = pc.red;
56
- else if (status >= 400)
57
- statusColor = pc.yellow;
58
- else if (status >= 300)
59
- statusColor = pc.cyan;
60
- const log = [
61
- pc.dim("[") + pc.bold(pc.blue("FARM")) + pc.dim("]"),
62
- pc.dim("[") + pc.bold(pc.cyan("API")) + pc.dim("]"),
63
- pc.dim("[") + pc.bold(pc.white(method.padEnd(3))) + pc.dim("]"),
64
- pc.gray(urlPath),
65
- pc.dim("-"),
66
- statusColor(status.toString()),
67
- pc.dim(`(${duration}ms)`),
68
- ].join(" ");
69
- console.log(log);
70
- }
71
- catch {
72
- console.log(`[FARM] [API] [${method}] ${urlPath} - ${status} (${duration}ms)`);
73
- }
74
- };
75
- // Create API router handler
76
- // We use direct invocation for better control over path matching
77
- const createBetterCallRouter = async () => {
78
- const totalEndpoints = Array.from(apiRoutesCache.values()).reduce((sum, route) => sum + route.methods.length, 0);
79
- if (totalEndpoints > 0) {
80
- // Create a handler that matches routes and invokes endpoints
81
- apiRouterHandler = async (request) => {
82
- const url = new URL(request.url);
83
- const method = request.method;
84
- const pathname = url.pathname;
85
- // Find matching route
86
- const route = apiRoutesCache.get(pathname);
87
- if (!route) {
88
- return new Response(JSON.stringify({ error: "Not Found" }), {
89
- status: 404,
90
- headers: { "Content-Type": "application/json" },
91
- });
92
- }
93
- const endpoint = route.endpoints[method];
94
- if (!endpoint) {
95
- return new Response(JSON.stringify({ error: "Method Not Allowed" }), {
96
- status: 405,
97
- headers: { "Content-Type": "application/json" },
98
- });
99
- }
100
- try {
101
- // Parse query params
102
- const queryObj = {};
103
- url.searchParams.forEach((value, key) => {
104
- queryObj[key] = value;
105
- });
106
- // Parse body for non-GET requests
107
- let bodyObj = {};
108
- if (method !== "GET" && method !== "HEAD") {
109
- try {
110
- const bodyText = await request.text();
111
- if (bodyText) {
112
- bodyObj = JSON.parse(bodyText);
113
- }
114
- }
115
- catch {
116
- // Body parsing failed, use empty object
117
- }
118
- }
119
- // Parse headers
120
- const headersObj = {};
121
- request.headers.forEach((value, key) => {
122
- headersObj[key] = value;
123
- });
124
- // Validate with Zod if schemas are defined
125
- const types = endpoint.__types || {};
126
- let validatedQuery = queryObj;
127
- let validatedBody = bodyObj;
128
- if (types.query && typeof types.query.parse === "function") {
129
- try {
130
- validatedQuery = types.query.parse(queryObj);
131
- }
132
- catch (e) {
133
- return new Response(JSON.stringify({
134
- error: "Invalid query parameters",
135
- details: e.errors || e.message,
136
- }), {
137
- status: 400,
138
- headers: { "Content-Type": "application/json" },
139
- });
140
- }
141
- }
142
- if (types.body && typeof types.body.parse === "function") {
143
- try {
144
- validatedBody = types.body.parse(bodyObj);
145
- }
146
- catch (e) {
147
- return new Response(JSON.stringify({
148
- error: "Invalid request body",
149
- details: e.errors || e.message,
150
- }), {
151
- status: 400,
152
- headers: { "Content-Type": "application/json" },
153
- });
154
- }
155
- }
156
- // Build context object
157
- const ctx = {
158
- query: validatedQuery,
159
- body: validatedBody,
160
- headers: headersObj,
161
- request,
162
- context: {},
163
- params: {},
164
- };
165
- // Call the handler (use __handler if available, otherwise the endpoint itself)
166
- const handlerFn = endpoint.__handler || endpoint;
167
- const result = await handlerFn(ctx);
168
- // If result is already a Response, return it
169
- if (result instanceof Response) {
170
- return result;
171
- }
172
- // Otherwise, serialize to JSON
173
- return new Response(JSON.stringify(result), {
174
- status: 200,
175
- headers: { "Content-Type": "application/json" },
176
- });
177
- }
178
- catch (error) {
179
- return new Response(JSON.stringify({ error: error.message || "Internal Server Error" }), {
180
- status: 500,
181
- headers: { "Content-Type": "application/json" },
182
- });
183
- }
184
- };
185
- log(`better-call router created with ${totalEndpoints} endpoints`);
186
- }
187
- };
188
- // Fallback handler when better-call is not available
189
- const createFallbackHandler = () => {
190
- return async (request) => {
191
- const url = new URL(request.url);
192
- const method = request.method;
193
- // Find matching route
194
- const route = apiRoutesCache.get(url.pathname);
195
- if (!route) {
196
- return new Response(JSON.stringify({ error: "Not Found" }), {
197
- status: 404,
198
- headers: { "Content-Type": "application/json" },
199
- });
200
- }
201
- const endpoint = route.endpoints[method];
202
- if (!endpoint) {
203
- return new Response(JSON.stringify({ error: "Method Not Allowed" }), {
204
- status: 405,
205
- headers: { "Content-Type": "application/json" },
206
- });
207
- }
208
- try {
209
- // For fallback, call the endpoint directly (without Zod parsing)
210
- const result = await endpoint(request);
211
- if (result instanceof Response) {
212
- return result;
213
- }
214
- return new Response(JSON.stringify(result), {
215
- status: 200,
216
- headers: { "Content-Type": "application/json" },
217
- });
218
- }
219
- catch (error) {
220
- return new Response(JSON.stringify({ error: error.message || "Internal Server Error" }), {
221
- status: 500,
222
- headers: { "Content-Type": "application/json" },
223
- });
224
- }
225
- };
226
- };
227
- return {
228
- name: "@farm.js/plugin/api",
229
- enforce: "pre",
230
- configureServer(server) {
231
- currentServer = server;
232
- // Discover API routes
233
- const discoverAPIRoutes = async (apiDir) => {
234
- const fs = await import("fs");
235
- const path = await import("path");
236
- if (!fs.existsSync(apiDir)) {
237
- log("No api directory found");
238
- return;
239
- }
240
- const findRouteFiles = (dir) => {
241
- const files = [];
242
- if (!fs.existsSync(dir))
243
- return files;
244
- const entries = fs.readdirSync(dir, { withFileTypes: true });
245
- for (const entry of entries) {
246
- const fullPath = path.join(dir, entry.name);
247
- if (entry.isDirectory()) {
248
- files.push(...findRouteFiles(fullPath));
249
- }
250
- else if (entry.name === "route.ts" ||
251
- entry.name === "route.tsx" ||
252
- entry.name === "route.js") {
253
- files.push(fullPath);
254
- }
255
- }
256
- return files;
257
- };
258
- const routeFiles = findRouteFiles(apiDir);
259
- // Clear cache for rediscovery
260
- apiRoutesCache.clear();
261
- for (const filePath of routeFiles) {
262
- try {
263
- const relativePath = path.relative(apiDir, path.dirname(filePath));
264
- const routePath = "/api/" + (relativePath === "." ? "" : relativePath.replace(/\\/g, "/"));
265
- const routeModule = await server.ssrLoadModule(filePath);
266
- const methods = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"];
267
- const endpoints = {};
268
- const availableMethods = [];
269
- for (const method of methods) {
270
- if (routeModule[method]) {
271
- availableMethods.push(method);
272
- endpoints[method] = routeModule[method];
273
- }
274
- }
275
- if (availableMethods.length > 0) {
276
- apiRoutesCache.set(routePath, {
277
- path: routePath,
278
- filePath,
279
- methods: availableMethods,
280
- endpoints,
281
- });
282
- log(`API route discovered: ${availableMethods.join(", ")} ${routePath}`);
283
- }
284
- }
285
- catch (e) {
286
- log(`API route load failed at ${filePath}: ${e.message}`);
287
- }
288
- }
289
- // Router is created after all routes are discovered (including root routes.ts)
290
- };
291
- // Discover routes from root routes.ts file
292
- const discoverRootRoutes = async () => {
293
- const fs = await import("fs");
294
- const path = await import("path");
295
- const routesFiles = [
296
- path.join(server.config.root, srcDir, "routes.ts"),
297
- path.join(server.config.root, srcDir, "routes.tsx"),
298
- path.join(server.config.root, srcDir, "routes.js"),
299
- ];
300
- for (const routesFile of routesFiles) {
301
- if (fs.existsSync(routesFile)) {
302
- try {
303
- const routesModule = await server.ssrLoadModule(routesFile);
304
- // Look for exported endpoints with explicit paths
305
- for (const [exportName, exportValue] of Object.entries(routesModule)) {
306
- // Check both object and function exports (better-call returns functions)
307
- const endpoint = exportValue;
308
- if (endpoint && endpoint.__path) {
309
- const routePath = endpoint.__path;
310
- const method = endpoint.__method || "GET";
311
- // Add to cache
312
- const existing = apiRoutesCache.get(routePath);
313
- if (existing) {
314
- existing.methods.push(method);
315
- existing.endpoints[method] = endpoint;
316
- }
317
- else {
318
- apiRoutesCache.set(routePath, {
319
- path: routePath,
320
- filePath: routesFile,
321
- methods: [method],
322
- endpoints: { [method]: endpoint },
323
- });
324
- }
325
- // Root route discovery log omitted
326
- }
327
- }
328
- }
329
- catch (e) {
330
- log(`Root routes.ts load failed: ${e.message}`);
331
- }
332
- break; // Only load the first routes file found
333
- }
334
- }
335
- };
336
- // Initialize discovery
337
- const initializeDiscovery = async () => {
338
- const path = await import("path");
339
- const apiDir = path.join(server.config.root, srcDir, "api");
340
- await discoverAPIRoutes(apiDir);
341
- await discoverRootRoutes();
342
- await createBetterCallRouter();
343
- discoveryComplete = true;
344
- };
345
- discoveryPromise = initializeDiscovery().catch((e) => {
346
- console.error("[FARM] API discovery error:", e);
347
- });
348
- // Expose API router for other plugins
349
- server.__farmApi__ = {
350
- getHandler: () => apiRouterHandler,
351
- getRoutes: () => apiRoutesCache,
352
- isReady: () => discoveryComplete,
353
- waitForDiscovery: () => discoveryPromise,
354
- };
355
- // Add middleware to handle API requests
356
- return () => {
357
- server.middlewares.use(_withAfterNodeMiddleware(async (req, res, next) => {
358
- const url = req.url || "/";
359
- const pathname = url.split("?")[0];
360
- const method = req.method || "GET";
361
- // Only handle /api/ routes
362
- if (!pathname.startsWith("/api/")) {
363
- return next();
364
- }
365
- // Wait for discovery
366
- if (discoveryPromise && !discoveryComplete) {
367
- await discoveryPromise;
368
- }
369
- if (!apiRouterHandler) {
370
- return next();
371
- }
372
- const startTime = Date.now();
373
- try {
374
- // Execute middleware if available
375
- const farmMiddleware = server.__farmMiddleware__;
376
- if (farmMiddleware) {
377
- await farmMiddleware.waitForDiscovery?.();
378
- const middlewareData = new Map();
379
- const handled = await farmMiddleware.execute(req, res, pathname, middlewareData);
380
- if (handled) {
381
- const duration = Date.now() - startTime;
382
- logResponse(method, pathname, res.statusCode || 200, duration);
383
- return;
384
- }
385
- }
386
- // Convert Node request to Web Request
387
- const fullUrl = `http://${req.headers.host || "localhost:3000"}${url}`;
388
- const headers = new Headers();
389
- for (const [key, value] of Object.entries(req.headers)) {
390
- if (value) {
391
- headers.set(key, Array.isArray(value) ? value.join(", ") : value);
392
- }
393
- }
394
- // Get body for non-GET requests
395
- let body;
396
- if (method !== "GET" && method !== "HEAD") {
397
- body = await new Promise((resolve) => {
398
- let data = "";
399
- req.on("data", (chunk) => {
400
- data += chunk;
401
- });
402
- req.on("end", () => {
403
- resolve(data);
404
- });
405
- });
406
- }
407
- const request = new Request(fullUrl, {
408
- method,
409
- headers,
410
- body: body || undefined,
411
- });
412
- const response = await apiRouterHandler(request);
413
- const duration = Date.now() - startTime;
414
- logResponse(method, pathname, response.status, duration);
415
- // Send response
416
- res.statusCode = response.status;
417
- response.headers.forEach((value, key) => {
418
- res.setHeader(key, value);
419
- });
420
- const responseBody = await response.text();
421
- res.end(responseBody);
422
- }
423
- catch (error) {
424
- const duration = Date.now() - startTime;
425
- logResponse(method, pathname, 500, duration);
426
- console.error("[FARM] API error:", error);
427
- res.statusCode = 500;
428
- res.setHeader("Content-Type", "application/json");
429
- res.end(JSON.stringify({ error: "Internal server error" }));
430
- }
431
- }));
432
- };
433
- },
434
- async handleHotUpdate({ file, server, modules }) {
435
- const fileName = file.split("/").pop() || "";
436
- // Handle root routes.ts updates
437
- if (fileName === "routes.ts" || fileName === "routes.tsx" || fileName === "routes.js") {
438
- log(`Root routes file updated: ${fileName}`);
439
- // Invalidate all affected modules
440
- for (const mod of modules) {
441
- server.moduleGraph.invalidateModule(mod);
442
- }
443
- // Clear routes that came from this file and rediscover
444
- for (const [routePath, route] of apiRoutesCache) {
445
- if (route.filePath === file) {
446
- apiRoutesCache.delete(routePath);
447
- }
448
- }
449
- try {
450
- const routesModule = await server.ssrLoadModule(file);
451
- // Look for exported endpoints with explicit paths
452
- // Endpoints can be functions (from better-call) or objects
453
- for (const [exportName, exportValue] of Object.entries(routesModule)) {
454
- const endpoint = exportValue;
455
- if (endpoint && endpoint.__path) {
456
- const routePath = endpoint.__path;
457
- const method = endpoint.__method || "GET";
458
- // Add to cache
459
- const existing = apiRoutesCache.get(routePath);
460
- if (existing) {
461
- existing.methods.push(method);
462
- existing.endpoints[method] = endpoint;
463
- }
464
- else {
465
- apiRoutesCache.set(routePath, {
466
- path: routePath,
467
- filePath: file,
468
- methods: [method],
469
- endpoints: { [method]: endpoint },
470
- });
471
- }
472
- log(`Root route reloaded: ${method} ${routePath}`);
473
- }
474
- }
475
- // Recreate better-call router
476
- await createBetterCallRouter();
477
- log(`better-call router recreated`);
478
- }
479
- catch (e) {
480
- log(`Root routes.ts HMR failed: ${e.message}`);
481
- }
482
- return [];
483
- }
484
- if (file.includes("/api/") && fileName.startsWith("route.")) {
485
- const shortPath = file.split("/api/")[1] || file;
486
- log(`API route updated: ${shortPath}`);
487
- // Invalidate all affected modules
488
- for (const mod of modules) {
489
- server.moduleGraph.invalidateModule(mod);
490
- }
491
- // Find the route path for this file
492
- let routePathToUpdate = null;
493
- for (const [routePath, route] of apiRoutesCache) {
494
- if (route.filePath === file) {
495
- routePathToUpdate = routePath;
496
- break;
497
- }
498
- }
499
- if (routePathToUpdate) {
500
- try {
501
- // Reload the module
502
- const routeModule = await server.ssrLoadModule(file);
503
- const methods = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"];
504
- const endpoints = {};
505
- const availableMethods = [];
506
- for (const method of methods) {
507
- if (routeModule[method]) {
508
- availableMethods.push(method);
509
- endpoints[method] = routeModule[method];
510
- }
511
- }
512
- if (availableMethods.length > 0) {
513
- apiRoutesCache.set(routePathToUpdate, {
514
- path: routePathToUpdate,
515
- filePath: file,
516
- methods: availableMethods,
517
- endpoints,
518
- });
519
- log(`API route reloaded: ${routePathToUpdate}`);
520
- // Recreate better-call router with updated endpoints
521
- await createBetterCallRouter();
522
- log(`better-call router recreated`);
523
- }
524
- }
525
- catch (e) {
526
- log(`API route HMR failed: ${e.message}`);
527
- }
528
- }
529
- else {
530
- // New route file - discover it
531
- try {
532
- const path = await import("path");
533
- const apiDir = path.join(server.config.root, srcDir, "api");
534
- const relativePath = path.relative(apiDir, path.dirname(file));
535
- const routePath = "/api/" + (relativePath === "." ? "" : relativePath.replace(/\\/g, "/"));
536
- const routeModule = await server.ssrLoadModule(file);
537
- const methods = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"];
538
- const endpoints = {};
539
- const availableMethods = [];
540
- for (const method of methods) {
541
- if (routeModule[method]) {
542
- availableMethods.push(method);
543
- endpoints[method] = routeModule[method];
544
- }
545
- }
546
- if (availableMethods.length > 0) {
547
- apiRoutesCache.set(routePath, {
548
- path: routePath,
549
- filePath: file,
550
- methods: availableMethods,
551
- endpoints,
552
- });
553
- log(`New API route discovered: ${routePath}`);
554
- // Recreate better-call router with new endpoint
555
- await createBetterCallRouter();
556
- }
557
- }
558
- catch (e) {
559
- log(`New API route load failed: ${e.message}`);
560
- }
561
- }
562
- return [];
563
- }
564
- },
565
- };
566
- }
567
- export { farmApi };