@voltx/server 0.4.5 → 0.4.6

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/dist/index.cjs CHANGED
@@ -41,6 +41,7 @@ __export(index_exports, {
41
41
  registerSSR: () => registerSSR,
42
42
  registerStaticFiles: () => registerStaticFiles,
43
43
  scanAndRegisterRoutes: () => scanAndRegisterRoutes,
44
+ voltxAPI: () => voltxAPI,
44
45
  voltxRouter: () => voltxRouter
45
46
  });
46
47
  module.exports = __toCommonJS(index_exports);
@@ -392,6 +393,86 @@ export { Link, NavLink, useNavigate, useParams, useLocation, useSearchParams } f
392
393
  };
393
394
  }
394
395
 
396
+ // src/vite-api-plugin.ts
397
+ function voltxAPI(options = {}) {
398
+ const apiDir = options.apiDir ?? "api";
399
+ const PUBLIC_ID = "voltx/api";
400
+ const RESOLVED_ID = "\0voltx/api";
401
+ return {
402
+ name: "voltx-api",
403
+ enforce: "pre",
404
+ resolveId(id) {
405
+ if (id === PUBLIC_ID) {
406
+ return RESOLVED_ID;
407
+ }
408
+ },
409
+ load(id) {
410
+ if (id === RESOLVED_ID) {
411
+ return `
412
+ const modules = import.meta.glob("/${apiDir}/**/*.ts", { eager: true });
413
+
414
+ const HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"];
415
+
416
+ function fileToRoute(filePath) {
417
+ let route = filePath
418
+ .replace("/${apiDir}", "/${apiDir}")
419
+ .replace(/\\.ts$/, "")
420
+ .replace(/\\/index$/, "");
421
+
422
+ // Convert [param] -> :param
423
+ route = route.replace(/\\[([^\\]\\.]+)\\]/g, ":$1");
424
+ // Convert [...slug] -> *
425
+ route = route.replace(/\\[\\.\\.\\.([^\\]]+)\\]/g, "*");
426
+
427
+ if (!route || route === "/${apiDir}") route = "/${apiDir}";
428
+ return route;
429
+ }
430
+
431
+ export function registerRoutes(app) {
432
+ const registered = [];
433
+
434
+ // Sort: static routes first, dynamic (:param) second, catch-all (*) last
435
+ const entries = Object.entries(modules).sort(([a], [b]) => {
436
+ const ra = fileToRoute(a);
437
+ const rb = fileToRoute(b);
438
+ const sa = ra.includes("*") ? 2 : ra.includes(":") ? 1 : 0;
439
+ const sb = rb.includes("*") ? 2 : rb.includes(":") ? 1 : 0;
440
+ if (sa !== sb) return sa - sb;
441
+ return ra.localeCompare(rb);
442
+ });
443
+
444
+ for (const [filePath, mod] of entries) {
445
+ const route = fileToRoute(filePath);
446
+
447
+ for (const method of HTTP_METHODS) {
448
+ const handler = mod[method];
449
+ if (typeof handler === "function") {
450
+ app.on(method, route, handler);
451
+ registered.push({ method, path: route });
452
+ }
453
+ }
454
+ }
455
+
456
+ return registered;
457
+ }
458
+
459
+ export { modules as apiModules };
460
+ `;
461
+ }
462
+ },
463
+ // HMR: when a file in api/ is added/removed, invalidate the virtual module
464
+ handleHotUpdate({ file, server }) {
465
+ if (file.includes(`/${apiDir}/`) || file.endsWith(`/${apiDir}`)) {
466
+ const mod = server.moduleGraph.getModuleById(RESOLVED_ID);
467
+ if (mod) {
468
+ server.moduleGraph.invalidateModule(mod);
469
+ server.ws.send({ type: "full-reload" });
470
+ }
471
+ }
472
+ }
473
+ };
474
+ }
475
+
395
476
  // src/ssr.ts
396
477
  var import_node_path3 = require("path");
397
478
  var import_node_fs = require("fs");
@@ -532,7 +613,7 @@ ${cssLinks}
532
613
  }
533
614
 
534
615
  // src/index.ts
535
- var VERSION = "0.4.5";
616
+ var VERSION = "0.4.6";
536
617
  // Annotate the CommonJS export names for ESM import in node:
537
618
  0 && (module.exports = {
538
619
  Hono,
@@ -546,5 +627,6 @@ var VERSION = "0.4.5";
546
627
  registerSSR,
547
628
  registerStaticFiles,
548
629
  scanAndRegisterRoutes,
630
+ voltxAPI,
549
631
  voltxRouter
550
632
  });
