@voltx/server 0.4.4 → 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);
@@ -313,26 +314,26 @@ function createViteDevConfig(options = {}) {
313
314
  // src/vite-plugin.ts
314
315
  function voltxRouter(options = {}) {
315
316
  const pagesDir = options.pagesDir ?? "src/pages";
316
- const virtualModuleId = "virtual:voltx-routes";
317
- const resolvedVirtualModuleId = "\0" + virtualModuleId;
317
+ const PUBLIC_ID = "voltx/router";
318
+ const LEGACY_ID = "virtual:voltx-routes";
319
+ const RESOLVED_ID = "\0voltx/router";
318
320
  return {
319
321
  name: "voltx-router",
320
322
  enforce: "pre",
321
323
  config() {
322
324
  return {
323
325
  ssr: {
324
- // react-router ships CJS — force Vite to bundle its .mjs for SSR
325
326
  noExternal: ["react-router"]
326
327
  }
327
328
  };
328
329
  },
329
330
  resolveId(id) {
330
- if (id === virtualModuleId) {
331
- return resolvedVirtualModuleId;
331
+ if (id === PUBLIC_ID || id === LEGACY_ID) {
332
+ return RESOLVED_ID;
332
333
  }
333
334
  },
334
335
  load(id) {
335
- if (id === resolvedVirtualModuleId) {
336
+ if (id === RESOLVED_ID) {
336
337
  return `
337
338
  import { createElement } from "react";
338
339
  import { Routes, Route } from "react-router";
@@ -374,13 +375,95 @@ export function VoltxRoutes() {
374
375
  }
375
376
 
376
377
  export { routes };
378
+
379
+ // Navigation primitives \u2014 single import source
380
+ export { Link, NavLink, useNavigate, useParams, useLocation, useSearchParams } from "react-router";
377
381
  `;
378
382
  }
379
383
  },
380
- // HMR: when a file in src/pages/ is added/removed, invalidate the virtual module
381
384
  handleHotUpdate({ file, server }) {
382
385
  if (file.includes(pagesDir.replace(/\//g, "/"))) {
383
- const mod = server.moduleGraph.getModuleById(resolvedVirtualModuleId);
386
+ const mod = server.moduleGraph.getModuleById(RESOLVED_ID);
387
+ if (mod) {
388
+ server.moduleGraph.invalidateModule(mod);
389
+ server.ws.send({ type: "full-reload" });
390
+ }
391
+ }
392
+ }
393
+ };
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);
384
467
  if (mod) {
385
468
  server.moduleGraph.invalidateModule(mod);
386
469
  server.ws.send({ type: "full-reload" });
@@ -530,7 +613,7 @@ ${cssLinks}
530
613
  }
531
614
 
532
615
  // src/index.ts
533
- var VERSION = "0.4.4";
616
+ var VERSION = "0.4.6";
534
617
  // Annotate the CommonJS export names for ESM import in node:
535
618
  0 && (module.exports = {
536
619
  Hono,
@@ -544,5 +627,6 @@ var VERSION = "0.4.4";
544
627
  registerSSR,
545
628
  registerStaticFiles,
546
629
  scanAndRegisterRoutes,
630
+ voltxAPI,
547
631
  voltxRouter
548
632
  });
package/dist/index.d.cts CHANGED
@@ -187,6 +187,9 @@ interface VoltxRouterOptions {
187
187
  * Scans `src/pages/` and generates a virtual module that maps
188
188
  * file paths to routes — just like Next.js.
189
189
  *
190
+ * Usage:
191
+ * import { Link, VoltxRoutes, useNavigate } from "voltx/router";
192
+ *
190
193
  * Convention:
191
194
  * src/pages/index.tsx → /
192
195
  * src/pages/about.tsx → /about
@@ -195,6 +198,24 @@ interface VoltxRouterOptions {
195
198
  */
196
199
  declare function voltxRouter(options?: VoltxRouterOptions): Plugin;
197
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
+
198
219
  interface SSROptions {
199
220
  /** Path to entry-server module (default: src/entry-server.tsx) */
200
221
  entryServer?: string;
@@ -242,6 +263,6 @@ interface ViteDevServer {
242
263
  */
243
264
  declare function registerSSR(app: Hono, vite: ViteDevServer | null, options?: SSROptions): void;
244
265
 
245
- declare const VERSION = "0.4.4";
266
+ declare const VERSION = "0.4.6";
246
267
 
247
- 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
@@ -187,6 +187,9 @@ interface VoltxRouterOptions {
187
187
  * Scans `src/pages/` and generates a virtual module that maps
188
188
  * file paths to routes — just like Next.js.
189
189
  *
190
+ * Usage:
191
+ * import { Link, VoltxRoutes, useNavigate } from "voltx/router";
192
+ *
190
193
  * Convention:
191
194
  * src/pages/index.tsx → /
192
195
  * src/pages/about.tsx → /about
@@ -195,6 +198,24 @@ interface VoltxRouterOptions {
195
198
  */
196
199
  declare function voltxRouter(options?: VoltxRouterOptions): Plugin;
197
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
+
198
219
  interface SSROptions {
199
220
  /** Path to entry-server module (default: src/entry-server.tsx) */
200
221
  entryServer?: string;
@@ -242,6 +263,6 @@ interface ViteDevServer {
242
263
  */
243
264
  declare function registerSSR(app: Hono, vite: ViteDevServer | null, options?: SSROptions): void;
244
265
 
245
- declare const VERSION = "0.4.4";
266
+ declare const VERSION = "0.4.6";
246
267
 
247
- 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
@@ -266,26 +266,26 @@ function createViteDevConfig(options = {}) {
266
266
  // src/vite-plugin.ts
267
267
  function voltxRouter(options = {}) {
268
268
  const pagesDir = options.pagesDir ?? "src/pages";
269
- const virtualModuleId = "virtual:voltx-routes";
270
- const resolvedVirtualModuleId = "\0" + virtualModuleId;
269
+ const PUBLIC_ID = "voltx/router";
270
+ const LEGACY_ID = "virtual:voltx-routes";
271
+ const RESOLVED_ID = "\0voltx/router";
271
272
  return {
272
273
  name: "voltx-router",
273
274
  enforce: "pre",
274
275
  config() {
275
276
  return {
276
277
  ssr: {
277
- // react-router ships CJS — force Vite to bundle its .mjs for SSR
278
278
  noExternal: ["react-router"]
279
279
  }
280
280
  };
281
281
  },
282
282
  resolveId(id) {
283
- if (id === virtualModuleId) {
284
- return resolvedVirtualModuleId;
283
+ if (id === PUBLIC_ID || id === LEGACY_ID) {
284
+ return RESOLVED_ID;
285
285
  }
286
286
  },
287
287
  load(id) {
288
- if (id === resolvedVirtualModuleId) {
288
+ if (id === RESOLVED_ID) {
289
289
  return `
290
290
  import { createElement } from "react";
291
291
  import { Routes, Route } from "react-router";
@@ -327,13 +327,95 @@ export function VoltxRoutes() {
327
327
  }
328
328
 
329
329
  export { routes };
330
+
331
+ // Navigation primitives \u2014 single import source
332
+ export { Link, NavLink, useNavigate, useParams, useLocation, useSearchParams } from "react-router";
330
333
  `;
331
334
  }
332
335
  },
333
- // HMR: when a file in src/pages/ is added/removed, invalidate the virtual module
334
336
  handleHotUpdate({ file, server }) {
335
337
  if (file.includes(pagesDir.replace(/\//g, "/"))) {
336
- const mod = server.moduleGraph.getModuleById(resolvedVirtualModuleId);
338
+ const mod = server.moduleGraph.getModuleById(RESOLVED_ID);
339
+ if (mod) {
340
+ server.moduleGraph.invalidateModule(mod);
341
+ server.ws.send({ type: "full-reload" });
342
+ }
343
+ }
344
+ }
345
+ };
346
+ }
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);
337
419
  if (mod) {
338
420
  server.moduleGraph.invalidateModule(mod);
339
421
  server.ws.send({ type: "full-reload" });
@@ -483,7 +565,7 @@ ${cssLinks}
483
565
  }
484
566
 
485
567
  // src/index.ts
486
- var VERSION = "0.4.4";
568
+ var VERSION = "0.4.6";
487
569
  export {
488
570
  Hono2 as Hono,
489
571
  VERSION,
@@ -496,5 +578,6 @@ export {
496
578
  registerSSR,
497
579
  registerStaticFiles,
498
580
  scanAndRegisterRoutes,
581
+ voltxAPI,
499
582
  voltxRouter
500
583
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltx/server",
3
- "version": "0.4.4",
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",