@voyant-travel/hono 0.128.0 → 0.128.2

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.js CHANGED
@@ -19,7 +19,6 @@ import { cors } from "./middleware/cors.js";
19
19
  import { db } from "./middleware/db.js";
20
20
  import { handleApiError, requestId } from "./middleware/error-boundary.js";
21
21
  import { logger } from "./middleware/logger.js";
22
- import { metrics } from "./middleware/metrics.js";
23
22
  import { publicResponseCache } from "./middleware/public-cache.js";
24
23
  import { rateLimit, resolveRateLimitStore, } from "./middleware/rate-limit.js";
25
24
  import { requireActor } from "./middleware/require-actor.js";
@@ -372,11 +371,6 @@ export function mountApp(config) {
372
371
  app.use("*", requestId);
373
372
  // Structured logger
374
373
  app.use("*", logger(config.logger));
375
- // Per-request metrics → Analytics Engine (no-op without the binding).
376
- // Mounted before the cache middleware so cache hits are measured too.
377
- if (config.metrics !== false) {
378
- app.use("*", metrics());
379
- }
380
374
  // CORS (allowlist via env CORS_ALLOWLIST)
381
375
  app.use("*", cors());
382
376
  if (config.securityHeaders !== false) {
package/dist/index.d.ts CHANGED
@@ -20,5 +20,5 @@ export { stampOpenApiRegistryApiId } from "./openapi-ownership.js";
20
20
  export { openApiValidationHook } from "./openapi-validation.js";
21
21
  export type { CreatePublicCapabilityOptions, PublicCapabilityCookieOptions, PublicCapabilityPayload, VerifyPublicCapabilityOptions, } from "./public-capability.js";
22
22
  export { createPublicCapabilityToken, extractPublicCapabilityToken, serializePublicCapabilityCookie, verifyPublicCapabilityToken, } from "./public-capability.js";
23
- export type { DbFactory, DbFactorySelector, DbSource, DbSurfaceSelection, LogEntry, LoggerProvider, VoyantAppConfig, VoyantAuthIntegration, VoyantAuthPermissionArgs, VoyantAuthResolveArgs, VoyantBindings, VoyantDb, VoyantExecutionContext, VoyantQueryRuntime, VoyantRequestAuthContext, VoyantRouteHandler, VoyantVariables, } from "./types.js";
23
+ export type { DbFactory, DbFactorySelector, DbSource, DbSurfaceSelection, LogEntry, LoggerProvider, VoyantAppConfig, VoyantAuthAppTokenResolveArgs, VoyantAuthIntegration, VoyantAuthPermissionArgs, VoyantAuthResolveArgs, VoyantBindings, VoyantDb, VoyantExecutionContext, VoyantQueryRuntime, VoyantRequestAuthContext, VoyantRouteHandler, VoyantVariables, } from "./types.js";
24
24
  export { ApiHttpError, ForbiddenApiError, normalizeValidationError, parseJsonBody, parseOptionalJsonBody, parseQuery, RequestValidationError, UnauthorizedApiError, } from "./validation.js";
@@ -139,6 +139,19 @@ function applyAuthContext(c, auth) {
139
139
  c.set("apiTokenId", auth.apiTokenId);
140
140
  if (auth.apiKeyId)
141
141
  c.set("apiKeyId", auth.apiKeyId);
142
+ if (auth.appId)
143
+ c.set("appId", auth.appId);
144
+ if (auth.appInstallationId)
145
+ c.set("appInstallationId", auth.appInstallationId);
146
+ if (auth.appReleaseId)
147
+ c.set("appReleaseId", auth.appReleaseId);
148
+ if (auth.appCredentialGeneration !== undefined) {
149
+ c.set("appCredentialGeneration", auth.appCredentialGeneration);
150
+ }
151
+ if (auth.appTokenMode)
152
+ c.set("appTokenMode", auth.appTokenMode);
153
+ if (auth.appViewerId)
154
+ c.set("appViewerId", auth.appViewerId);
142
155
  }
143
156
  export function requireAuth(dbSource, opts) {
144
157
  const publicPaths = opts?.publicPaths ?? [];
@@ -270,7 +283,29 @@ export function requireAuth(dbSource, opts) {
270
283
  await lease.release();
271
284
  }
272
285
  }
273
- // Strategy 3: App-provided auth resolution (cookies, provider tokens, etc.)
286
+ // Strategy 3: Remote app access token support
287
+ if (token && opts?.auth?.resolveAppToken) {
288
+ const lease = acquireRequestDb(c, dbFactory);
289
+ try {
290
+ const resolved = await opts.auth.resolveAppToken({
291
+ request: c.req.raw,
292
+ env: c.env,
293
+ db: lease.db,
294
+ // Guarded: Hono throws on `executionCtx` access outside Workers.
295
+ ctx: tryGetExecutionCtx(c),
296
+ token,
297
+ });
298
+ if (resolved?.callerType === "app" && resolved.appInstallationId) {
299
+ applyAuthContext(c, resolved);
300
+ // `await` is load-bearing — see strategy 2.
301
+ return await next();
302
+ }
303
+ }
304
+ finally {
305
+ await lease.release();
306
+ }
307
+ }
308
+ // Strategy 4: App-provided auth resolution (cookies, provider tokens, etc.)
274
309
  if (opts?.auth?.resolve) {
275
310
  const lease = acquireRequestDb(c, dbFactory);
276
311
  try {
@@ -293,7 +328,7 @@ export function requireAuth(dbSource, opts) {
293
328
  await lease.release();
294
329
  }
295
330
  }
296
- // Strategy 4: Generic session-claims bearer token support
331
+ // Strategy 5: Generic session-claims bearer token support
297
332
  const sessionSecret = c.env.SESSION_CLAIMS_SECRET;
298
333
  if (token && sessionSecret && token.includes(".")) {
299
334
  try {
@@ -5,9 +5,8 @@ export { db } from "./db.js";
5
5
  export { errorBoundary, type HandleApiErrorOptions, handleApiError, requestId, } from "./error-boundary.js";
6
6
  export { DEFAULT_IDEMPOTENCY_TTL_MS, type IdempotencyKeyOptions, idempotencyKey, purgeExpiredIdempotencyKeys, } from "./idempotency-key.js";
7
7
  export { consoleLoggerProvider, logger } from "./logger.js";
8
- export { type AnalyticsEngineDatasetLike, DB_METRICS_CONTEXT_KEY, type MetricsMiddlewareOptions, metrics, type RequestDbMetrics, withQueryCounting, } from "./metrics.js";
9
8
  export { type PublicCacheOptions, publicResponseCache, resetPublicCacheStateForTests, } from "./public-cache.js";
10
- export { type CloudflareRateLimiterBinding, clientIpKey, createCloudflareRateLimitStore, createKvRateLimitStore, createMemoryRateLimitStore, createRedisRateLimitStore, enforceRateLimit, LIVE_LIMITS, type RateLimitConfig, type RateLimitPolicy, type RateLimitRequestContext, type RateLimitResult, type RateLimitRule, type RateLimitStore, type RedisRateLimitStoreOptions, rateLimit, resolveRateLimitStore, } from "./rate-limit.js";
9
+ export { clientIpKey, createMemoryRateLimitStore, createRedisRateLimitStore, enforceRateLimit, LIVE_LIMITS, type RateLimitConfig, type RateLimitPolicy, type RateLimitRequestContext, type RateLimitResult, type RateLimitRule, type RateLimitStore, type RedisRateLimitStoreOptions, rateLimit, resolveRateLimitStore, } from "./rate-limit.js";
11
10
  export { isStaffRbacEnforced, requireActor } from "./require-actor.js";
12
11
  export { requirePermission } from "./require-permission.js";
13
12
  export { type SecurityHeadersOptions, securityHeaders } from "./security-headers.js";
@@ -5,9 +5,8 @@ export { db } from "./db.js";
5
5
  export { errorBoundary, handleApiError, requestId, } from "./error-boundary.js";
6
6
  export { DEFAULT_IDEMPOTENCY_TTL_MS, idempotencyKey, purgeExpiredIdempotencyKeys, } from "./idempotency-key.js";
7
7
  export { consoleLoggerProvider, logger } from "./logger.js";
8
- export { DB_METRICS_CONTEXT_KEY, metrics, withQueryCounting, } from "./metrics.js";
9
8
  export { publicResponseCache, resetPublicCacheStateForTests, } from "./public-cache.js";
10
- export { clientIpKey, createCloudflareRateLimitStore, createKvRateLimitStore, createMemoryRateLimitStore, createRedisRateLimitStore, enforceRateLimit, LIVE_LIMITS, rateLimit, resolveRateLimitStore, } from "./rate-limit.js";
9
+ export { clientIpKey, createMemoryRateLimitStore, createRedisRateLimitStore, enforceRateLimit, LIVE_LIMITS, rateLimit, resolveRateLimitStore, } from "./rate-limit.js";
11
10
  export { isStaffRbacEnforced, requireActor } from "./require-actor.js";
12
11
  export { requirePermission } from "./require-permission.js";
13
12
  export { securityHeaders } from "./security-headers.js";
@@ -1,10 +1,5 @@
1
1
  import type { MiddlewareHandler } from "hono";
2
- import type { VoyantVariables } from "../types.js";
3
- /**
4
- * Structural shape of a Workers Analytics Engine dataset binding —
5
- * declared locally so `@voyant-travel/hono` needs no `@cloudflare/workers-types`
6
- * dependency.
7
- */
2
+ /** Structural sink contract for request metrics. */
8
3
  export interface AnalyticsEngineDatasetLike {
9
4
  writeDataPoint(point: {
10
5
  blobs?: string[];
@@ -18,19 +13,11 @@ export interface RequestDbMetrics {
18
13
  }
19
14
  export declare const DB_METRICS_CONTEXT_KEY = "__voyantDbMetrics";
20
15
  export interface MetricsMiddlewareOptions {
21
- /**
22
- * Resolve the Analytics Engine dataset from bindings. Defaults to
23
- * `env.METRICS`. Returning undefined makes the middleware a no-op for
24
- * that request (deployments without the binding pay ~nothing).
25
- */
26
- dataset?: (env: unknown) => AnalyticsEngineDatasetLike | undefined;
16
+ /** Resolve the metrics sink for a request. Returning undefined is a no-op. */
17
+ dataset: (env: unknown) => AnalyticsEngineDatasetLike | undefined;
27
18
  }
28
19
  /**
29
- * Per-request metrics Workers Analytics Engine (RFC voyant#1687 Phase
30
- * 3.4, the in-worker half — the platform dispatcher's DISPATCH_METRICS
31
- * dataset already records hostname/plan/cache/duration per dispatch;
32
- * this adds what only the worker can see: the matched route pattern,
33
- * the db query count, and in-worker cache hits).
20
+ * Per-request metrics for an explicitly supplied deployment sink.
34
21
  *
35
22
  * One data point per request:
36
23
  * - blobs: [method, routePattern, surface, cacheStatus]
@@ -40,8 +27,10 @@ export interface MetricsMiddlewareOptions {
40
27
  * `writeDataPoint` is fire-and-forget and never throws into the
41
28
  * request; a missing binding short-circuits before timing overhead.
42
29
  */
43
- export declare function metrics(options?: MetricsMiddlewareOptions): MiddlewareHandler<{
44
- Variables: Pick<VoyantVariables, typeof DB_METRICS_CONTEXT_KEY>;
30
+ export declare function metrics(options: MetricsMiddlewareOptions): MiddlewareHandler<{
31
+ Variables: {
32
+ [DB_METRICS_CONTEXT_KEY]?: RequestDbMetrics;
33
+ };
45
34
  }>;
46
35
  /**
47
36
  * Wrap a drizzle client so top-level query-initiating calls increment
@@ -9,11 +9,7 @@ function surfaceOf(path) {
9
9
  return "other";
10
10
  }
11
11
  /**
12
- * Per-request metrics Workers Analytics Engine (RFC voyant#1687 Phase
13
- * 3.4, the in-worker half — the platform dispatcher's DISPATCH_METRICS
14
- * dataset already records hostname/plan/cache/duration per dispatch;
15
- * this adds what only the worker can see: the matched route pattern,
16
- * the db query count, and in-worker cache hits).
12
+ * Per-request metrics for an explicitly supplied deployment sink.
17
13
  *
18
14
  * One data point per request:
19
15
  * - blobs: [method, routePattern, surface, cacheStatus]
@@ -23,9 +19,8 @@ function surfaceOf(path) {
23
19
  * `writeDataPoint` is fire-and-forget and never throws into the
24
20
  * request; a missing binding short-circuits before timing overhead.
25
21
  */
26
- export function metrics(options = {}) {
27
- const resolveDataset = options.dataset ??
28
- ((env) => env?.METRICS);
22
+ export function metrics(options) {
23
+ const resolveDataset = options.dataset;
29
24
  return async (c, next) => {
30
25
  const dataset = resolveDataset(c.env);
31
26
  if (!dataset || typeof dataset.writeDataPoint !== "function") {
@@ -1,4 +1,3 @@
1
- import type { KVStore } from "@voyant-travel/utils/cache";
2
1
  import { type LazyRedisClient } from "@voyant-travel/utils/redis-client";
3
2
  import type { MiddlewareHandler } from "hono";
4
3
  /**
@@ -6,11 +5,9 @@ import type { MiddlewareHandler } from "hono";
6
5
  *
7
6
  * The limiter is split into two halves:
8
7
  *
9
- * - a {@link RateLimitStore} — the counting backend. Three implementations
10
- * ship with the framework: the Cloudflare native Rate Limiting binding
11
- * (truly distributed, limits configured in wrangler), a KV-backed
12
- * fixed-window counter (best-effort — see {@link createKvRateLimitStore}),
13
- * and an in-memory Map for Node/dev/tests (per-isolate).
8
+ * - a {@link RateLimitStore} — the counting backend. Composition injects the
9
+ * selected provider; an in-memory Map remains the zero-configuration
10
+ * fallback for Node/dev/tests.
14
11
  * - the enforcement surface — the {@link rateLimit} Hono middleware and the
15
12
  * imperative {@link enforceRateLimit} helper that route packages
16
13
  * (storefront, bookings, …) call directly inside handlers.
@@ -73,9 +70,8 @@ export interface RateLimitPolicy {
73
70
  export interface RateLimitConfig {
74
71
  /**
75
72
  * Explicit store, or a function-of-bindings for stores built from env
76
- * bindings (Workers env is only available per request). When omitted
77
- * or when the function returns `undefined` — resolution falls through
78
- * to `c.env.RATE_LIMITER` (CF binding) → `c.env.RATE_LIMIT` (KV) →
73
+ * bindings. When omitted or when the function returns `undefined`
74
+ * resolution falls through to the injected `RATE_LIMIT_STORE`, then
79
75
  * in-memory.
80
76
  */
81
77
  store?: RateLimitStore | ((env: unknown) => RateLimitStore | undefined);
@@ -92,21 +88,6 @@ export interface RateLimitRule {
92
88
  max: number;
93
89
  windowSeconds: number;
94
90
  }
95
- /**
96
- * The Cloudflare native Rate Limiting binding shape (wrangler
97
- * `[[ratelimits]]`). The binding's `limit`/`period` are fixed in wrangler
98
- * config — the policy's `max`/`windowSeconds` are advisory for headers
99
- * only. One shared binding works across policies because the bucket is
100
- * part of the key; deployments wanting different limits per policy bind
101
- * one ratelimiter per policy and pass it via `policy.store`.
102
- */
103
- export interface CloudflareRateLimiterBinding {
104
- limit(opts: {
105
- key: string;
106
- }): Promise<{
107
- success: boolean;
108
- }>;
109
- }
110
91
  /**
111
92
  * Minimal structural view of a Hono context — enough for key derivation,
112
93
  * store resolution, and response headers — so route packages can call
@@ -142,30 +123,6 @@ export declare function clientIpKey(c: {
142
123
  export declare function createMemoryRateLimitStore(options?: {
143
124
  maxEntries?: number;
144
125
  }): RateLimitStore;
145
- /**
146
- * KV-backed fixed-window store.
147
- *
148
- * **Best-effort by construction**: KV is eventually consistent and the
149
- * counter is a non-atomic read-modify-write, so concurrent requests
150
- * across PoPs undercount — a determined attacker gets somewhat more than
151
- * `max` through before the window converges. With per-client keys this
152
- * is still a real brake on brute force and write floods; deployments
153
- * needing exact distributed limits should bind the Cloudflare Rate
154
- * Limiting binding (`RATE_LIMITER`) instead.
155
- *
156
- * Stored keys are `lim:<bucket>:<clientKey>:<windowKey>` (the window
157
- * suffix is appended here) with a TTL of `max(60, windowSeconds * 2)` —
158
- * KV's TTL floor is 60s.
159
- */
160
- export declare function createKvRateLimitStore(kv: KVStore): RateLimitStore;
161
- /**
162
- * Adapter for the Cloudflare native Rate Limiting binding. Truly
163
- * distributed and atomic; the actual limit/period are fixed in wrangler
164
- * config, so the policy's `max`/`windowSeconds` only inform the
165
- * `Retry-After` hint. `remaining` is never reported (the binding does
166
- * not expose it).
167
- */
168
- export declare function createCloudflareRateLimitStore(binding: CloudflareRateLimiterBinding): RateLimitStore;
169
126
  export interface RedisRateLimitStoreOptions {
170
127
  client?: LazyRedisClient;
171
128
  }
@@ -174,8 +131,8 @@ export declare function createRedisRateLimitStore(redisUrl: string, options?: Re
174
131
  export declare function resetRateLimitWarningsForTests(): void;
175
132
  /**
176
133
  * Resolve the best available store from the environment:
177
- * `c.env.RATE_LIMITER` (CF Rate Limiting binding) `c.env.RATE_LIMIT`
178
- * (KV) → in-memory fallback. **Fails open into the memory store** —
134
+ * Injected `c.env.RATE_LIMIT_STORE` in-memory fallback. **Fails open into
135
+ * the memory store** —
179
136
  * rate limiting must never break Node/headless deployments that bind
180
137
  * neither — but warns once per isolate outside dev/test so a
181
138
  * production deploy without a distributed backend is visible in logs.
@@ -67,58 +67,6 @@ export function createMemoryRateLimitStore(options) {
67
67
  },
68
68
  };
69
69
  }
70
- /**
71
- * KV-backed fixed-window store.
72
- *
73
- * **Best-effort by construction**: KV is eventually consistent and the
74
- * counter is a non-atomic read-modify-write, so concurrent requests
75
- * across PoPs undercount — a determined attacker gets somewhat more than
76
- * `max` through before the window converges. With per-client keys this
77
- * is still a real brake on brute force and write floods; deployments
78
- * needing exact distributed limits should bind the Cloudflare Rate
79
- * Limiting binding (`RATE_LIMITER`) instead.
80
- *
81
- * Stored keys are `lim:<bucket>:<clientKey>:<windowKey>` (the window
82
- * suffix is appended here) with a TTL of `max(60, windowSeconds * 2)` —
83
- * KV's TTL floor is 60s.
84
- */
85
- export function createKvRateLimitStore(kv) {
86
- return {
87
- async limit(key, { max, windowSeconds }) {
88
- const nowSeconds = Math.floor(Date.now() / 1000);
89
- const windowKey = Math.floor(nowSeconds / windowSeconds);
90
- const storageKey = `${key}:${windowKey}`;
91
- const raw = await kv.get(storageKey);
92
- const current = raw ? Number(raw) || 0 : 0;
93
- const next = current + 1;
94
- await kv.put(storageKey, String(next), {
95
- expirationTtl: Math.max(60, windowSeconds * 2),
96
- });
97
- return {
98
- allowed: next <= max,
99
- remaining: Math.max(0, max - next),
100
- retryAfterSeconds: Math.max(1, windowSeconds - (nowSeconds % windowSeconds)),
101
- };
102
- },
103
- };
104
- }
105
- /**
106
- * Adapter for the Cloudflare native Rate Limiting binding. Truly
107
- * distributed and atomic; the actual limit/period are fixed in wrangler
108
- * config, so the policy's `max`/`windowSeconds` only inform the
109
- * `Retry-After` hint. `remaining` is never reported (the binding does
110
- * not expose it).
111
- */
112
- export function createCloudflareRateLimitStore(binding) {
113
- return {
114
- async limit(key, { windowSeconds }) {
115
- const { success } = await binding.limit({ key });
116
- return success
117
- ? { allowed: true }
118
- : { allowed: false, retryAfterSeconds: Math.max(1, windowSeconds) };
119
- },
120
- };
121
- }
122
70
  export function createRedisRateLimitStore(redisUrl, options = {}) {
123
71
  const lazyClient = options.client ?? createLazyRedisClient(redisUrl);
124
72
  return {
@@ -140,9 +88,7 @@ export function createRedisRateLimitStore(redisUrl, options = {}) {
140
88
  };
141
89
  }
142
90
  // ---- Store resolution ----
143
- const bindingStoreCache = new WeakMap();
144
91
  const explicitStoreCache = new WeakMap();
145
- const kvStoreCache = new WeakMap();
146
92
  const sharedMemoryStore = createMemoryRateLimitStore();
147
93
  let warnedNoDistributedStore = false;
148
94
  function isDevLikeEnv() {
@@ -156,23 +102,14 @@ export function resetRateLimitWarningsForTests() {
156
102
  }
157
103
  /**
158
104
  * Resolve the best available store from the environment:
159
- * `c.env.RATE_LIMITER` (CF Rate Limiting binding) `c.env.RATE_LIMIT`
160
- * (KV) → in-memory fallback. **Fails open into the memory store** —
105
+ * Injected `c.env.RATE_LIMIT_STORE` in-memory fallback. **Fails open into
106
+ * the memory store** —
161
107
  * rate limiting must never break Node/headless deployments that bind
162
108
  * neither — but warns once per isolate outside dev/test so a
163
109
  * production deploy without a distributed backend is visible in logs.
164
110
  */
165
111
  export function resolveRateLimitStore(c, memoryFallback = sharedMemoryStore) {
166
112
  const env = (c.env ?? {});
167
- const binding = env.RATE_LIMITER;
168
- if (binding && typeof binding.limit === "function") {
169
- let store = bindingStoreCache.get(binding);
170
- if (!store) {
171
- store = createCloudflareRateLimitStore(binding);
172
- bindingStoreCache.set(binding, store);
173
- }
174
- return store;
175
- }
176
113
  const explicit = env.RATE_LIMIT_STORE;
177
114
  if (explicit && typeof explicit.limit === "function") {
178
115
  let store = explicitStoreCache.get(explicit);
@@ -182,19 +119,10 @@ export function resolveRateLimitStore(c, memoryFallback = sharedMemoryStore) {
182
119
  }
183
120
  return store;
184
121
  }
185
- const kv = env.RATE_LIMIT;
186
- if (kv && typeof kv.get === "function" && typeof kv.put === "function") {
187
- let store = kvStoreCache.get(kv);
188
- if (!store) {
189
- store = createKvRateLimitStore(kv);
190
- kvStoreCache.set(kv, store);
191
- }
192
- return store;
193
- }
194
122
  if (!warnedNoDistributedStore && !isDevLikeEnv()) {
195
123
  warnedNoDistributedStore = true;
196
124
  console.warn("[voyant] rate-limit: no distributed store available (inject RATE_LIMIT_STORE " +
197
- "or a KV-compatible RATE_LIMIT fallback). " +
125
+ "). " +
198
126
  "Falling back to a per-isolate in-memory limiter — limits apply " +
199
127
  "per instance, not fleet-wide.");
200
128
  }
@@ -98,7 +98,9 @@ export function requireActor(...args) {
98
98
  return async (c, next) => {
99
99
  if (c.req.method === "OPTIONS")
100
100
  return next();
101
- if (c.get("callerType") === "api_key" || c.get("callerType") === "internal") {
101
+ if (c.get("callerType") === "api_key" ||
102
+ c.get("callerType") === "app" ||
103
+ c.get("callerType") === "internal") {
102
104
  const pathname = normalizePathname(new URL(c.req.url).pathname, {
103
105
  basePath: options.basePath,
104
106
  });
@@ -116,7 +118,7 @@ export function requireActor(...args) {
116
118
  hasAnyApiKeyPermission(c.get("scopes"), resource, actions, options.accessCatalog)) {
117
119
  return next();
118
120
  }
119
- return c.json({ error: "Forbidden: API key missing required permission" }, 403);
121
+ return c.json({ error: "Forbidden: token missing required permission" }, 403);
120
122
  }
121
123
  const actor = c.get("actor");
122
124
  if (!actor) {
package/dist/types.d.ts CHANGED
@@ -24,17 +24,9 @@ export interface VoyantBindings {
24
24
  APP_URL?: string;
25
25
  DASH_BASE_URL?: string;
26
26
  API_BASE_URL?: string;
27
- RATE_LIMIT?: KVStore;
28
27
  RATE_LIMIT_STORE?: import("./middleware/rate-limit.js").RateLimitStore;
29
28
  CACHE?: KVStore;
30
29
  SHARED_STATE?: KVStore;
31
- RATE_LIMITER?: import("./middleware/rate-limit.js").CloudflareRateLimiterBinding;
32
- /**
33
- * Workers Analytics Engine dataset receiving per-request metrics
34
- * (see the `metrics` middleware). Optional — without it the
35
- * middleware is a no-op.
36
- */
37
- METRICS?: import("./middleware/metrics.js").AnalyticsEngineDatasetLike;
38
30
  }
39
31
  export type VoyantDb = PostgresJsDatabase | NeonHttpDatabase | NeonWsDatabase;
40
32
  export type VoyantQueryRuntime = QueryRunner;
@@ -55,8 +47,6 @@ export type VoyantVariables = CoreVoyantVariables & {
55
47
  query?: VoyantQueryRuntime;
56
48
  /** Optional workflow driver surfaced to HTTP routes after lazy app bootstrap. */
57
49
  workflowDriver?: import("@voyant-travel/workflows/driver").WorkflowDriver;
58
- /** Per-request db metrics counter populated by the metrics middleware. */
59
- __voyantDbMetrics?: import("./middleware/metrics.js").RequestDbMetrics;
60
50
  };
61
51
  /** Handler contract for application-authored Hono API routes. */
62
52
  export type VoyantRouteHandler<TBindings extends VoyantBindings = VoyantBindings> = Handler<{
@@ -144,6 +134,9 @@ export interface VoyantAuthResolveArgs<TBindings extends VoyantBindings = Voyant
144
134
  db: VoyantDb;
145
135
  ctx?: VoyantExecutionContext;
146
136
  }
137
+ export interface VoyantAuthAppTokenResolveArgs<TBindings extends VoyantBindings = VoyantBindings> extends VoyantAuthResolveArgs<TBindings> {
138
+ token: string;
139
+ }
147
140
  export interface VoyantAuthPermissionArgs<TBindings extends VoyantBindings = VoyantBindings> extends VoyantAuthResolveArgs<TBindings> {
148
141
  permission: VoyantPermission;
149
142
  auth: VoyantRequestAuthContext;
@@ -170,6 +163,7 @@ export interface VoyantAuthIntegration<TBindings extends VoyantBindings = Voyant
170
163
  * so `/v1/public/*` route guards work.
171
164
  */
172
165
  resolve?: (args: VoyantAuthResolveArgs<TBindings>) => Promise<VoyantRequestAuthContext | null> | VoyantRequestAuthContext | null;
166
+ resolveAppToken?: (args: VoyantAuthAppTokenResolveArgs<TBindings>) => Promise<VoyantAuthContext | null> | VoyantAuthContext | null;
173
167
  hasPermission?: (args: VoyantAuthPermissionArgs<TBindings>) => Promise<boolean> | boolean;
174
168
  validateApiKey?: (args: VoyantAuthApiKeyValidationArgs<TBindings>) => Promise<boolean> | boolean;
175
169
  onUnauthorized?: (args: VoyantAuthUnauthorizedArgs<TBindings>) => Promise<Response | null> | Response | null;
@@ -260,13 +254,6 @@ export interface VoyantAppConfig<TBindings extends VoyantBindings = VoyantBindin
260
254
  * `insertOutboxEvents(tx, ...)`. Default off.
261
255
  */
262
256
  outbox?: boolean;
263
- /**
264
- * Per-request metrics to the `env.METRICS` Analytics Engine dataset
265
- * (method, route pattern, surface, cache status, duration, status,
266
- * db query count). Enabled by default and inert without the binding;
267
- * set `false` to disable entirely.
268
- */
269
- metrics?: boolean;
270
257
  /**
271
258
  * Default request body limit enforced before route handlers parse
272
259
  * JSON/form data. Content-type-aware: JSON bodies are capped at 10 MiB
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voyant-travel/hono",
3
- "version": "0.128.0",
3
+ "version": "0.128.2",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -130,18 +130,17 @@
130
130
  "drizzle-orm": "^0.45.2",
131
131
  "hono": "^4.12.27",
132
132
  "zod": "^4.4.3",
133
- "@voyant-travel/core": "^0.124.0",
134
- "@voyant-travel/db": "^0.114.8",
135
- "@voyant-travel/types": "^0.109.2",
133
+ "@voyant-travel/core": "^0.125.1",
134
+ "@voyant-travel/db": "^0.114.10",
135
+ "@voyant-travel/types": "^0.109.3",
136
136
  "@voyant-travel/utils": "^0.107.1",
137
- "@voyant-travel/workflows": "^0.122.0"
137
+ "@voyant-travel/workflows": "^0.122.3"
138
138
  },
139
139
  "devDependencies": {
140
- "@cloudflare/workers-types": "^4.20260702.1",
141
140
  "typescript": "^6.0.3",
142
141
  "vitest": "^4.1.9",
143
142
  "@voyant-travel/voyant-typescript-config": "^0.1.0",
144
- "@voyant-travel/workflows-orchestrator": "^0.122.0"
143
+ "@voyant-travel/workflows-orchestrator": "^0.122.3"
145
144
  },
146
145
  "files": [
147
146
  "dist"