package/dist/index.d.cts CHANGED
@@ -198,6 +198,24 @@ interface VoltxRouterOptions {
198
198
  */
199
199
  declare function voltxRouter(options?: VoltxRouterOptions): Plugin;
200
200
 
201
+ interface VoltxAPIOptions {
202
+ /** Directory to scan for API route files (default: "api") */
203
+ apiDir?: string;
204
+ }
205
+ /**
206
+ * VoltX file-based API routing plugin for Vite.
207
+ *
208
+ * Usage in server.ts:
209
+ * import { registerRoutes } from "voltx/api";
210
+ * registerRoutes(app);
211
+ *
212
+ * Convention:
213
+ * api/index.ts → /api
214
+ * api/users.ts → /api/users
215
+ * api/users/[id].ts → /api/users/:id
216
+ */
217
+ declare function voltxAPI(options?: VoltxAPIOptions): Plugin;
218
+
201
219
  interface SSROptions {
202
220
  /** Path to entry-server module (default: src/entry-server.tsx) */
203
221
  entryServer?: string;
@@ -245,6 +263,6 @@ interface ViteDevServer {
245
263
  */
246
264
  declare function registerSSR(app: Hono, vite: ViteDevServer | null, options?: SSROptions): void;
247
265
 
248
- declare const VERSION = "0.4.5";
266
+ declare const VERSION = "0.4.6";
249
267
 
250
- export { type CorsConfig, type HttpMethod, type MiddlewareHandler, type RouteEntry, type RouteHandler, type RouteModule, type SSROptions, type ServerConfig, type ServerInfo, VERSION, type ViteDevOptions, type VoltxRouterOptions, type VoltxServer, createCorsMiddleware, createErrorHandler, createLoggerMiddleware, createServer, createViteDevConfig, filePathToUrlPath, registerSSR, registerStaticFiles, scanAndRegisterRoutes, voltxRouter };
268
+ export { type CorsConfig, type HttpMethod, type MiddlewareHandler, type RouteEntry, type RouteHandler, type RouteModule, type SSROptions, type ServerConfig, type ServerInfo, VERSION, type ViteDevOptions, type VoltxAPIOptions, type VoltxRouterOptions, type VoltxServer, createCorsMiddleware, createErrorHandler, createLoggerMiddleware, createServer, createViteDevConfig, filePathToUrlPath, registerSSR, registerStaticFiles, scanAndRegisterRoutes, voltxAPI, voltxRouter };
package/dist/index.d.ts CHANGED
@@ -198,6 +198,24 @@ interface VoltxRouterOptions {
198
198
  */
199
199
  declare function voltxRouter(options?: VoltxRouterOptions): Plugin;
200
200
 
201
+ interface VoltxAPIOptions {
202
+ /** Directory to scan for API route files (default: "api") */
203
+ apiDir?: string;
204
+ }
205
+ /**
206
+ * VoltX file-based API routing plugin for Vite.
207
+ *
208
+ * Usage in server.ts:
209
+ * import { registerRoutes } from "voltx/api";
210
+ * registerRoutes(app);
211
+ *
212
+ * Convention:
213
+ * api/index.ts → /api
214
+ * api/users.ts → /api/users
215
+ * api/users/[id].ts → /api/users/:id
216
+ */
217
+ declare function voltxAPI(options?: VoltxAPIOptions): Plugin;
218
+
201
219
  interface SSROptions {
202
220
  /** Path to entry-server module (default: src/entry-server.tsx) */
203
221
  entryServer?: string;
@@ -245,6 +263,6 @@ interface ViteDevServer {
245
263
  */
246
264
  declare function registerSSR(app: Hono, vite: ViteDevServer | null, options?: SSROptions): void;
247
265
 
248
- declare const VERSION = "0.4.5";
266
+ declare const VERSION = "0.4.6";
249
267
 
250
- export { type CorsConfig, type HttpMethod, type MiddlewareHandler, type RouteEntry, type RouteHandler, type RouteModule, type SSROptions, type ServerConfig, type ServerInfo, VERSION, type ViteDevOptions, type VoltxRouterOptions, type VoltxServer, createCorsMiddleware, createErrorHandler, createLoggerMiddleware, createServer, createViteDevConfig, filePathToUrlPath, registerSSR, registerStaticFiles, scanAndRegisterRoutes, voltxRouter };
268
+ export { type CorsConfig, type HttpMethod, type MiddlewareHandler, type RouteEntry, type RouteHandler, type RouteModule, type SSROptions, type ServerConfig, type ServerInfo, VERSION, type ViteDevOptions, type VoltxAPIOptions, type VoltxRouterOptions, type VoltxServer, createCorsMiddleware, createErrorHandler, createLoggerMiddleware, createServer, createViteDevConfig, filePathToUrlPath, registerSSR, registerStaticFiles, scanAndRegisterRoutes, voltxAPI, voltxRouter };
package/dist/index.js CHANGED
@@ -345,6 +345,86 @@ export { Link, NavLink, useNavigate, useParams, useLocation, useSearchParams } f
345
345
  };
346
346
  }
347
347
 
348
+ // src/vite-api-plugin.ts
349
+ function voltxAPI(options = {}) {
350
+ const apiDir = options.apiDir ?? "api";
351
+ const PUBLIC_ID = "voltx/api";
352
+ const RESOLVED_ID = "\0voltx/api";
353
+ return {
354
+ name: "voltx-api",
355
+ enforce: "pre",
356
+ resolveId(id) {
357
+ if (id === PUBLIC_ID) {
358
+ return RESOLVED_ID;
359
+ }
360
+ },
361
+ load(id) {
362
+ if (id === RESOLVED_ID) {
363
+ return `
364
+ const modules = import.meta.glob("/${apiDir}/**/*.ts", { eager: true });
365
+
366
+ const HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"];
367
+
368
+ function fileToRoute(filePath) {
369
+ let route = filePath
370
+ .replace("/${apiDir}", "/${apiDir}")
371
+ .replace(/\\.ts$/, "")
372
+ .replace(/\\/index$/, "");
373
+
374
+ // Convert [param] -> :param
375
+ route = route.replace(/\\[([^\\]\\.]+)\\]/g, ":$1");
376
+ // Convert [...slug] -> *
377
+ route = route.replace(/\\[\\.\\.\\.([^\\]]+)\\]/g, "*");
378
+
379
+ if (!route || route === "/${apiDir}") route = "/${apiDir}";
380
+ return route;
381
+ }
382
+
383
+ export function registerRoutes(app) {
384
+ const registered = [];
385
+
386
+ // Sort: static routes first, dynamic (:param) second, catch-all (*) last
387
+ const entries = Object.entries(modules).sort(([a], [b]) => {
388
+ const ra = fileToRoute(a);
389
+ const rb = fileToRoute(b);
390
+ const sa = ra.includes("*") ? 2 : ra.includes(":") ? 1 : 0;
391
+ const sb = rb.includes("*") ? 2 : rb.includes(":") ? 1 : 0;
392
+ if (sa !== sb) return sa - sb;
393
+ return ra.localeCompare(rb);
394
+ });
395
+
396
+ for (const [filePath, mod] of entries) {
397
+ const route = fileToRoute(filePath);
398
+
399
+ for (const method of HTTP_METHODS) {
400
+ const handler = mod[method];
401
+ if (typeof handler === "function") {
402
+ app.on(method, route, handler);
403
+ registered.push({ method, path: route });
404
+ }
405
+ }
406
+ }
407
+
408
+ return registered;
409
+ }
410
+
411
+ export { modules as apiModules };
412
+ `;
413
+ }
414
+ },
415
+ // HMR: when a file in api/ is added/removed, invalidate the virtual module
416
+ handleHotUpdate({ file, server }) {
417
+ if (file.includes(`/${apiDir}/`) || file.endsWith(`/${apiDir}`)) {
418
+ const mod = server.moduleGraph.getModuleById(RESOLVED_ID);
419
+ if (mod) {
420
+ server.moduleGraph.invalidateModule(mod);
421
+ server.ws.send({ type: "full-reload" });
422
+ }
423
+ }
424
+ }
425
+ };
426
+ }
427
+
348
428
  // src/ssr.ts
349
429
  import { resolve as resolve2 } from "path";
350
430
  import { readFileSync, existsSync } from "fs";
@@ -485,7 +565,7 @@ ${cssLinks}
485
565
  }
486
566
 
487
567
  // src/index.ts
488
- var VERSION = "0.4.5";
568
+ var VERSION = "0.4.6";
489
569
  export {
490
570
  Hono2 as Hono,
491
571
  VERSION,
@@ -498,5 +578,6 @@ export {
498
578
  registerSSR,
499
579
  registerStaticFiles,
500
580
  scanAndRegisterRoutes,
581
+ voltxAPI,
501
582
  voltxRouter
502
583
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltx/server",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
4
4
  "description": "VoltX Server — Hono-based HTTP server with file-based routing, SSE streaming, and static file serving",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",