@voyant-travel/hono 0.110.3 → 0.112.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/dist/app.d.ts CHANGED
@@ -36,7 +36,14 @@ export interface VoyantAppExtensions<TBindings = unknown> {
36
36
  */
37
37
  eventBus: import("@voyant-travel/core").EventBus;
38
38
  }
39
- export declare function createApp<TBindings extends VoyantBindings>(config: VoyantAppConfig<TBindings>): Hono<{
39
+ /**
40
+ * Low-level app factory: given an already-resolved `modules`/`extensions` set
41
+ * (plus middleware config), build the Hono app. Most deployments use the
42
+ * config-driven `createApp` (see `create-app.ts`), which derives the modules
43
+ * from a manifest + registry + capabilities and delegates here. Use `mountApp`
44
+ * directly only when you have the resolved set in hand (tests, advanced hosts).
45
+ */
46
+ export declare function mountApp<TBindings extends VoyantBindings>(config: VoyantAppConfig<TBindings>): Hono<{
40
47
  Bindings: TBindings;
41
48
  Variables: VoyantVariables;
42
49
  }> & VoyantAppExtensions<TBindings>;
package/dist/app.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AA4B3B,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAY,eAAe,EAAE,MAAM,YAAY,CAAA;AA2C5F;;;;;;;;;;GAUG;AACH,MAAM,WAAW,mBAAmB,CAAC,SAAS,GAAG,OAAO;IACtD;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,QAAQ,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1C;;;;;;;;OAQG;IACH,QAAQ,EAAE,OAAO,qBAAqB,EAAE,QAAQ,CAAA;CACjD;AAED,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,GAAG,mBAAmB,CAAC,SAAS,CAAC,CA6Z5F"}
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AA6B3B,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAY,eAAe,EAAE,MAAM,YAAY,CAAA;AA2C5F;;;;;;;;;;GAUG;AACH,MAAM,WAAW,mBAAmB,CAAC,SAAS,GAAG,OAAO;IACtD;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,QAAQ,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1C;;;;;;;;OAQG;IACH,QAAQ,EAAE,OAAO,qBAAqB,EAAE,QAAQ,CAAA;CACjD;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,SAAS,SAAS,cAAc,EACvD,MAAM,EAAE,eAAe,CAAC,SAAS,CAAC,GACjC,IAAI,CAAC;IAAE,QAAQ,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,eAAe,CAAA;CAAE,CAAC,GAAG,mBAAmB,CAAC,SAAS,CAAC,CA6a5F"}
package/dist/app.js CHANGED
@@ -2,6 +2,7 @@ import { createContainer, createEventBus, createQueryRunner, } from "@voyant-tra
2
2
  import { createOutboxEventStore } from "@voyant-travel/db/outbox";
3
3
  import { Hono } from "hono";
4
4
  import { containerToServiceResolver, makeFrameworkLogger, wireWorkflowRuntime, } from "./app-workflows.js";
5
+ import { mountLazyRoutePaths, mountLazyRoutesAt } from "./lazy-routes.js";
5
6
  import { createPathDbSelector } from "./lib/db-selector.js";
6
7
  import { tryGetExecutionCtx } from "./lib/execution-ctx.js";
7
8
  import { matchesPublicPath, normalizePathname } from "./lib/public-paths.js";
@@ -41,7 +42,14 @@ function buildRateLimitPolicy(config, env, bucket, defaults) {
41
42
  store: resolveConfiguredRateLimitStore(config, env) ?? resolveRateLimitStore({ env }),
42
43
  };
43
44
  }
