@voyantjs/hono 0.4.5 → 0.6.0

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
@@ -1,6 +1,7 @@
1
1
  # @voyantjs/hono
2
2
 
3
- Hono transport adapter for Voyant. Provides `createApp()`, middleware, auth helpers, and plugin expansion for mounting Voyant modules behind a Hono app.
3
+ Hono transport adapter for Voyant. Provides `createApp()`, middleware, auth
4
+ helpers, and plugin expansion for mounting Voyant modules behind a Hono app.
4
5
 
5
6
  ## Install
6
7
 
@@ -17,10 +18,19 @@ const app = createApp({
17
18
  db: (env) => getDb(env),
18
19
  auth: { handler, resolve },
19
20
  modules: [crmModule, productsModule, bookingsModule],
20
- plugins: [payloadCmsPlugin({ /* ... */ })],
21
+ extensions: [smartbillFinanceExtension],
22
+ plugins: [
23
+ payloadCmsPlugin({
24
+ /* optional distribution bundle */
25
+ }),
26
+ ],
21
27
  })
22
28
  ```
23
29
 
30
+ Use `modules`, `extensions`, and provider-backed route helpers as the default
31
+ composition surface. Use `plugins` when you want to register a reusable
32
+ distribution bundle that packages those pieces together.
33
+
24
34
  The middleware chain is: container → requestId → logger → errorBoundary → CORS → health → auth handler → requireAuth → db → actor guards → module routes.
25
35
 
26
36
  ## Exports
package/dist/app.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAS3B,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAElF,wBAAgB,SAAS,CAAC,SAAS,SAAS,cAAc,EACxD,MAAM,EAAE,eAAe,CAAC,SAAS,CAAC,GACjC,IAAI,CAAC;IAAE,QAAQ,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,eAAe,CAAA;CAAE,CAAC,CAsF3D"}
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAS3B,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAoBlF,wBAAgB,SAAS,CAAC,SAAS,SAAS,cAAc,EACxD,MAAM,EAAE,eAAe,CAAC,SAAS,CAAC,GACjC,IAAI,CAAC;IAAE,QAAQ,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,eAAe,CAAA;CAAE,CAAC,CAkI3D"}
package/dist/app.js CHANGED
@@ -1,18 +1,35 @@
1
- import { createContainer } from "@voyantjs/core";
1
+ import { createContainer, createEventBus, createQueryRunner } from "@voyantjs/core";
2
2
  import { Hono } from "hono";
3
3
  import { requireAuth } from "./middleware/auth.js";
4
4
  import { cors } from "./middleware/cors.js";
5
5
  import { db } from "./middleware/db.js";
6
- import { errorBoundary, requestId } from "./middleware/error-boundary.js";
6
+ import { handleApiError, requestId } from "./middleware/error-boundary.js";
7
7
  import { logger } from "./middleware/logger.js";
8
8
  import { requireActor } from "./middleware/require-actor.js";
9
9
  import { expandHonoPlugins } from "./plugin.js";
10
+ function resolveSurfaceMountPath(prefix, path, fallback) {
11
+ const normalized = path?.trim();
12
+ if (!normalized) {
13
+ return `${prefix}/${fallback}`;
14
+ }
15
+ if (normalized === "/") {
16
+ return prefix;
17
+ }
18
+ return `${prefix}/${normalized.replace(/^\/+|\/+$/g, "")}`;
19
+ }
10
20
  export function createApp(config) {
11
21
  const app = new Hono();
22
+ app.onError(handleApiError);
12
23
  // Expand plugins into their constituent modules/extensions before mounting
13
24
  const expanded = config.plugins ? expandHonoPlugins(config.plugins) : null;
14
25
  const allModules = [...(config.modules ?? []), ...(expanded?.modules ?? [])];
15
26
  const allExtensions = [...(config.extensions ?? []), ...(expanded?.extensions ?? [])];
27
+ const eventBus = config.eventBus ?? createEventBus();
28
+ const query = typeof config.query === "function"
29
+ ? config.query
30
+ : config.query
31
+ ? createQueryRunner(config.query)
32
+ : undefined;
16
33
  // Module container — registered services are resolvable from routes
17
34
  const container = createContainer();
18
35
  for (const mod of allModules) {
@@ -20,16 +37,43 @@ export function createApp(config) {
20
37
  container.register(mod.module.name, mod.module.service);
21
38
  }
22
39
  }
40
+ for (const sub of expanded?.subscribers ?? []) {
41
+ eventBus.subscribe(sub.event, sub.handler);
42
+ }
43
+ let bootstrapPromise = null;
44
+ function ensureRuntimeBootstrapped(bindings) {
45
+ if (!bootstrapPromise) {
46
+ bootstrapPromise = (async () => {
47
+ const ctx = { bindings, container, eventBus };
48
+ for (const plugin of config.plugins ?? []) {
49
+ await plugin.bootstrap?.(ctx);
50
+ }
51
+ for (const mod of allModules) {
52
+ await mod.module.bootstrap?.(ctx);
53
+ }
54
+ for (const ext of allExtensions) {
55
+ await ext.extension.bootstrap?.(ctx);
56
+ }
57
+ })();
58
+ }
59
+ return bootstrapPromise;
60
+ }
23
61
  app.use("*", async (c, next) => {
24
62
  c.set("container", container);
63
+ c.set("eventBus", eventBus);
64
+ if (config.link) {
65
+ c.set("link", config.link);
66
+ }
67
+ if (query) {
68
+ c.set("query", query);
69
+ }
70
+ await ensureRuntimeBootstrapped(c.env);
25
71
  return next();
26
72
  });
27
73
  // Request ID header
28
74
  app.use("*", requestId);
29
75
  // Structured logger
30
76
  app.use("*", logger(config.logger));
31
- // Global error boundary
32
- app.use("*", errorBoundary);
33
77
  // CORS (allowlist via env CORS_ALLOWLIST)
34
78
  app.use("*", cors());
35
79
  // Health check (public, no auth)
@@ -55,7 +99,7 @@ export function createApp(config) {
55
99
  app.route(`/v1/admin/${mod.module.name}`, mod.adminRoutes);
56
100
  }
57
101
  if (mod.publicRoutes) {
58
- app.route(`/v1/public/${mod.module.name}`, mod.publicRoutes);
102
+ app.route(resolveSurfaceMountPath("/v1/public", mod.publicPath, mod.module.name), mod.publicRoutes);
59
103
  }
60
104
  if (mod.routes) {
61
105
  app.route(`/v1/${mod.module.name}`, mod.routes);
@@ -67,7 +111,7 @@ export function createApp(config) {
67
111
  app.route(`/v1/admin/${ext.extension.module}`, ext.adminRoutes);
68
112
  }
69
113
  if (ext.publicRoutes) {
70
- app.route(`/v1/public/${ext.extension.module}`, ext.publicRoutes);
114
+ app.route(resolveSurfaceMountPath("/v1/public", ext.publicPath, ext.extension.module), ext.publicRoutes);
71
115
  }
72
116
  if (ext.routes) {
73
117
  app.route(`/v1/${ext.extension.module}`, ext.routes);
@@ -1,4 +1,5 @@
1
1
  export { generateNumericCode, randomBytesHex, sha256Base64Url, sha256Hex, unsignCookie, } from "./crypto.js";
2
+ export { requireUserId } from "./require-user.js";
2
3
  export type { SessionAuthContext } from "./session-jwt.js";
3
4
  export { extractBearerToken, verifySession } from "./session-jwt.js";
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,eAAe,EACf,SAAS,EACT,YAAY,GACb,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAC1D,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,eAAe,EACf,SAAS,EACT,YAAY,GACb,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACjD,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAC1D,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA"}
@@ -1,2 +1,3 @@
1
1
  export { generateNumericCode, randomBytesHex, sha256Base64Url, sha256Hex, unsignCookie, } from "./crypto.js";
2
+ export { requireUserId } from "./require-user.js";
2
3
  export { extractBearerToken, verifySession } from "./session-jwt.js";
@@ -0,0 +1,3 @@
1
+ import type { Context } from "hono";
2
+ export declare function requireUserId(c: Context): string;
3
+ //# sourceMappingURL=require-user.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"require-user.d.ts","sourceRoot":"","sources":["../../src/auth/require-user.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAInC,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAOhD"}
@@ -0,0 +1,8 @@
1
+ import { UnauthorizedApiError } from "../validation.js";
2
+ export function requireUserId(c) {
3
+ const userId = c.get("userId");
4
+ if (!userId) {
5
+ throw new UnauthorizedApiError();
6
+ }
7
+ return userId;
8
+ }
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  export type { VoyantPermission } from "@voyantjs/core";
2
2
  export { createApp } from "./app.js";
3
3
  export type { SessionAuthContext } from "./auth/index.js";
4
- export { extractBearerToken, generateNumericCode, randomBytesHex, sha256Base64Url, sha256Hex, unsignCookie, verifySession, } from "./auth/index.js";
5
- export { consoleLoggerProvider, cors, db, errorBoundary, LIVE_LIMITS, logger, rateLimit, requestId, requireActor, requireAuth, requirePermission, } from "./middleware/index.js";
4
+ export { extractBearerToken, generateNumericCode, randomBytesHex, requireUserId, sha256Base64Url, sha256Hex, unsignCookie, verifySession, } from "./auth/index.js";
5
+ export { consoleLoggerProvider, cors, db, errorBoundary, handleApiError, LIVE_LIMITS, logger, rateLimit, requestId, requireActor, requireAuth, requirePermission, } from "./middleware/index.js";
6
6
  export type { HonoExtension, HonoModule } from "./module.js";
7
- export type { ExpandedHonoPlugins, HonoPlugin } from "./plugin.js";
8
- export { defineHonoPlugin, expandHonoPlugins } from "./plugin.js";
9
- export type { DbFactory, LogEntry, LoggerProvider, VoyantAppConfig, VoyantAuthIntegration, VoyantAuthPermissionArgs, VoyantAuthResolveArgs, VoyantBindings, VoyantDb, VoyantExecutionContext, VoyantRequestAuthContext, VoyantVariables, } from "./types.js";
7
+ export type { ExpandedHonoBundles, ExpandedHonoPlugins, HonoBundle, HonoPlugin, } from "./plugin.js";
8
+ export { defineHonoBundle, defineHonoPlugin, expandHonoBundles, expandHonoPlugins, } from "./plugin.js";
9
+ export type { DbFactory, LogEntry, LoggerProvider, VoyantAppConfig, VoyantAuthIntegration, VoyantAuthPermissionArgs, VoyantAuthResolveArgs, VoyantBindings, VoyantDb, VoyantExecutionContext, VoyantQueryRuntime, VoyantRequestAuthContext, VoyantVariables, } from "./types.js";
10
+ export { ApiHttpError, ForbiddenApiError, normalizeValidationError, parseJsonBody, parseOptionalJsonBody, parseQuery, RequestValidationError, UnauthorizedApiError, } from "./validation.js";
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACpC,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,eAAe,EACf,SAAS,EACT,YAAY,EACZ,aAAa,GACd,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,qBAAqB,EACrB,IAAI,EACJ,EAAE,EACF,aAAa,EACb,WAAW,EACX,MAAM,EACN,SAAS,EACT,SAAS,EACT,YAAY,EACZ,WAAW,EACX,iBAAiB,GAClB,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAC5D,YAAY,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAClE,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AACjE,YAAY,EACV,SAAS,EACT,QAAQ,EACR,cAAc,EACd,eAAe,EACf,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,cAAc,EACd,QAAQ,EACR,sBAAsB,EACtB,wBAAwB,EACxB,eAAe,GAChB,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACpC,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,eAAe,EACf,SAAS,EACT,YAAY,EACZ,aAAa,GACd,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,qBAAqB,EACrB,IAAI,EACJ,EAAE,EACF,aAAa,EACb,cAAc,EACd,WAAW,EACX,MAAM,EACN,SAAS,EACT,SAAS,EACT,YAAY,EACZ,WAAW,EACX,iBAAiB,GAClB,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAC5D,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,UAAU,EACV,UAAU,GACX,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,aAAa,CAAA;AACpB,YAAY,EACV,SAAS,EACT,QAAQ,EACR,cAAc,EACd,eAAe,EACf,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,cAAc,EACd,QAAQ,EACR,sBAAsB,EACtB,kBAAkB,EAClB,wBAAwB,EACxB,eAAe,GAChB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,wBAAwB,EACxB,aAAa,EACb,qBAAqB,EACrB,UAAU,EACV,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,iBAAiB,CAAA"}
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { createApp } from "./app.js";
2
- export { extractBearerToken, generateNumericCode, randomBytesHex, sha256Base64Url, sha256Hex, unsignCookie, verifySession, } from "./auth/index.js";
3
- export { consoleLoggerProvider, cors, db, errorBoundary, LIVE_LIMITS, logger, rateLimit, requestId, requireActor, requireAuth, requirePermission, } from "./middleware/index.js";
4
- export { defineHonoPlugin, expandHonoPlugins } from "./plugin.js";
2
+ export { extractBearerToken, generateNumericCode, randomBytesHex, requireUserId, sha256Base64Url, sha256Hex, unsignCookie, verifySession, } from "./auth/index.js";
3
+ export { consoleLoggerProvider, cors, db, errorBoundary, handleApiError, LIVE_LIMITS, logger, rateLimit, requestId, requireActor, requireAuth, requirePermission, } from "./middleware/index.js";
4
+ export { defineHonoBundle, defineHonoPlugin, expandHonoBundles, expandHonoPlugins, } from "./plugin.js";
5
+ export { ApiHttpError, ForbiddenApiError, normalizeValidationError, parseJsonBody, parseOptionalJsonBody, parseQuery, RequestValidationError, UnauthorizedApiError, } from "./validation.js";
@@ -1,4 +1,5 @@
1
- import type { MiddlewareHandler } from "hono";
1
+ import type { Context, MiddlewareHandler } from "hono";
2
2
  export declare const requestId: MiddlewareHandler;
3
+ export declare function handleApiError(err: unknown, c: Context): Response;
3
4
  export declare const errorBoundary: MiddlewareHandler;
4
5
  //# sourceMappingURL=error-boundary.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"error-boundary.d.ts","sourceRoot":"","sources":["../../src/middleware/error-boundary.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAA;AAU7C,eAAO,MAAM,SAAS,EAAE,iBAKvB,CAAA;AAID,eAAO,MAAM,aAAa,EAAE,iBAmC3B,CAAA"}
1
+ {"version":3,"file":"error-boundary.d.ts","sourceRoot":"","sources":["../../src/middleware/error-boundary.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAA;AAYtD,eAAO,MAAM,SAAS,EAAE,iBAKvB,CAAA;AAID,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,QAAQ,CA+CjE;AAED,eAAO,MAAM,aAAa,EAAE,iBAM3B,CAAA"}
@@ -1,4 +1,5 @@
1
1
  import { apiErrorSchema } from "@voyantjs/types";
2
+ import { normalizeValidationError } from "../validation.js";
2
3
  function generateRequestId() {
3
4
  const bytes = new Uint8Array(16);
4
5
  crypto.getRandomValues(bytes);
@@ -13,35 +14,52 @@ export const requestId = async (c, next) => {
13
14
  await next();
14
15
  };
15
16
  const REDACT_HEADERS = new Set(["authorization", "x-api-key"]);
17
+ export function handleApiError(err, c) {
18
+ const id = c.res.headers.get("X-Request-Id") || generateRequestId();
19
+ const apiError = normalizeValidationError(err);
20
+ const errRecord = err instanceof Object ? err : {};
21
+ const code = apiError?.code ?? (typeof errRecord.code === "string" ? errRecord.code : undefined);
22
+ const status = apiError?.status ?? (typeof errRecord.status === "number" ? errRecord.status : 500);
23
+ const details = apiError?.details ??
24
+ (status < 500 && errRecord.details && typeof errRecord.details === "object"
25
+ ? errRecord.details
26
+ : undefined);
27
+ const errorMessage = status < 500
28
+ ? (apiError?.message ??
29
+ (typeof errRecord.message === "string" ? errRecord.message : "Bad Request"))
30
+ : "Internal Server Error";
31
+ try {
32
+ const headers = {};
33
+ c.req.raw.headers.forEach((value, key) => {
34
+ const lowerKey = key.toLowerCase();
35
+ headers[lowerKey] = REDACT_HEADERS.has(lowerKey) ? "[redacted]" : value;
36
+ });
37
+ console.error("[API:error]", {
38
+ id,
39
+ status,
40
+ code,
41
+ path: c.req.path,
42
+ method: c.req.method,
43
+ headers,
44
+ err: err instanceof Error ? err.message : String(err),
45
+ });
46
+ }
47
+ catch {
48
+ /* ignore logging errors */
49
+ }
50
+ const statusCode = status >= 100 && status <= 599 ? status : 500;
51
+ return new Response(JSON.stringify(apiErrorSchema.parse({ error: errorMessage, code, requestId: id, details })), {
52
+ status: statusCode,
53
+ headers: {
54
+ "content-type": "application/json",
55
+ },
56
+ });
57
+ }
16
58
  export const errorBoundary = async (c, next) => {
17
59
  try {
18
60
  await next();
19
61
  }
20
62
  catch (err) {
21
- const id = c.res.headers.get("X-Request-Id") || generateRequestId();
22
- const errRecord = err instanceof Object ? err : {};
23
- const code = typeof errRecord.code === "string" ? errRecord.code : undefined;
24
- const status = typeof errRecord.status === "number" ? errRecord.status : 500;
25
- try {
26
- const headers = {};
27
- c.req.raw.headers.forEach((value, key) => {
28
- const lowerKey = key.toLowerCase();
29
- headers[lowerKey] = REDACT_HEADERS.has(lowerKey) ? "[redacted]" : value;
30
- });
31
- console.error("[API:error]", {
32
- id,
33
- status,
34
- code,
35
- path: c.req.path,
36
- method: c.req.method,
37
- headers,
38
- err: err instanceof Error ? err.message : String(err),
39
- });
40
- }
41
- catch {
42
- /* ignore logging errors */
43
- }
44
- const statusCode = (status >= 100 && status <= 599 ? status : 500);
45
- return c.json(apiErrorSchema.parse({ error: "Internal Server Error", code, requestId: id }), statusCode);
63
+ return handleApiError(err, c);
46
64
  }
47
65
  };
@@ -1,7 +1,7 @@
1
1
  export { requireAuth } from "./auth.js";
2
2
  export { cors } from "./cors.js";
3
3
  export { db } from "./db.js";
4
- export { errorBoundary, requestId } from "./error-boundary.js";
4
+ export { errorBoundary, handleApiError, requestId } from "./error-boundary.js";
5
5
  export { consoleLoggerProvider, logger } from "./logger.js";
6
6
  export { LIVE_LIMITS, rateLimit } from "./rate-limit.js";
7
7
  export { requireActor } from "./require-actor.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA;AAC5B,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA;AAC5B,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC9E,OAAO,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA"}
@@ -1,7 +1,7 @@
1
1
  export { requireAuth } from "./auth.js";
2
2
  export { cors } from "./cors.js";
3
3
  export { db } from "./db.js";
4
- export { errorBoundary, requestId } from "./error-boundary.js";
4
+ export { errorBoundary, handleApiError, requestId } from "./error-boundary.js";
5
5
  export { consoleLoggerProvider, logger } from "./logger.js";
6
6
  export { LIVE_LIMITS, rateLimit } from "./rate-limit.js";
7
7
  export { requireActor } from "./require-actor.js";
@@ -1 +1 @@
1
- {"version":3,"file":"require-permission.d.ts","sourceRoot":"","sources":["../../src/middleware/require-permission.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAA;AAE7C,OAAO,KAAK,EAAE,SAAS,EAAE,qBAAqB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAcpG,wBAAgB,iBAAiB,CAAC,SAAS,SAAS,cAAc,EAChE,SAAS,EAAE,SAAS,CAAC,SAAS,CAAC,EAC/B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE;IACL,IAAI,CAAC,EAAE,qBAAqB,CAAC,SAAS,CAAC,CAAA;CACxC,GACA,iBAAiB,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAA;IACnB,SAAS,EAAE,eAAe,CAAA;CAC3B,CAAC,CA6CD"}
1
+ {"version":3,"file":"require-permission.d.ts","sourceRoot":"","sources":["../../src/middleware/require-permission.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAA;AAG7C,OAAO,KAAK,EAAE,SAAS,EAAE,qBAAqB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAepG,wBAAgB,iBAAiB,CAAC,SAAS,SAAS,cAAc,EAChE,SAAS,EAAE,SAAS,CAAC,SAAS,CAAC,EAC/B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE;IACL,IAAI,CAAC,EAAE,qBAAqB,CAAC,SAAS,CAAC,CAAA;CACxC,GACA,iBAAiB,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAA;IACnB,SAAS,EAAE,eAAe,CAAA;CAC3B,CAAC,CA0CD"}
@@ -1,3 +1,5 @@
1
+ import { requireUserId } from "../auth/require-user.js";
2
+ import { ForbiddenApiError } from "../validation.js";
1
3
  function hasScope(scopes, permission) {
2
4
  if (!scopes || scopes.length === 0)
3
5
  return false;
@@ -17,10 +19,7 @@ export function requirePermission(dbFactory, resource, action, opts) {
17
19
  if (hasScope(scopes, permission)) {
18
20
  return next();
19
21
  }
20
- const userId = c.get("userId");
21
- if (!userId) {
22
- return c.json({ error: "Unauthorized" }, 401);
23
- }
22
+ const userId = requireUserId(c);
24
23
  if (!opts?.auth?.hasPermission) {
25
24
  return c.json({ error: "No auth permission checker configured" }, 500);
26
25
  }
@@ -41,7 +40,7 @@ export function requirePermission(dbFactory, resource, action, opts) {
41
40
  permission,
42
41
  });
43
42
  if (!allowed) {
44
- return c.json({ error: "Forbidden" }, 403);
43
+ throw new ForbiddenApiError();
45
44
  }
46
45
  return next();
47
46
  };
package/dist/module.d.ts CHANGED
@@ -14,6 +14,13 @@ export interface HonoModule {
14
14
  adminRoutes?: Hono<any>;
15
15
  /** Customer/partner/supplier-facing routes — mounted at `/v1/public/{module.name}`. */
16
16
  publicRoutes?: Hono<any>;
17
+ /**
18
+ * Optional override for the public mount path relative to `/v1/public`.
19
+ *
20
+ * Defaults to `{module.name}`. Use `"/"` to mount a module directly at the
21
+ * public root and omit the extra module segment.
22
+ */
23
+ publicPath?: string;
17
24
  }
18
25
  export interface HonoExtension {
19
26
  extension: Extension;
@@ -23,5 +30,12 @@ export interface HonoExtension {
23
30
  adminRoutes?: Hono<any>;
24
31
  /** Customer/partner/supplier-facing routes — mounted at `/v1/public/{extension.module}`. */
25
32
  publicRoutes?: Hono<any>;
33
+ /**
34
+ * Optional override for the public mount path relative to `/v1/public`.
35
+ *
36
+ * Defaults to `{extension.module}`. Use `"/"` to mount an extension directly
37
+ * at the public root and omit the extra module segment.
38
+ */
39
+ publicPath?: string;
26
40
  }
27
41
  //# sourceMappingURL=module.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AACvD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAEhC,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAA;IACd;;;;;;OAMG;IAEH,MAAM,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;IAClB,kEAAkE;IAElE,WAAW,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;IACvB,uFAAuF;IAEvF,YAAY,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,SAAS,CAAA;IACpB,0DAA0D;IAE1D,MAAM,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;IAClB,uEAAuE;IAEvE,WAAW,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;IACvB,4FAA4F;IAE5F,YAAY,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;CACzB"}
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AACvD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAEhC,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAA;IACd;;;;;;OAMG;IAEH,MAAM,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;IAClB,kEAAkE;IAElE,WAAW,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;IACvB,uFAAuF;IAEvF,YAAY,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;IACxB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,SAAS,CAAA;IACpB,0DAA0D;IAE1D,MAAM,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;IAClB,uEAAuE;IAEvE,WAAW,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;IACvB,4FAA4F;IAE5F,YAAY,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;IACxB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB"}
package/dist/plugin.d.ts CHANGED
@@ -1,21 +1,24 @@
1
- import type { LinkDefinition, Subscriber } from "@voyantjs/core";
1
+ import type { BootstrapHandler, LinkDefinition, Subscriber } from "@voyantjs/core";
2
2
  import type { HonoExtension, HonoModule } from "./module.js";
3
3
  /**
4
- * Hono-flavoured plugin bundle.
4
+ * Hono-flavoured bundle contribution surface.
5
5
  *
6
- * Unlike the transport-agnostic `Plugin` in `@voyantjs/core`, a
7
- * `HonoPlugin` contributes {@link HonoModule} / {@link HonoExtension}
8
- * wrappers that can carry HTTP routes.
6
+ * `@voyantjs/hono` is the default HTTP transport adapter for Voyant. The
7
+ * preferred `HonoBundle` term describes reusable packages that contribute
8
+ * {@link HonoModule} / {@link HonoExtension} wrappers that can carry HTTP
9
+ * routes. `HonoPlugin` remains as a compatibility alias for the same shape.
9
10
  *
10
11
  * Registered via `createApp({ plugins: [...] })` — the app factory expands
11
- * each plugin into the underlying modules, extensions, subscribers, and link
12
+ * each bundle into the underlying modules, extensions, subscribers, and link
12
13
  * definitions before mounting them.
13
14
  */
14
- export interface HonoPlugin {
15
- /** Unique plugin identifier (e.g. "payload-cms", "bokun"). */
15
+ export interface HonoBundle {
16
+ /** Unique bundle identifier (e.g. "payload-cms", "bokun"). */
16
17
  name: string;
17
18
  /** Optional version tag for diagnostics. */
18
19
  version?: string;
20
+ /** Optional lazy runtime bootstrap executed once per app/isolate. */
21
+ bootstrap?: BootstrapHandler;
19
22
  /** Hono modules (module + routes) contributed by the plugin. */
20
23
  modules?: HonoModule[];
21
24
  /** Hono extensions (extension + routes) contributed by the plugin. */
@@ -25,20 +28,28 @@ export interface HonoPlugin {
25
28
  /** Link definitions contributed by the plugin. */
26
29
  links?: LinkDefinition[];
27
30
  }
31
+ /** @deprecated Prefer {@link HonoBundle}. */
32
+ export type HonoPlugin = HonoBundle;
28
33
  /**
29
- * Identity helper — returns the plugin unchanged, purely for IDE inference.
34
+ * Identity helper — returns the bundle unchanged, purely for IDE inference.
30
35
  */
31
- export declare function defineHonoPlugin<P extends HonoPlugin>(plugin: P): P;
32
- export interface ExpandedHonoPlugins {
36
+ export declare function defineHonoBundle<P extends HonoBundle>(bundle: P): P;
37
+ /** @deprecated Prefer {@link defineHonoBundle}. */
38
+ export declare const defineHonoPlugin: typeof defineHonoBundle;
39
+ export interface ExpandedHonoBundles {
33
40
  modules: HonoModule[];
34
41
  extensions: HonoExtension[];
35
42
  subscribers: Subscriber[];
36
43
  links: LinkDefinition[];
37
44
  }
45
+ /** @deprecated Prefer {@link ExpandedHonoBundles}. */
46
+ export type ExpandedHonoPlugins = ExpandedHonoBundles;
38
47
  /**
39
- * Flatten a list of {@link HonoPlugin} values into their constituent pieces.
48
+ * Flatten a list of {@link HonoBundle} values into their constituent pieces.
40
49
  *
41
- * Throws if two plugins declare the same `name`.
50
+ * Throws if two bundles declare the same `name`.
42
51
  */
43
- export declare function expandHonoPlugins(plugins: ReadonlyArray<HonoPlugin>): ExpandedHonoPlugins;
52
+ export declare function expandHonoBundles(bundles: ReadonlyArray<HonoBundle>): ExpandedHonoBundles;
53
+ /** @deprecated Prefer {@link expandHonoBundles}. */
54
+ export declare const expandHonoPlugins: typeof expandHonoBundles;
44
55
  //# sourceMappingURL=plugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAEhE,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE5D;;;;;;;;;;GAUG;AACH,MAAM,WAAW,UAAU;IACzB,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAA;IACZ,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gEAAgE;IAChE,OAAO,CAAC,EAAE,UAAU,EAAE,CAAA;IACtB,sEAAsE;IACtE,UAAU,CAAC,EAAE,aAAa,EAAE,CAAA;IAC5B,wEAAwE;IACxE,WAAW,CAAC,EAAE,UAAU,EAAE,CAAA;IAC1B,kDAAkD;IAClD,KAAK,CAAC,EAAE,cAAc,EAAE,CAAA;CACzB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,UAAU,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAEnE;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,UAAU,EAAE,CAAA;IACrB,UAAU,EAAE,aAAa,EAAE,CAAA;IAC3B,WAAW,EAAE,UAAU,EAAE,CAAA;IACzB,KAAK,EAAE,cAAc,EAAE,CAAA;CACxB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,aAAa,CAAC,UAAU,CAAC,GAAG,mBAAmB,CAoBzF"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAElF,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE5D;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,UAAU;IACzB,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAA;IACZ,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,qEAAqE;IACrE,SAAS,CAAC,EAAE,gBAAgB,CAAA;IAC5B,gEAAgE;IAChE,OAAO,CAAC,EAAE,UAAU,EAAE,CAAA;IACtB,sEAAsE;IACtE,UAAU,CAAC,EAAE,aAAa,EAAE,CAAA;IAC5B,wEAAwE;IACxE,WAAW,CAAC,EAAE,UAAU,EAAE,CAAA;IAC1B,kDAAkD;IAClD,KAAK,CAAC,EAAE,cAAc,EAAE,CAAA;CACzB;AAED,6CAA6C;AAC7C,MAAM,MAAM,UAAU,GAAG,UAAU,CAAA;AAEnC;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,UAAU,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAEnE;AAED,mDAAmD;AACnD,eAAO,MAAM,gBAAgB,yBAAmB,CAAA;AAEhD,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,UAAU,EAAE,CAAA;IACrB,UAAU,EAAE,aAAa,EAAE,CAAA;IAC3B,WAAW,EAAE,UAAU,EAAE,CAAA;IACzB,KAAK,EAAE,cAAc,EAAE,CAAA;CACxB;AAED,sDAAsD;AACtD,MAAM,MAAM,mBAAmB,GAAG,mBAAmB,CAAA;AAErD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,aAAa,CAAC,UAAU,CAAC,GAAG,mBAAmB,CAoBzF;AAED,oDAAoD;AACpD,eAAO,MAAM,iBAAiB,0BAAoB,CAAA"}
package/dist/plugin.js CHANGED
@@ -1,33 +1,37 @@
1
1
  /**
2
- * Identity helper — returns the plugin unchanged, purely for IDE inference.
2
+ * Identity helper — returns the bundle unchanged, purely for IDE inference.
3
3
  */
4
- export function defineHonoPlugin(plugin) {
5
- return plugin;
4
+ export function defineHonoBundle(bundle) {
5
+ return bundle;
6
6
  }
7
+ /** @deprecated Prefer {@link defineHonoBundle}. */
8
+ export const defineHonoPlugin = defineHonoBundle;
7
9
  /**
8
- * Flatten a list of {@link HonoPlugin} values into their constituent pieces.
10
+ * Flatten a list of {@link HonoBundle} values into their constituent pieces.
9
11
  *
10
- * Throws if two plugins declare the same `name`.
12
+ * Throws if two bundles declare the same `name`.
11
13
  */
12
- export function expandHonoPlugins(plugins) {
14
+ export function expandHonoBundles(bundles) {
13
15
  const seen = new Set();
14
16
  const modules = [];
15
17
  const extensions = [];
16
18
  const subscribers = [];
17
19
  const links = [];
18
- for (const plugin of plugins) {
19
- if (seen.has(plugin.name)) {
20
- throw new Error(`Duplicate plugin name: "${plugin.name}"`);
20
+ for (const bundle of bundles) {
21
+ if (seen.has(bundle.name)) {
22
+ throw new Error(`Duplicate bundle name: "${bundle.name}"`);
21
23
  }
22
- seen.add(plugin.name);
23
- if (plugin.modules)
24
- modules.push(...plugin.modules);
25
- if (plugin.extensions)
26
- extensions.push(...plugin.extensions);
27
- if (plugin.subscribers)
28
- subscribers.push(...plugin.subscribers);
29
- if (plugin.links)
30
- links.push(...plugin.links);
24
+ seen.add(bundle.name);
25
+ if (bundle.modules)
26
+ modules.push(...bundle.modules);
27
+ if (bundle.extensions)
28
+ extensions.push(...bundle.extensions);
29
+ if (bundle.subscribers)
30
+ subscribers.push(...bundle.subscribers);
31
+ if (bundle.links)
32
+ links.push(...bundle.links);
31
33
  }
32
34
  return { modules, extensions, subscribers, links };
33
35
  }
36
+ /** @deprecated Prefer {@link expandHonoBundles}. */
37
+ export const expandHonoPlugins = expandHonoBundles;
package/dist/types.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import type { VoyantVariables as CoreVoyantVariables, ModuleContainer, VoyantAuthContext, VoyantPermission } from "@voyantjs/core";
1
+ import type { VoyantVariables as CoreVoyantVariables, EventBus, LinkService, ModuleContainer, QueryGraphContext, QueryRunner, VoyantAuthContext, VoyantPermission } from "@voyantjs/core";
2
2
  import type { KVStore } from "@voyantjs/utils/cache";
3
3
  import type { NeonHttpDatabase } from "drizzle-orm/neon-http";
4
4
  import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
5
5
  import type { Hono } from "hono";
6
6
  import type { HonoExtension, HonoModule } from "./module.js";
7
- import type { HonoPlugin } from "./plugin.js";
7
+ import type { HonoBundle } from "./plugin.js";
8
8
  export interface VoyantExecutionContext {
9
9
  waitUntil?: (promise: Promise<unknown>) => void;
10
10
  passThroughOnException?: () => void;
@@ -22,9 +22,16 @@ export interface VoyantBindings {
22
22
  CACHE?: KVStore;
23
23
  }
24
24
  export type VoyantDb = PostgresJsDatabase | NeonHttpDatabase;
25
+ export type VoyantQueryRuntime = QueryRunner;
25
26
  export type VoyantVariables = CoreVoyantVariables & {
26
27
  db: VoyantDb;
28
+ /** Shared app/runtime container for explicit service resolution. */
27
29
  container: ModuleContainer;
30
+ eventBus: EventBus;
31
+ /** Shared cross-module link runtime, when the app wires one in. */
32
+ link?: LinkService;
33
+ /** Shared cross-module query runtime, when the app wires one in. */
34
+ query?: VoyantQueryRuntime;
28
35
  };
29
36
  export type DbFactory<TBindings extends VoyantBindings = VoyantBindings> = (env: TBindings) => VoyantDb;
30
37
  export type VoyantRequestAuthContext = VoyantAuthContext & {
@@ -60,7 +67,10 @@ export interface VoyantAppConfig<TBindings extends VoyantBindings = VoyantBindin
60
67
  db: DbFactory<TBindings>;
61
68
  modules?: HonoModule[];
62
69
  extensions?: HonoExtension[];
63
- plugins?: HonoPlugin[];
70
+ plugins?: HonoBundle[];
71
+ eventBus?: EventBus;
72
+ link?: LinkService;
73
+ query?: QueryGraphContext | VoyantQueryRuntime;
64
74
  auth?: VoyantAuthIntegration<TBindings>;
65
75
  publicPaths?: string[];
66
76
  logger?: LoggerProvider;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { Context } from "hono";
2
+ import { type ZodType } from "zod";
3
+ export declare class ApiHttpError extends Error {
4
+ readonly status: number;
5
+ readonly code?: string;
6
+ readonly details?: Record<string, unknown>;
7
+ constructor(message: string, options: {
8
+ status: number;
9
+ code?: string;
10
+ details?: Record<string, unknown>;
11
+ });
12
+ }
13
+ export declare class RequestValidationError extends ApiHttpError {
14
+ constructor(message: string, details?: Record<string, unknown>);
15
+ }
16
+ export declare class UnauthorizedApiError extends ApiHttpError {
17
+ constructor(message?: string);
18
+ }
19
+ export declare class ForbiddenApiError extends ApiHttpError {
20
+ constructor(message?: string);
21
+ }
22
+ export declare function parseJsonBody<T>(c: Context, schema: ZodType<T>, options?: {
23
+ invalidJsonMessage?: string;
24
+ invalidBodyMessage?: string;
25
+ }): Promise<T>;
26
+ export declare function parseOptionalJsonBody<T>(c: Context, schema: ZodType<T>, options?: {
27
+ defaultValue?: unknown;
28
+ invalidBodyMessage?: string;
29
+ }): Promise<T>;
30
+ export declare function parseQuery<T>(c: Context, schema: ZodType<T>, options?: {
31
+ invalidQueryMessage?: string;
32
+ }): T;
33
+ export declare function normalizeValidationError(error: unknown): ApiHttpError | undefined;
34
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,EAAY,KAAK,OAAO,EAAE,MAAM,KAAK,CAAA;AAE5C,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;gBAGxC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;QACP,MAAM,EAAE,MAAM,CAAA;QACd,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAClC;CAQJ;AAED,qBAAa,sBAAuB,SAAQ,YAAY;gBAC1C,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAQ/D;AAED,qBAAa,oBAAqB,SAAQ,YAAY;gBACxC,OAAO,SAAiB;CAOrC;AAED,qBAAa,iBAAkB,SAAQ,YAAY;gBACrC,OAAO,SAAc;CAOlC;AAqBD,wBAAsB,aAAa,CAAC,CAAC,EACnC,CAAC,EAAE,OAAO,EACV,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAClB,OAAO,CAAC,EAAE;IAAE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAAE,GACrE,OAAO,CAAC,CAAC,CAAC,CAUZ;AAED,wBAAsB,qBAAqB,CAAC,CAAC,EAC3C,CAAC,EAAE,OAAO,EACV,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAClB,OAAO,CAAC,EAAE;IACR,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B,GACA,OAAO,CAAC,CAAC,CAAC,CAUZ;AAED,wBAAgB,UAAU,CAAC,CAAC,EAC1B,CAAC,EAAE,OAAO,EACV,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAClB,OAAO,CAAC,EAAE;IAAE,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAAE,GACzC,CAAC,CAMH;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,GAAG,SAAS,CAUjF"}
@@ -0,0 +1,86 @@
1
+ import { ZodError } from "zod";
2
+ export class ApiHttpError extends Error {
3
+ status;
4
+ code;
5
+ details;
6
+ constructor(message, options) {
7
+ super(message);
8
+ this.name = "ApiHttpError";
9
+ this.status = options.status;
10
+ this.code = options.code;
11
+ this.details = options.details;
12
+ }
13
+ }
14
+ export class RequestValidationError extends ApiHttpError {
15
+ constructor(message, details) {
16
+ super(message, {
17
+ status: 400,
18
+ code: "invalid_request",
19
+ details,
20
+ });
21
+ this.name = "RequestValidationError";
22
+ }
23
+ }
24
+ export class UnauthorizedApiError extends ApiHttpError {
25
+ constructor(message = "Unauthorized") {
26
+ super(message, {
27
+ status: 401,
28
+ code: "unauthorized",
29
+ });
30
+ this.name = "UnauthorizedApiError";
31
+ }
32
+ }
33
+ export class ForbiddenApiError extends ApiHttpError {
34
+ constructor(message = "Forbidden") {
35
+ super(message, {
36
+ status: 403,
37
+ code: "forbidden",
38
+ });
39
+ this.name = "ForbiddenApiError";
40
+ }
41
+ }
42
+ function toValidationError(error, fallbackMessage = "Invalid request") {
43
+ return new RequestValidationError(error.issues[0]?.message ?? fallbackMessage, {
44
+ issues: error.issues,
45
+ fields: error.flatten(),
46
+ });
47
+ }
48
+ function validate(schema, input, fallbackMessage) {
49
+ const parsed = schema.safeParse(input);
50
+ if (!parsed.success) {
51
+ throw toValidationError(parsed.error, fallbackMessage);
52
+ }
53
+ return parsed.data;
54
+ }
55
+ export async function parseJsonBody(c, schema, options) {
56
+ let input;
57
+ try {
58
+ input = await c.req.json();
59
+ }
60
+ catch {
61
+ throw new RequestValidationError(options?.invalidJsonMessage ?? "Invalid JSON body");
62
+ }
63
+ return validate(schema, input, options?.invalidBodyMessage);
64
+ }
65
+ export async function parseOptionalJsonBody(c, schema, options) {
66
+ let input;
67
+ try {
68
+ input = await c.req.json();
69
+ }
70
+ catch {
71
+ input = options?.defaultValue ?? {};
72
+ }
73
+ return validate(schema, input, options?.invalidBodyMessage);
74
+ }
75
+ export function parseQuery(c, schema, options) {
76
+ return validate(schema, Object.fromEntries(new URL(c.req.url).searchParams), options?.invalidQueryMessage ?? "Invalid query parameters");
77
+ }
78
+ export function normalizeValidationError(error) {
79
+ if (error instanceof ApiHttpError) {
80
+ return error;
81
+ }
82
+ if (error instanceof ZodError) {
83
+ return toValidationError(error);
84
+ }
85
+ return undefined;
86
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voyantjs/hono",
3
- "version": "0.4.5",
3
+ "version": "0.6.0",
4
4
  "license": "FSL-1.1-Apache-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -76,10 +76,11 @@
76
76
  "dependencies": {
77
77
  "drizzle-orm": "^0.45.2",
78
78
  "hono": "^4.12.10",
79
- "@voyantjs/core": "0.4.5",
80
- "@voyantjs/db": "0.4.5",
81
- "@voyantjs/types": "0.4.5",
82
- "@voyantjs/utils": "0.4.5"
79
+ "zod": "^4.3.6",
80
+ "@voyantjs/core": "0.6.0",
81
+ "@voyantjs/db": "0.6.0",
82
+ "@voyantjs/types": "0.6.0",
83
+ "@voyantjs/utils": "0.6.0"
83
84
  },
84
85
  "devDependencies": {
85
86
  "@cloudflare/workers-types": "^4.20260403.1",