44
- export function createApp(config) {
45
+ /**
46
+ * Low-level app factory: given an already-resolved `modules`/`extensions` set
47
+ * (plus middleware config), build the Hono app. Most deployments use the
48
+ * config-driven `createApp` (see `create-app.ts`), which derives the modules
49
+ * from a manifest + registry + capabilities and delegates here. Use `mountApp`
50
+ * directly only when you have the resolved set in hand (tests, advanced hosts).
51
+ */
52
+ export function mountApp(config) {
45
53
  const app = new Hono();
46
54
  app.onError(handleApiError);
47
55
  // Expand plugins into their constituent modules/extensions before mounting
@@ -358,11 +366,22 @@ export function createApp(config) {
358
366
  }
359
367
  // Mount module routes
360
368
  for (const mod of allModules) {
369
+ const adminPrefix = `/v1/admin/${mod.module.name}`;
370
+ const publicPrefix = resolveSurfaceMountPath("/v1/public", mod.publicPath, mod.module.name);
361
371
  if (mod.adminRoutes) {
362
- app.route(`/v1/admin/${mod.module.name}`, mod.adminRoutes);
372
+ app.route(adminPrefix, mod.adminRoutes);
363
373
  }
364
374
  if (mod.publicRoutes) {
365
- app.route(resolveSurfaceMountPath("/v1/public", mod.publicPath, mod.module.name), mod.publicRoutes);
375
+ app.route(publicPrefix, mod.publicRoutes);
376
+ }
377
+ if (mod.lazyAdminRoutes) {
378
+ mountLazyRoutesAt(app, adminPrefix, mod.lazyAdminRoutes);
379
+ }
380
+ if (mod.lazyPublicRoutes) {
381
+ mountLazyRoutesAt(app, publicPrefix, mod.lazyPublicRoutes);
382
+ }
383
+ if (mod.lazyRoutes) {
384
+ mountLazyRoutePaths(app, mod.lazyRoutes.paths, mod.lazyRoutes.load);
366
385
  }
367
386
  if (mod.routes) {
368
387
  app.route(`/v1/${mod.module.name}`, mod.routes);
@@ -370,11 +389,22 @@ export function createApp(config) {
370
389
  }
371
390
  // Mount extension routes
372
391
  for (const ext of allExtensions) {
392
+ const adminPrefix = `/v1/admin/${ext.extension.module}`;
393
+ const publicPrefix = resolveSurfaceMountPath("/v1/public", ext.publicPath, ext.extension.module);
373
394
  if (ext.adminRoutes) {
374
- app.route(`/v1/admin/${ext.extension.module}`, ext.adminRoutes);
395
+ app.route(adminPrefix, ext.adminRoutes);
375
396
  }
376
397
  if (ext.publicRoutes) {
377
- app.route(resolveSurfaceMountPath("/v1/public", ext.publicPath, ext.extension.module), ext.publicRoutes);
398
+ app.route(publicPrefix, ext.publicRoutes);
399
+ }
400
+ if (ext.lazyAdminRoutes) {
401
+ mountLazyRoutesAt(app, adminPrefix, ext.lazyAdminRoutes);
402
+ }
403
+ if (ext.lazyPublicRoutes) {
404
+ mountLazyRoutesAt(app, publicPrefix, ext.lazyPublicRoutes);
405
+ }
406
+ if (ext.lazyRoutes) {
407
+ mountLazyRoutePaths(app, ext.lazyRoutes.paths, ext.lazyRoutes.load);
378
408
  }
379
409
  if (ext.routes) {
380
410
  app.route(`/v1/${ext.extension.module}`, ext.routes);
@@ -0,0 +1,36 @@
1
+ import { type CompositionManifest, type CompositionRegistry } from "./composition.js";
2
+ import type { VoyantAppConfig, VoyantBindings } from "./types.js";
3
+ /**
4
+ * Config-driven app config: the manifest + registry + capabilities replace the
5
+ * resolved `modules`/`extensions` of {@link VoyantAppConfig}. Everything else
6
+ * (db surfaces, auth, plugins, publicPaths, …) is unchanged.
7
+ */
8
+ export interface CreateAppConfig<TBindings extends VoyantBindings, TCapabilities> extends Omit<VoyantAppConfig<TBindings>, "modules" | "extensions"> {
9
+ /** Ordered module/extension manifest. */
10
+ manifest: CompositionManifest;
11
+ /** Maps each manifest entry to its factory. */
12
+ registry: CompositionRegistry<TCapabilities>;
13
+ /** Deployment capabilities passed to every registry factory. */
14
+ capabilities: TCapabilities;
15
+ }
16
+ /**
17
+ * The low-level config-driven front door. Derives the module/extension set from
18
+ * an explicit manifest + registry + capabilities, then mounts the app — instead
19
+ * of `composeFromManifest(...)` followed by `mountApp(...)`:
20
+ *
21
+ * export const app = createApp<CloudflareBindings, Providers>({
22
+ * manifest: runtimeManifest,
23
+ * registry: composition,
24
+ * capabilities: buildProviders(),
25
+ * db, auth, plugins, ...
26
+ * })
27
+ *
28
+ * Standard deployments usually call `@voyant-travel/framework`'s higher-level
29
+ * `createVoyantApp({ providers, modules })` (which assembles the framework-owned
30
+ * manifest + registry for you and delegates here) rather than this directly.
31
+ */
32
+ export declare function createApp<TBindings extends VoyantBindings, TCapabilities>(config: CreateAppConfig<TBindings, TCapabilities>): import("hono").Hono<{
33
+ Bindings: TBindings;
34
+ Variables: import("./types.js").VoyantVariables;
35
+ }, import("hono/types").BlankSchema, "/"> & import("./app.js").VoyantAppExtensions<TBindings>;
36
+ //# sourceMappingURL=create-app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-app.d.ts","sourceRoot":"","sources":["../src/create-app.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EAEzB,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEjE;;;;GAIG;AACH,MAAM,WAAW,eAAe,CAAC,SAAS,SAAS,cAAc,EAAE,aAAa,CAC9E,SAAQ,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC;IAClE,yCAAyC;IACzC,QAAQ,EAAE,mBAAmB,CAAA;IAC7B,+CAA+C;IAC/C,QAAQ,EAAE,mBAAmB,CAAC,aAAa,CAAC,CAAA;IAC5C,gEAAgE;IAChE,YAAY,EAAE,aAAa,CAAA;CAC5B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,SAAS,CAAC,SAAS,SAAS,cAAc,EAAE,aAAa,EACvE,MAAM,EAAE,eAAe,CAAC,SAAS,EAAE,aAAa,CAAC;;;8FAKlD"}
@@ -0,0 +1,23 @@
1
+ import { mountApp } from "./app.js";
2
+ import { composeFromManifest, } from "./composition.js";
3
+ /**
4
+ * The low-level config-driven front door. Derives the module/extension set from
5
+ * an explicit manifest + registry + capabilities, then mounts the app — instead
6
+ * of `composeFromManifest(...)` followed by `mountApp(...)`:
7
+ *
8
+ * export const app = createApp<CloudflareBindings, Providers>({
9
+ * manifest: runtimeManifest,
10
+ * registry: composition,
11
+ * capabilities: buildProviders(),
12
+ * db, auth, plugins, ...
13
+ * })
14
+ *
15
+ * Standard deployments usually call `@voyant-travel/framework`'s higher-level
16
+ * `createVoyantApp({ providers, modules })` (which assembles the framework-owned
17
+ * manifest + registry for you and delegates here) rather than this directly.
18
+ */
19
+ export function createApp(config) {
20
+ const { manifest, registry, capabilities, ...rest } = config;
21
+ const { modules, extensions } = composeFromManifest(manifest, registry, capabilities);
22
+ return mountApp({ ...rest, modules, extensions });
23
+ }
package/dist/index.d.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  export type { VoyantPermission } from "@voyant-travel/core";
2
- export { createApp } from "./app.js";
2
+ export { mountApp } from "./app.js";
3
3
  export type { SessionAuthContext } from "./auth/index.js";
4
4
  export { constantTimeEqual, extractBearerToken, generateNumericCode, randomBytesHex, requireUserId, sha256Base64Url, sha256Hex, unsignCookie, verifySession, } from "./auth/index.js";
5
+ export { type CreateAppConfig, createApp } from "./create-app.js";
5
6
  export type { DocumentDownloadEnvelope, DocumentDownloadResolution, DocumentDownloadResolver, DocumentDownloadResolverResult, StoredDocumentReference, } from "./document-download.js";
6
7
  export { resolveStoredDocumentDownload } from "./document-download.js";
8
+ export { createLazyRouteHandler, type LazyHonoRoutes, type LazyRoutesLoader, mountLazyRoutePaths, mountLazyRoutesAt, } from "./lazy-routes.js";
7
9
  export { createPathDbSelector, type PathDbSelectorOptions } from "./lib/db-selector.js";
8
10
  export { clientIpKey, consoleLoggerProvider, cors, DEFAULT_IDEMPOTENCY_TTL_MS, db, enforceRateLimit, errorBoundary, handleApiError, type IdempotencyKeyOptions, idempotencyKey, LIVE_LIMITS, logger, purgeExpiredIdempotencyKeys, rateLimit, requestId, requireActor, requireAuth, requirePermission, } from "./middleware/index.js";
9
11
  export type { HonoExtension, HonoModule } from "./module.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACpC,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,eAAe,EACf,SAAS,EACT,YAAY,EACZ,aAAa,GACd,MAAM,iBAAiB,CAAA;AACxB,YAAY,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,wBAAwB,EACxB,8BAA8B,EAC9B,uBAAuB,GACxB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,6BAA6B,EAAE,MAAM,wBAAwB,CAAA;AACtE,OAAO,EAAE,oBAAoB,EAAE,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AACvF,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,IAAI,EACJ,0BAA0B,EAC1B,EAAE,EACF,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,KAAK,qBAAqB,EAC1B,cAAc,EACd,WAAW,EACX,MAAM,EACN,2BAA2B,EAC3B,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,6BAA6B,EAC7B,6BAA6B,EAC7B,uBAAuB,EACvB,6BAA6B,GAC9B,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACL,2BAA2B,EAC3B,4BAA4B,EAC5B,+BAA+B,EAC/B,2BAA2B,GAC5B,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACL,KAAK,iCAAiC,EACtC,6CAA6C,EAC7C,iCAAiC,EACjC,sCAAsC,EACtC,kCAAkC,EAClC,KAAK,mCAAmC,EACxC,KAAK,8BAA8B,EACnC,KAAK,2BAA2B,EAChC,KAAK,gCAAgC,EACrC,KAAK,gCAAgC,EACrC,KAAK,kCAAkC,EACvC,KAAK,4BAA4B,EACjC,KAAK,sCAAsC,EAC3C,kCAAkC,EAClC,iCAAiC,GAClC,MAAM,+BAA+B,CAAA;AACtC,YAAY,EACV,SAAS,EACT,iBAAiB,EACjB,QAAQ,EACR,kBAAkB,EAClB,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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACnC,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,eAAe,EACf,SAAS,EACT,YAAY,EACZ,aAAa,GACd,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,KAAK,eAAe,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AACjE,YAAY,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,wBAAwB,EACxB,8BAA8B,EAC9B,uBAAuB,GACxB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,6BAA6B,EAAE,MAAM,wBAAwB,CAAA;AACtE,OAAO,EACL,sBAAsB,EACtB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,oBAAoB,EAAE,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AACvF,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,IAAI,EACJ,0BAA0B,EAC1B,EAAE,EACF,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,KAAK,qBAAqB,EAC1B,cAAc,EACd,WAAW,EACX,MAAM,EACN,2BAA2B,EAC3B,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,6BAA6B,EAC7B,6BAA6B,EAC7B,uBAAuB,EACvB,6BAA6B,GAC9B,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACL,2BAA2B,EAC3B,4BAA4B,EAC5B,+BAA+B,EAC/B,2BAA2B,GAC5B,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACL,KAAK,iCAAiC,EACtC,6CAA6C,EAC7C,iCAAiC,EACjC,sCAAsC,EACtC,kCAAkC,EAClC,KAAK,mCAAmC,EACxC,KAAK,8BAA8B,EACnC,KAAK,2BAA2B,EAChC,KAAK,gCAAgC,EACrC,KAAK,gCAAgC,EACrC,KAAK,kCAAkC,EACvC,KAAK,4BAA4B,EACjC,KAAK,sCAAsC,EAC3C,kCAAkC,EAClC,iCAAiC,GAClC,MAAM,+BAA+B,CAAA;AACtC,YAAY,EACV,SAAS,EACT,iBAAiB,EACjB,QAAQ,EACR,kBAAkB,EAClB,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,6 +1,8 @@
1
- export { createApp } from "./app.js";
1
+ export { mountApp } from "./app.js";
2
2
  export { constantTimeEqual, extractBearerToken, generateNumericCode, randomBytesHex, requireUserId, sha256Base64Url, sha256Hex, unsignCookie, verifySession, } from "./auth/index.js";
3
+ export { createApp } from "./create-app.js";
3
4
  export { resolveStoredDocumentDownload } from "./document-download.js";
5
+ export { createLazyRouteHandler, mountLazyRoutePaths, mountLazyRoutesAt, } from "./lazy-routes.js";
4
6
  export { createPathDbSelector } from "./lib/db-selector.js";
5
7
  export { clientIpKey, consoleLoggerProvider, cors, DEFAULT_IDEMPOTENCY_TTL_MS, db, enforceRateLimit, errorBoundary, handleApiError, idempotencyKey, LIVE_LIMITS, logger, purgeExpiredIdempotencyKeys, rateLimit, requestId, requireActor, requireAuth, requirePermission, } from "./middleware/index.js";
6
8
  export { defineHonoBundle, defineHonoPlugin, expandHonoBundles, expandHonoPlugins, } from "./plugin.js";
@@ -0,0 +1,67 @@
1
+ /**
2
+ * First-class lazy route mounting for `@voyant-travel/hono`.
3
+ *
4
+ * A module or extension can declare `lazyAdminRoutes` / `lazyPublicRoutes` as a
5
+ * loader (`() => import("./routes").then((m) => m.createRoutes(opts))`) instead
6
+ * of eager `adminRoutes` / `publicRoutes`. The route bundle is dynamically
7
+ * imported on first matching request and cached per isolate/process, so heavy
8
+ * route families don't inflate the main bundle or the Worker cold start.
9
+ *
10
+ * For deployment-local families that span MULTIPLE absolute path prefixes (e.g.
11
+ * an operator bundle exposing `/v1/uploads`, `/v1/admin/uploads`, `/v1/media/*`),
12
+ * the single-surface loaders don't fit. Such families declare `lazyRoutes`:
13
+ * `{ paths, load }` where `load` returns a sub-app whose routes are ABSOLUTE and
14
+ * `paths` are the explicit matchers the framework installs up front. This is the
15
+ * context-preserving replacement for the starter's `mountLazyRouteApp(...)`.
16
+ *
17
+ * The hard requirement (and the reason the starter's old `mountLazyRouteApp`
18
+ * was insufficient): the lazy routes must behave **exactly** like eager routes.
19
+ * Eager routes mounted via `app.route(...)` share the request context, so they
20
+ * see `c.var.db`, `c.var.container`, the resolved actor, etc. set by the
21
+ * `createApp` middleware pipeline. A naive `subApp.fetch(c.req.raw, c.env)`
22
+ * forward builds a fresh context and drops every `c.var`.
23
+ *
24
+ * So this dispatcher bridges the request-scoped context across the forward: it
25
+ * snapshots `c.var`, carries it on the forwarded `env` under a private symbol,
26
+ * and a wrapper middleware re-hydrates it onto the loaded sub-app's context
27
+ * before the real routes run. The db lease is *carried*, not re-acquired — the
28
+ * outer `db` middleware still owns its lifecycle (dispose), so there is no
29
+ * double-release.
30
+ */
31
+ import type { Context, Hono as HonoType } from "hono";
32
+ type AnyHono = HonoType<any>;
33
+ /** Loads (and builds) the Hono sub-app for a lazy route surface. */
34
+ export type LazyRoutesLoader = () => Promise<AnyHono>;
35
+ /**
36
+ * A deployment-local lazy route family spanning explicit absolute path
37
+ * matchers. `load` returns a sub-app whose routes are ABSOLUTE; `paths` are the
38
+ * matchers the framework installs up front (no bundle import until a request
39
+ * matches).
40
+ */
41
+ export interface LazyHonoRoutes {
42
+ paths: readonly string[];
43
+ load: LazyRoutesLoader;
44
+ }
45
+ /**
46
+ * Build a cached, context-preserving request handler. `mountPrefix` is where the
47
+ * loaded routes are re-mounted in the wrapper sub-app so the forwarded absolute
48
+ * request URL matches: the surface prefix (e.g. `/v1/admin/flights`) for
49
+ * relative-route loaders, or `"/"` for loaders that already return absolute
50
+ * routes.
51
+ */
52
+ export declare function createLazyRouteHandler(mountPrefix: string, load: LazyRoutesLoader): (c: Context) => Promise<Response>;
53
+ /**
54
+ * Register a single lazy surface on `app` at `prefix` (loader returns RELATIVE
55
+ * routes). Matches both the prefix root (`POST /v1/admin/foo`) and any sub-path
56
+ * (`/v1/admin/foo/bar`).
57
+ */
58
+ export declare function mountLazyRoutesAt(app: AnyHono, prefix: string, load: LazyRoutesLoader): void;
59
+ /**
60
+ * Register a multi-prefix lazy family on `app` at explicit `paths` (loader
61
+ * returns ABSOLUTE routes). One shared cached/context-bridging handler backs
62
+ * every path. Context-preserving replacement for the starter's
63
+ * `mountLazyRouteApp(...)`.
64
+ */
65
+ export declare function mountLazyRoutePaths(app: AnyHono, paths: readonly string[], load: LazyRoutesLoader): void;
66
+ export {};
67
+ //# sourceMappingURL=lazy-routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lazy-routes.d.ts","sourceRoot":"","sources":["../src/lazy-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,MAAM,CAAA;AAMrD,KAAK,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAA;AAE5B,oEAAoE;AACpE,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;AAErD;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB,IAAI,EAAE,gBAAgB,CAAA;CACvB;AAKD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,IA0ClE,GAAG,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CAO7C;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,IAAI,CAI5F;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,IAAI,EAAE,gBAAgB,GACrB,IAAI,CAKN"}
@@ -0,0 +1,79 @@
1
+ import { Hono } from "hono";
2
+ import { tryGetExecutionCtx } from "./lib/execution-ctx.js";
3
+ /** Private carrier key for snapshotting `c.var` across the forward. */
4
+ const LAZY_CONTEXT_CARRIER = Symbol.for("voyant.hono.lazyContextCarrier");
5
+ /**
6
+ * Build a cached, context-preserving request handler. `mountPrefix` is where the
7
+ * loaded routes are re-mounted in the wrapper sub-app so the forwarded absolute
8
+ * request URL matches: the surface prefix (e.g. `/v1/admin/flights`) for
9
+ * relative-route loaders, or `"/"` for loaders that already return absolute
10
+ * routes.
11
+ */
12
+ export function createLazyRouteHandler(mountPrefix, load) {
13
+ let cached;
14
+ function getApp() {
15
+ // Cache the BUILT, wrapped sub-app; reset on failure so a transient
16
+ // import/config error can recover on the next request.
17
+ if (!cached) {
18
+ cached = load()
19
+ .then((routes) => {
20
+ const wrapped = new Hono();
21
+ // Re-throw handler errors instead of letting the wrapper sub-app's
22
+ // default Hono error handler swallow them into a plain 500. This
23
+ // propagates the throw back through `app.fetch` to the outer
24
+ // `createApp` pipeline, so lazy routes hit the same `errorBoundary` /
25
+ // `handleApiError` normalization (JSON error shape + structured
26
+ // logging) as eager `app.route(...)` mounts.
27
+ wrapped.onError((err) => {
28
+ throw err;
29
+ });
30
+ wrapped.use("*", async (cc, next) => {
31
+ const carried = cc.env?.[LAZY_CONTEXT_CARRIER];
32
+ if (carried) {
33
+ for (const [key, value] of Object.entries(carried)) {
34
+ // biome-ignore lint/suspicious/noExplicitAny: re-hydrating arbitrary context vars -- owner: hono.
35
+ cc.set(key, value);
36
+ }
37
+ }
38
+ await next();
39
+ });
40
+ wrapped.route(mountPrefix, routes);
41
+ return wrapped;
42
+ })
43
+ .catch((err) => {
44
+ cached = undefined;
45
+ throw err;
46
+ });
47
+ }
48
+ return cached;
49
+ }
50
+ return async (c) => {
51
+ const app = await getApp();
52
+ const snapshot = { ...c.var };
53
+ const env = { ...c.env, [LAZY_CONTEXT_CARRIER]: snapshot };
54
+ // biome-ignore lint/suspicious/noExplicitAny: forward the host execution context when present -- owner: hono.
55
+ return app.fetch(c.req.raw, env, tryGetExecutionCtx(c));
56
+ };
57
+ }
58
+ /**
59
+ * Register a single lazy surface on `app` at `prefix` (loader returns RELATIVE
60
+ * routes). Matches both the prefix root (`POST /v1/admin/foo`) and any sub-path
61
+ * (`/v1/admin/foo/bar`).
62
+ */
63
+ export function mountLazyRoutesAt(app, prefix, load) {
64
+ const handler = createLazyRouteHandler(prefix, load);
65
+ app.all(prefix, handler);
66
+ app.all(`${prefix}/*`, handler);
67
+ }
68
+ /**
69
+ * Register a multi-prefix lazy family on `app` at explicit `paths` (loader
70
+ * returns ABSOLUTE routes). One shared cached/context-bridging handler backs
71
+ * every path. Context-preserving replacement for the starter's
72
+ * `mountLazyRouteApp(...)`.
73
+ */
74
+ export function mountLazyRoutePaths(app, paths, load) {
75
+ const handler = createLazyRouteHandler("/", load);
76
+ for (const path of paths) {
77
+ app.all(path, handler);
78
+ }
79
+ }
package/dist/module.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { Extension, Module } from "@voyant-travel/core";
2
2
  import type { Hono } from "hono";
3
+ import type { LazyHonoRoutes, LazyRoutesLoader } from "./lazy-routes.js";
3
4
  export interface HonoModule {
4
5
  module: Module;
5
6
  /**
@@ -14,6 +15,22 @@ export interface HonoModule {
14
15
  adminRoutes?: Hono<any>;
15
16
  /** Customer/partner/supplier-facing routes — mounted at `/v1/public/{module.name}`. */
16
17
  publicRoutes?: Hono<any>;
18
+ /**
19
+ * Lazy variant of `adminRoutes` — the route bundle is dynamically imported on
20
+ * first request and cached per isolate. Mounted at `/v1/admin/{module.name}`
21
+ * with the request context bridged in, so it behaves identically to eager
22
+ * `adminRoutes`. Use for heavy route families to protect Worker cold start.
23
+ */
24
+ lazyAdminRoutes?: LazyRoutesLoader;
25
+ /** Lazy variant of `publicRoutes` — mounted at `/v1/public/{publicPath ?? module.name}`. */
26
+ lazyPublicRoutes?: LazyRoutesLoader;
27
+ /**
28
+ * Deployment-local lazy family spanning explicit absolute path matchers (for
29
+ * route bundles that don't fit a single admin/public surface). The loader
30
+ * returns ABSOLUTE routes; the framework mounts + caches them with the request
31
+ * context bridged in. Context-preserving replacement for `mountLazyRouteApp`.
32
+ */
33
+ lazyRoutes?: LazyHonoRoutes;
17
34
  /**
18
35
  * Optional override for the public mount path relative to `/v1/public`.
19
36
  *
@@ -30,6 +47,12 @@ export interface HonoExtension {
30
47
  adminRoutes?: Hono<any>;
31
48
  /** Customer/partner/supplier-facing routes — mounted at `/v1/public/{extension.module}`. */
32
49
  publicRoutes?: Hono<any>;
50
+ /** Lazy variant of `adminRoutes` — mounted at `/v1/admin/{extension.module}` (see HonoModule). */
51
+ lazyAdminRoutes?: LazyRoutesLoader;
52
+ /** Lazy variant of `publicRoutes` — mounted at `/v1/public/{publicPath ?? extension.module}`. */
53
+ lazyPublicRoutes?: LazyRoutesLoader;
54
+ /** Deployment-local lazy family at explicit absolute paths (see HonoModule). */
55
+ lazyRoutes?: LazyHonoRoutes;
33
56
  /**
34
57
  * Optional override for the public mount path relative to `/v1/public`.
35
58
  *
@@ -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,qBAAqB,CAAA;AAC5D,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"}
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAC5D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAEhC,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAExE,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,eAAe,CAAC,EAAE,gBAAgB,CAAA;IAClC,4FAA4F;IAC5F,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,cAAc,CAAA;IAC3B;;;;;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,kGAAkG;IAClG,eAAe,CAAC,EAAE,gBAAgB,CAAA;IAClC,iGAAiG;IACjG,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC,gFAAgF;IAChF,UAAU,CAAC,EAAE,cAAc,CAAA;IAC3B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voyant-travel/hono",
3
- "version": "0.110.3",
3
+ "version": "0.112.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -119,19 +119,19 @@
119
119
  "drizzle-orm": "^0.45.2",
120
120
  "hono": "^4.12.10",
121
121
  "zod": "^4.3.6",
122
- "@voyant-travel/core": "^0.109.0",
123
- "@voyant-travel/db": "^0.108.1",
124
- "@voyant-travel/storage": "^0.104.1",
122
+ "@voyant-travel/core": "^0.110.0",
123
+ "@voyant-travel/db": "^0.108.2",
124
+ "@voyant-travel/storage": "^0.105.0",
125
125
  "@voyant-travel/types": "^0.104.5",
126
126
  "@voyant-travel/utils": "^0.105.2",
127
- "@voyant-travel/workflows": "^0.109.1"
127
+ "@voyant-travel/workflows": "^0.109.4"
128
128
  },
129
129
  "devDependencies": {
130
130
  "@cloudflare/workers-types": "^4.20260426.1",
131
131
  "typescript": "^6.0.2",
132
132
  "vitest": "^4.1.2",
133
133
  "@voyant-travel/voyant-typescript-config": "^0.1.0",
134
- "@voyant-travel/workflows-orchestrator": "^0.109.1"
134
+ "@voyant-travel/workflows-orchestrator": "^0.109.4"
135
135
  },
136
136
  "files": [
137
137
  "dist"