@guren/server 1.1.0 → 1.3.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.
@@ -1,13 +1,13 @@
1
1
  import * as hono from 'hono';
2
2
  import { Context, MiddlewareHandler, Hono, Next, ExecutionContext } from 'hono';
3
- import { l as OAuthManager, g as AuthContext, j as ApiToken, C as CreateSessionMiddlewareOptions } from './api-token-BSSCLlFW.js';
4
- import { A as AuthManager } from './AuthManager-SfhCNkAU.js';
3
+ import { l as OAuthManager, g as AuthContext, j as ApiToken, C as CreateSessionMiddlewareOptions } from './api-token-CB12xnVg.js';
4
+ import { A as AuthManager } from './AuthManager-BU8ZvenF.js';
5
5
  import { InlineConfig, ViteDevServer } from 'vite';
6
6
  import { E as EventManager } from './EventManager-CmIoLt7r.js';
7
7
  import { C as CacheManager } from './CacheManager-BkvHEOZX.js';
8
8
  import { M as MailManager } from './MailManager-DpMvYiP9.js';
9
9
  import { L as LogManager } from './LogManager-7mxnkaPM.js';
10
- import { I as I18nManager } from './I18nManager-BiSoczfV.js';
10
+ import { I as I18nManager } from './I18nManager-CmUqj6bU.js';
11
11
  import { B as BroadcastManager } from './BroadcastManager-CGWl9rUO.js';
12
12
  import { E as Encrypter, A as AppKeyring } from './app-key-CsBfRC_Q.js';
13
13
  import { S as StorageManager } from './StorageManager-oZTHqaza.js';
@@ -91,6 +91,8 @@ interface InertiaOptions {
91
91
  readonly headers?: HeadersInit;
92
92
  readonly request?: Request;
93
93
  readonly title?: string;
94
+ /** Value for the root `<html lang>` attribute. Defaults to "en". */
95
+ readonly lang?: string;
94
96
  readonly entry?: string;
95
97
  readonly importMap?: Record<string, string>;
96
98
  readonly styles?: string[];
@@ -198,7 +200,7 @@ declare function getValidatedData<T = Record<string, unknown>>(ctx: Context): T
198
200
  *
199
201
  * const CreateUserSchema = z.object({
200
202
  * name: z.string().min(1),
201
- * email: z.string().email(),
203
+ * email: z.email(),
202
204
  * })
203
205
  *
204
206
  * app.post('/users', validateRequest(CreateUserSchema), (c) => {
@@ -1258,6 +1260,7 @@ interface ServiceBindings {
1258
1260
  /** Structural type for DI containers, avoiding a hard dependency on Container. */
1259
1261
  interface ContainerLike {
1260
1262
  make(key: string): unknown;
1263
+ has?(key: string): boolean;
1261
1264
  }
1262
1265
  /**
1263
1266
  * Duck-type interface for Zod-like schemas.
@@ -1334,6 +1337,7 @@ type InertiaPageProps<TPage extends InertiaPageContractLike> = NonNullable<TPage
1334
1337
  * ```
1335
1338
  */
1336
1339
  declare class Controller {
1340
+ #private;
1337
1341
  /**
1338
1342
  * Dependencies to inject from the container.
1339
1343
  * Override in subclasses to declare required services.
@@ -1940,6 +1944,10 @@ declare class RouterMiddlewareGroupBuilder<M extends string = never> {
1940
1944
  delete<TParamsSchema extends SchemaLike<unknown>, TQuerySchema extends SchemaLike<unknown>, TBodySchema extends SchemaLike<unknown>, TOutputSchema extends SchemaLike<unknown>>(path: string, options: RouteContractOptions<TParamsSchema, TQuerySchema, TBodySchema, TOutputSchema>, handler: TypedRouteHandler<TParamsSchema, TQuerySchema, TBodySchema, TOutputSchema>): RouteBuilder<M>;
1941
1945
  }
1942
1946
 
1947
+ /**
1948
+ * Constructor shape accepted wherever a provider class can be registered.
1949
+ */
1950
+ type ServiceProviderConstructor = new (container: Container) => ServiceProvider;
1943
1951
  /**
1944
1952
  * Base service provider class.
1945
1953
  *
@@ -2007,11 +2015,11 @@ declare class ProviderManager {
2007
2015
  /**
2008
2016
  * Register a provider.
2009
2017
  */
2010
- register(providerOrClass: ServiceProvider | (new (container: Container) => ServiceProvider)): this;
2018
+ register(providerOrClass: ServiceProvider | ServiceProviderConstructor): this;
2011
2019
  /**
2012
2020
  * Register multiple providers.
2013
2021
  */
2014
- registerMany(providers: Array<ServiceProvider | (new (container: Container) => ServiceProvider)>): this;
2022
+ registerMany(providers: Array<ServiceProvider | ServiceProviderConstructor>): this;
2015
2023
  /**
2016
2024
  * Register all non-deferred providers.
2017
2025
  */
@@ -2060,10 +2068,7 @@ interface StartedViteDevServer {
2060
2068
  declare function startViteDevServer(options?: StartViteDevServerOptions): Promise<StartedViteDevServer>;
2061
2069
 
2062
2070
  type BootCallback = (app: Hono) => void | Promise<void>;
2063
- /**
2064
- * Service provider class constructor type.
2065
- */
2066
- type ServiceProviderConstructor = new (container: Container) => ServiceProvider;
2071
+
2067
2072
  type RouteRegistration = (router: Router) => void | Promise<void>;
2068
2073
  type ApplicationFeatures = Record<string, unknown>;
2069
2074
  interface ApplicationOptions {
@@ -2178,4 +2183,4 @@ declare class Application {
2178
2183
  }
2179
2184
  declare function createApp(options?: ApplicationOptions): Application;
2180
2185
 
2181
- export { type NotificationAttachment as $, Application as A, type InertiaPagePayload as B, Container as C, type DevBannerOptions as D, type ErrorResponse as E, type FailedJob as F, GUREN_ASCII_ART as G, type HostAuthorizationOptions as H, type InertiaOptions as I, type InertiaResponse as J, type InertiaSharedProps as K, type InertiaSsrContext as L, type InertiaSsrOptions as M, NotificationManager as N, type InertiaSsrRenderer as O, type Provider as P, QueueManager as Q, type InertiaSsrResult as R, ServiceProvider as S, type InferInertiaProps as T, Job as U, type JobClass as V, type JobFailureHandler as W, type JobHandler as X, type JobOptions as Y, type Notifiable as Z, Notification as _, type StartViteDevServerOptions as a, setExceptionHandler as a$, type NotificationChannel as a0, type NotificationChannelFactory as a1, type NotificationClass as a2, type NotificationMailMessage as a3, type NotificationManagerOptions as a4, ProviderManager as a5, type QueueConfig as a6, type QueueDriver as a7, type QueueDriverFactory as a8, type QueuedJob as a9, abortIf as aA, abortUnless as aB, clearJobRegistry as aC, createApp as aD, createContainer as aE, createCsrfMiddleware as aF, createExceptionHandler as aG, createHostAuthorizationMiddleware as aH, createNotificationManager as aI, createQueueManager as aJ, createSecurityHeaders as aK, csrfField as aL, formatValidationErrors as aM, getContainer as aN, getCsrfToken as aO, getExceptionHandler as aP, getInertiaSharedPropsResolver as aQ, getJob as aR, getNotificationManager as aS, getQueueDriver as aT, getRegisteredJobs as aU, getValidatedData as aV, inertia as aW, parseRequestPayload as aX, registerJob as aY, resolve as aZ, setContainer as a_, type RendererRegistration as aa, type ResolvedSharedInertiaProps as ab, type ResourceRouteOptions as ac, type RouteBuilder as ad, type RouteContractOptions as ae, type RouteDefinition as af, type RouteOpenApiMetadata as ag, type ResourceAction as ah, Router as ai, type SecurityHeadersOptions as aj, type SentNotification as ak, type ServiceBinding as al, type ServiceClass as am, type ServiceFactory as an, type ServiceProviderClass as ao, type ServiceProviderConstructor as ap, type ServiceProviderOptions as aq, type SharedInertiaPropsResolver as ar, type SlackAttachment as as, type SlackBlock as at, type SlackMessage as au, VALIDATED_DATA_KEY as av, type ValidateRequestOptions as aw, type ValidationSchema as ax, type WorkerOptions as ay, abort as az, type StartedViteDevServer as b, setInertiaSharedProps as b0, setNotificationManager as b1, setQueueDriver as b2, shareInertiaProps as b3, validate as b4, validateRequest as b5, validateRequestWith as b6, validateSafe as b7, verifyCsrfToken as b8, type ServiceBindings as c, type ApplicationListenOptions as d, type ApplicationOptions as e, type AuthPayload as f, type AuthPluginOptions as g, CSRF_FORM_FIELD as h, CSRF_HEADER_NAME as i, CSRF_TOKEN_KEY as j, type ContextualBinding as k, logDevServerBanner as l, type ContextualBindingBuilder as m, type ContextualNeedsBuilder as n, Controller as o, type ControllerInertiaProps as p, type CsrfOptions as q, type DatabaseChannelOptions as r, startViteDevServer as s, type DatabaseNotification as t, type ExceptionClass as u, ExceptionHandler as v, type ExceptionHandlerOptions as w, type ExceptionRenderer as x, type ExceptionReporter as y, type HstsOptions as z };
2186
+ export { Notification as $, Application as A, type HstsOptions as B, Container as C, type DevBannerOptions as D, type ErrorResponse as E, type FailedJob as F, GUREN_ASCII_ART as G, type HostAuthorizationOptions as H, type InertiaOptions as I, type InertiaPagePayload as J, type InertiaResponse as K, type InertiaSharedProps as L, type InertiaSsrContext as M, NotificationManager as N, type InertiaSsrOptions as O, type Provider as P, QueueManager as Q, type InertiaSsrRenderer as R, ServiceProvider as S, type InertiaSsrResult as T, type InferInertiaProps as U, Job as V, type JobClass as W, type JobFailureHandler as X, type JobHandler as Y, type JobOptions as Z, type Notifiable as _, type StartViteDevServerOptions as a, setExceptionHandler as a$, type NotificationAttachment as a0, type NotificationChannel as a1, type NotificationChannelFactory as a2, type NotificationClass as a3, type NotificationMailMessage as a4, type NotificationManagerOptions as a5, ProviderManager as a6, type QueueConfig as a7, type QueueDriver as a8, type QueueDriverFactory as a9, abortIf as aA, abortUnless as aB, clearJobRegistry as aC, createApp as aD, createContainer as aE, createCsrfMiddleware as aF, createExceptionHandler as aG, createHostAuthorizationMiddleware as aH, createNotificationManager as aI, createQueueManager as aJ, createSecurityHeaders as aK, csrfField as aL, formatValidationErrors as aM, getContainer as aN, getCsrfToken as aO, getExceptionHandler as aP, getInertiaSharedPropsResolver as aQ, getJob as aR, getNotificationManager as aS, getQueueDriver as aT, getRegisteredJobs as aU, getValidatedData as aV, inertia as aW, parseRequestPayload as aX, registerJob as aY, resolve as aZ, setContainer as a_, type QueuedJob as aa, type RendererRegistration as ab, type ResolvedSharedInertiaProps as ac, type ResourceRouteOptions as ad, type RouteBuilder as ae, type RouteContractOptions as af, type RouteDefinition as ag, type RouteOpenApiMetadata as ah, type ResourceAction as ai, Router as aj, type SecurityHeadersOptions as ak, type SentNotification as al, type ServiceBinding as am, type ServiceClass as an, type ServiceFactory as ao, type ServiceProviderClass as ap, type ServiceProviderOptions as aq, type SharedInertiaPropsResolver as ar, type SlackAttachment as as, type SlackBlock as at, type SlackMessage as au, VALIDATED_DATA_KEY as av, type ValidateRequestOptions as aw, type ValidationSchema as ax, type WorkerOptions as ay, abort as az, type StartedViteDevServer as b, setInertiaSharedProps as b0, setNotificationManager as b1, setQueueDriver as b2, shareInertiaProps as b3, validate as b4, validateRequest as b5, validateRequestWith as b6, validateSafe as b7, verifyCsrfToken as b8, type ServiceProviderConstructor as c, type ServiceBindings as d, type ApplicationListenOptions as e, type ApplicationOptions as f, type AuthPayload as g, type AuthPluginOptions as h, CSRF_FORM_FIELD as i, CSRF_HEADER_NAME as j, CSRF_TOKEN_KEY as k, logDevServerBanner as l, type ContextualBinding as m, type ContextualBindingBuilder as n, type ContextualNeedsBuilder as o, Controller as p, type ControllerInertiaProps as q, type CsrfOptions as r, startViteDevServer as s, type DatabaseChannelOptions as t, type DatabaseNotification as u, type ExceptionClass as v, ExceptionHandler as w, type ExceptionHandlerOptions as x, type ExceptionRenderer as y, type ExceptionReporter as z };
@@ -1,6 +1,6 @@
1
1
  import { Context } from 'hono';
2
2
  import { Model } from '@guren/orm';
3
- import { A as Authenticatable, U as UserProvider, a as AuthCredentials, b as AuthManagerContract, c as AuthManagerOptions, G as GuardFactory, P as ProviderFactory, d as GuardContext, e as Guard, f as AttachContextOptions, g as AuthContext } from './api-token-BSSCLlFW.js';
3
+ import { A as Authenticatable, U as UserProvider, a as AuthCredentials, b as AuthManagerContract, c as AuthManagerOptions, G as GuardFactory, P as ProviderFactory, d as GuardContext, e as Guard, f as AttachContextOptions, g as AuthContext } from './api-token-CB12xnVg.js';
4
4
 
5
5
  interface PasswordHasher {
6
6
  hash(plain: string): Promise<string>;
@@ -1,4 +1,4 @@
1
- import { C as Container } from './Application-BnsyCKXY.js';
1
+ import { C as Container } from './Application-DImeY-VW.js';
2
2
 
3
3
  /**
4
4
  * Parsed argument definition.
@@ -272,4 +272,4 @@ declare function t(key: string, replacements?: ReplacementValues): string;
272
272
  */
273
273
  declare function tc(key: string, count: number, replacements?: ReplacementValues): string;
274
274
 
275
- export { I18nManager as I, type PluralizationRule as P, type ReplacementValues as R, type TranslationLoader as T, type I18nConfig as a, type TranslationLoaderFactory as b, type TranslationMessages as c, Translator as d, type TranslatorOptions as e, createI18n as f, getI18n as g, tc as h, setI18n as s, t };
275
+ export { I18nManager as I, type PluralizationRule as P, type ReplacementValues as R, Translator as T, type I18nConfig as a, type TranslationLoader as b, type TranslationLoaderFactory as c, type TranslationMessages as d, type TranslatorOptions as e, createI18n as f, getI18n as g, tc as h, setI18n as s, t };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  McpServiceProvider
3
- } from "./chunk-WVY45EIW.js";
4
- import "./chunk-VT5KRDPH.js";
3
+ } from "./chunk-J753UWAY.js";
4
+ import "./chunk-STZUODC3.js";
5
5
  export {
6
6
  McpServiceProvider
7
7
  };
@@ -90,6 +90,37 @@ interface UserProvider<User = Authenticatable> {
90
90
  */
91
91
  sanitize?(user: User): User;
92
92
  }
93
+ /**
94
+ * The conventional names for the credential columns that
95
+ * `ModelUserProvider.sanitize()` strips: the password column (`password`
96
+ * by default, commonly configured as `passwordHash`/`password_hash`) and
97
+ * the remember-token column (`remember_token` by default, commonly
98
+ * `rememberToken`).
99
+ */
100
+ type DefaultSanitizedKeys = 'password' | 'passwordHash' | 'password_hash' | 'rememberToken' | 'remember_token';
101
+ /**
102
+ * The shape of a user record after auth-layer sanitization — what
103
+ * `auth.user()` returns at runtime when the provider's column names
104
+ * follow the {@link DefaultSanitizedKeys} conventions. Removes those
105
+ * conventional credential keys plus any extra keys passed as `Hidden`.
106
+ *
107
+ * The runtime strips exactly the *configured* password/remember-token
108
+ * columns plus the model's `static hidden` — a static type cannot see
109
+ * that configuration. If your columns use other names, or the model
110
+ * hides additional fields, list them in `Hidden` explicitly. Only
111
+ * meaningful for concrete record types (index-signature records like
112
+ * `Record<string, string>` cannot have individual keys removed).
113
+ *
114
+ * @example
115
+ * ```ts
116
+ * const user = await this.auth.userOrFail<Sanitized<UserRecord>>()
117
+ * user.passwordHash // compile error — stripped at runtime
118
+ *
119
+ * // With additional hidden fields or custom column names:
120
+ * type SafeUser = Sanitized<UserRecord, 'twoFactorSecret' | 'credentialDigest'>
121
+ * ```
122
+ */
123
+ type Sanitized<User, Hidden extends string = never> = User extends unknown ? Omit<User, Extract<keyof User, DefaultSanitizedKeys | Hidden>> : never;
93
124
  interface GuardContext {
94
125
  ctx: Context;
95
126
  session: Session | undefined;
@@ -170,6 +201,12 @@ interface OAuthStateConfig {
170
201
  expiresIn?: number;
171
202
  stateLength?: number;
172
203
  hashAlgorithm?: 'sha256' | 'sha512';
204
+ /**
205
+ * External hosts allowed as `redirectTo` targets (supports `*.example.com`
206
+ * wildcards). App-relative paths (`/dashboard`) are always allowed; anything
207
+ * else is dropped to prevent open redirects.
208
+ */
209
+ allowedRedirectHosts?: string[];
173
210
  }
174
211
  interface OAuthAuthorizeOptions {
175
212
  scope?: string[];
@@ -211,6 +248,16 @@ declare class OAuthManager {
211
248
  expiresAt: Date;
212
249
  }>;
213
250
  user(providerName: string, payload: OAuthCallbackPayload): Promise<OAuthUserProfile>;
251
+ /**
252
+ * Verify the callback and return the user profile together with the
253
+ * sanitized `redirectTo` stored at authorize time. `redirectTo` is safe to
254
+ * pass to a redirect response: app-relative paths and allowlisted hosts
255
+ * only.
256
+ */
257
+ handleCallback(providerName: string, payload: OAuthCallbackPayload): Promise<{
258
+ profile: OAuthUserProfile;
259
+ redirectTo?: string;
260
+ }>;
214
261
  }
215
262
  declare function createOAuthManager(options?: OAuthManagerOptions): OAuthManager;
216
263
  declare function createOAuthState(provider: string, store: OAuthStateStore, config?: OAuthStateConfig, redirectTo?: string, fixedState?: string): Promise<{
@@ -218,6 +265,14 @@ declare function createOAuthState(provider: string, store: OAuthStateStore, conf
218
265
  expiresAt: Date;
219
266
  }>;
220
267
  declare function verifyOAuthState(state: string, provider: string, store: OAuthStateStore, config?: OAuthStateConfig): Promise<OAuthStatePayload | null>;
268
+ /**
269
+ * Reduce a `redirectTo` value to a safe target: app-relative paths always
270
+ * pass; absolute http(s) URLs pass only when their host is in the allowlist
271
+ * (supports `*.example.com` wildcards). Everything else — protocol-relative
272
+ * URLs, backslash tricks, `javascript:` and other schemes — returns
273
+ * `undefined`.
274
+ */
275
+ declare function sanitizeOAuthRedirect(redirectTo: string | undefined, allowedHosts?: string[]): string | undefined;
221
276
  declare function buildOAuthAuthorizeUrl(provider: OAuthProviderConfig, state: string, options?: {
222
277
  scope?: string[];
223
278
  extraParams?: Record<string, string>;
@@ -538,4 +593,4 @@ declare function getApiTokenOrFail(ctx: Context): {
538
593
  abilities: string[];
539
594
  };
540
595
 
541
- export { revokeAllApiTokens as $, type Authenticatable as A, type BearerTokenMiddlewareOptions as B, type CreateSessionMiddlewareOptions as C, buildOAuthAuthorizeUrl as D, buildOAuthRedirectUrl as E, createApiToken as F, type GuardFactory as G, createBearerTokenMiddleware as H, createDiscordOAuthProviderConfig as I, createGitHubOAuthProviderConfig as J, createGoogleOAuthProviderConfig as K, createOAuthManager as L, MemoryApiTokenStore as M, createOAuthState as N, type OAuthStateStore as O, type ProviderFactory as P, createSessionMiddleware as Q, exchangeOAuthCode as R, type SessionStore as S, fetchOAuthUserProfile as T, type UserProvider as U, getApiToken as V, getApiTokenOrFail as W, getSessionFromContext as X, getUserApiTokens as Y, parseApiToken as Z, parseOAuthRedirectUrl as _, type AuthCredentials as a, revokeApiToken as a0, tokenCan as a1, tokenCanAll as a2, tokenCanAny as a3, verifyApiToken as a4, verifyOAuthState as a5, type AuthManagerContract as b, type AuthManagerOptions as c, type GuardContext as d, type Guard as e, type AttachContextOptions as f, type AuthContext as g, type SessionData as h, type ApiTokenStore as i, type ApiToken as j, type OAuthStatePayload as k, OAuthManager as l, API_TOKEN_KEY as m, type CreateApiTokenOptions as n, type CreateApiTokenResult as o, MemoryOAuthStateStore as p, MemorySessionStore as q, type OAuthAuthorizeOptions as r, type OAuthCallbackPayload as s, type OAuthManagerOptions as t, type OAuthProviderConfig as u, type OAuthProviderFactoryInput as v, type OAuthStateConfig as w, type OAuthTokenResult as x, type OAuthUserProfile as y, type Session as z };
596
+ export { parseApiToken as $, type Authenticatable as A, type BearerTokenMiddlewareOptions as B, type CreateSessionMiddlewareOptions as C, type DefaultSanitizedKeys as D, type Session as E, buildOAuthAuthorizeUrl as F, type GuardFactory as G, buildOAuthRedirectUrl as H, createApiToken as I, createBearerTokenMiddleware as J, createDiscordOAuthProviderConfig as K, createGitHubOAuthProviderConfig as L, MemoryApiTokenStore as M, createGoogleOAuthProviderConfig as N, type OAuthStateStore as O, type ProviderFactory as P, createOAuthManager as Q, createOAuthState as R, type SessionStore as S, createSessionMiddleware as T, type UserProvider as U, exchangeOAuthCode as V, fetchOAuthUserProfile as W, getApiToken as X, getApiTokenOrFail as Y, getSessionFromContext as Z, getUserApiTokens as _, type AuthCredentials as a, parseOAuthRedirectUrl as a0, revokeAllApiTokens as a1, revokeApiToken as a2, sanitizeOAuthRedirect as a3, tokenCan as a4, tokenCanAll as a5, tokenCanAny as a6, verifyApiToken as a7, verifyOAuthState as a8, type AuthManagerContract as b, type AuthManagerOptions as c, type GuardContext as d, type Guard as e, type AttachContextOptions as f, type AuthContext as g, type SessionData as h, type ApiTokenStore as i, type ApiToken as j, type OAuthStatePayload as k, OAuthManager as l, API_TOKEN_KEY as m, type CreateApiTokenOptions as n, type CreateApiTokenResult as o, MemoryOAuthStateStore as p, MemorySessionStore as q, type OAuthAuthorizeOptions as r, type OAuthCallbackPayload as s, type OAuthManagerOptions as t, type OAuthProviderConfig as u, type OAuthProviderFactoryInput as v, type OAuthStateConfig as w, type OAuthTokenResult as x, type OAuthUserProfile as y, type Sanitized as z };
@@ -1,9 +1,9 @@
1
- import { P as PasswordHasher } from '../AuthManager-SfhCNkAU.js';
2
- export { A as AuthManager, B as BaseUserProvider, M as ModelUserProvider } from '../AuthManager-SfhCNkAU.js';
3
- import { A as Authenticatable, e as Guard, U as UserProvider, z as Session, a as AuthCredentials } from '../api-token-BSSCLlFW.js';
4
- export { m as API_TOKEN_KEY, j as ApiToken, i as ApiTokenStore, f as AttachContextOptions, g as AuthContext, b as AuthManagerContract, c as AuthManagerOptions, B as BearerTokenMiddlewareOptions, n as CreateApiTokenOptions, o as CreateApiTokenResult, d as GuardContext, G as GuardFactory, M as MemoryApiTokenStore, p as MemoryOAuthStateStore, r as OAuthAuthorizeOptions, s as OAuthCallbackPayload, l as OAuthManager, t as OAuthManagerOptions, u as OAuthProviderConfig, v as OAuthProviderFactoryInput, w as OAuthStateConfig, k as OAuthStatePayload, O as OAuthStateStore, x as OAuthTokenResult, y as OAuthUserProfile, P as ProviderFactory, D as buildOAuthAuthorizeUrl, E as buildOAuthRedirectUrl, F as createApiToken, H as createBearerTokenMiddleware, I as createDiscordOAuthProviderConfig, J as createGitHubOAuthProviderConfig, K as createGoogleOAuthProviderConfig, L as createOAuthManager, N as createOAuthState, R as exchangeOAuthCode, T as fetchOAuthUserProfile, V as getApiToken, W as getApiTokenOrFail, Y as getUserApiTokens, Z as parseApiToken, _ as parseOAuthRedirectUrl, $ as revokeAllApiTokens, a0 as revokeApiToken, a1 as tokenCan, a2 as tokenCanAll, a3 as tokenCanAny, a4 as verifyApiToken, a5 as verifyOAuthState } from '../api-token-BSSCLlFW.js';
1
+ import { P as PasswordHasher } from '../AuthManager-BU8ZvenF.js';
2
+ export { A as AuthManager, B as BaseUserProvider, M as ModelUserProvider } from '../AuthManager-BU8ZvenF.js';
3
+ import { A as Authenticatable, e as Guard, U as UserProvider, E as Session, a as AuthCredentials } from '../api-token-CB12xnVg.js';
4
+ export { m as API_TOKEN_KEY, j as ApiToken, i as ApiTokenStore, f as AttachContextOptions, g as AuthContext, b as AuthManagerContract, c as AuthManagerOptions, B as BearerTokenMiddlewareOptions, n as CreateApiTokenOptions, o as CreateApiTokenResult, D as DefaultSanitizedKeys, d as GuardContext, G as GuardFactory, M as MemoryApiTokenStore, p as MemoryOAuthStateStore, r as OAuthAuthorizeOptions, s as OAuthCallbackPayload, l as OAuthManager, t as OAuthManagerOptions, u as OAuthProviderConfig, v as OAuthProviderFactoryInput, w as OAuthStateConfig, k as OAuthStatePayload, O as OAuthStateStore, x as OAuthTokenResult, y as OAuthUserProfile, P as ProviderFactory, z as Sanitized, F as buildOAuthAuthorizeUrl, H as buildOAuthRedirectUrl, I as createApiToken, J as createBearerTokenMiddleware, K as createDiscordOAuthProviderConfig, L as createGitHubOAuthProviderConfig, N as createGoogleOAuthProviderConfig, Q as createOAuthManager, R as createOAuthState, V as exchangeOAuthCode, W as fetchOAuthUserProfile, X as getApiToken, Y as getApiTokenOrFail, _ as getUserApiTokens, $ as parseApiToken, a0 as parseOAuthRedirectUrl, a1 as revokeAllApiTokens, a2 as revokeApiToken, a3 as sanitizeOAuthRedirect, a4 as tokenCan, a5 as tokenCanAll, a6 as tokenCanAny, a7 as verifyApiToken, a8 as verifyOAuthState } from '../api-token-CB12xnVg.js';
5
5
  import { PlainObject, Model } from '@guren/orm';
6
- export { b as EmailVerificationConfig, a as EmailVerificationToken, c as EmailVerificationTokenResult, E as EmailVerificationTokenStore, M as MemoryEmailVerificationStore, d as MemoryPasswordResetStore, e as PasswordResetConfig, f as PasswordResetTokenResult, P as PasswordResetTokenStore, g as buildPasswordResetUrl, q as buildTokenUrl, h as buildVerificationUrl, i as completeEmailVerification, j as completePasswordReset, k as createEmailVerificationToken, l as createPasswordResetToken, s as generateId, t as generateToken, u as hashToken, m as isEmailVerified, p as parsePasswordResetUrl, w as parseTokenUrl, n as parseVerificationUrl, r as requireVerifiedEmail, x as secureCompare, y as secureStringCompare, v as verifyEmailToken, o as verifyPasswordResetToken } from '../email-verification-CAeArjui.js';
6
+ export { b as EmailVerificationConfig, a as EmailVerificationToken, c as EmailVerificationTokenResult, E as EmailVerificationTokenStore, M as MemoryEmailVerificationStore, d as MemoryPasswordResetStore, e as PasswordResetConfig, f as PasswordResetTokenResult, P as PasswordResetTokenStore, g as buildPasswordResetUrl, q as buildTokenUrl, h as buildVerificationUrl, i as completeEmailVerification, j as completePasswordReset, k as createEmailVerificationToken, l as createPasswordResetToken, s as generateId, t as generateToken, u as hashToken, m as isEmailVerified, p as parsePasswordResetUrl, w as parseTokenUrl, n as parseVerificationUrl, r as requireVerifiedEmail, x as secureCompare, y as secureStringCompare, v as verifyEmailToken, o as verifyPasswordResetToken } from '../email-verification-D9u7krkn.js';
7
7
  import 'hono';
8
8
 
9
9
  interface SessionGuardOptions {
@@ -45,6 +45,7 @@ import {
45
45
  requireVerifiedEmail,
46
46
  revokeAllApiTokens,
47
47
  revokeApiToken,
48
+ sanitizeOAuthRedirect,
48
49
  secureCompare,
49
50
  secureStringCompare,
50
51
  tokenCan,
@@ -54,7 +55,7 @@ import {
54
55
  verifyEmailToken,
55
56
  verifyOAuthState,
56
57
  verifyPasswordResetToken
57
- } from "../chunk-2T6JN4VR.js";
58
+ } from "../chunk-VY2JPS6V.js";
58
59
  import "../chunk-DAQKYKLH.js";
59
60
  import "../chunk-ONSDE37A.js";
60
61
  import "../chunk-QQKTH5KX.js";
@@ -105,6 +106,7 @@ export {
105
106
  requireVerifiedEmail,
106
107
  revokeAllApiTokens,
107
108
  revokeApiToken,
109
+ sanitizeOAuthRedirect,
108
110
  secureCompare,
109
111
  secureStringCompare,
110
112
  tokenCan,
@@ -6,7 +6,7 @@ import standard from "figlet/importable-fonts/Standard.js";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@guren/server",
9
- version: "1.1.0",
9
+ version: "1.3.0",
10
10
  type: "module",
11
11
  license: "MIT",
12
12
  repository: {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ServiceProvider
3
- } from "./chunk-VT5KRDPH.js";
3
+ } from "./chunk-STZUODC3.js";
4
4
 
5
5
  // src/mcp/create-mcp-server.ts
6
6
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -45,6 +45,11 @@ var ProviderManager = class {
45
45
  );
46
46
  }
47
47
  const provider = providerOrClass instanceof ServiceProvider ? providerOrClass : new providerOrClass(this.container);
48
+ if (provider.isDeferred() && provider.provides().length === 0) {
49
+ throw new Error(
50
+ `Deferred provider "${provider.constructor.name}" must declare at least one service in "provides", otherwise it can never be loaded.`
51
+ );
52
+ }
48
53
  if (provider.isDeferred()) {
49
54
  for (const service of provider.provides()) {
50
55
  this.deferredProviders.set(service, provider);
@@ -155,6 +155,25 @@ var SessionImpl = class {
155
155
  return this.originalId;
156
156
  }
157
157
  };
158
+ var TESTING_SESSION_HEADER = "x-testing-session";
159
+ function resolveTestingSession(ctx) {
160
+ if (!process.env.GUREN_TESTING) {
161
+ return null;
162
+ }
163
+ const rawSession = ctx.req.header(TESTING_SESSION_HEADER);
164
+ if (!rawSession) {
165
+ return null;
166
+ }
167
+ try {
168
+ const parsed = JSON.parse(rawSession);
169
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
170
+ return null;
171
+ }
172
+ return parsed;
173
+ } catch {
174
+ return null;
175
+ }
176
+ }
158
177
  function encodeBase64Url(value) {
159
178
  return Buffer.from(value, "utf8").toString("base64url");
160
179
  }
@@ -216,7 +235,9 @@ function createSessionMiddleware(options = {}) {
216
235
  const existingId = signer.verify(getCookie(ctx, cookieName));
217
236
  const sessionId = existingId ?? globalThis.crypto.randomUUID();
218
237
  const isNew = !existingId;
219
- const initialData = existingId ? await store.read(existingId) ?? {} : {};
238
+ const storedData = existingId ? await store.read(existingId) ?? {} : {};
239
+ const testingData = resolveTestingSession(ctx);
240
+ const initialData = testingData ? { ...storedData, ...testingData } : storedData;
220
241
  const session = new SessionImpl(sessionId, initialData, isNew);
221
242
  session.ageFlashData();
222
243
  ctx.set(SESSION_CONTEXT_KEY, session);
@@ -1270,6 +1291,29 @@ function getApiTokenOrFail(ctx) {
1270
1291
  return result;
1271
1292
  }
1272
1293
 
1294
+ // src/support/redirect-target.ts
1295
+ function normalizeRedirectTarget(value) {
1296
+ return value.replace(/\\/g, "/");
1297
+ }
1298
+ function isAppRelativePath(value) {
1299
+ return value.startsWith("/") && !value.startsWith("//");
1300
+ }
1301
+ function hostMatchesAllowlist(target, allowedHosts) {
1302
+ const hostname = target.hostname.toLowerCase();
1303
+ const host = target.host.toLowerCase();
1304
+ for (const allowed of allowedHosts) {
1305
+ if (allowed.startsWith("*.")) {
1306
+ const suffix = allowed.slice(1).toLowerCase();
1307
+ if (hostname.endsWith(suffix) && hostname.length > suffix.length) {
1308
+ return true;
1309
+ }
1310
+ } else if (host === allowed.toLowerCase()) {
1311
+ return true;
1312
+ }
1313
+ }
1314
+ return false;
1315
+ }
1316
+
1273
1317
  // src/auth/oauth/index.ts
1274
1318
  var DEFAULT_STATE_EXPIRES_IN = 10 * 60 * 1e3;
1275
1319
  var DEFAULT_STATE_LENGTH = 24;
@@ -1324,7 +1368,8 @@ var OAuthManager = class {
1324
1368
  this.stateConfig = {
1325
1369
  expiresIn: options.stateConfig?.expiresIn ?? DEFAULT_STATE_EXPIRES_IN,
1326
1370
  stateLength: options.stateConfig?.stateLength ?? DEFAULT_STATE_LENGTH,
1327
- hashAlgorithm: options.stateConfig?.hashAlgorithm ?? DEFAULT_STATE_HASH_ALGORITHM
1371
+ hashAlgorithm: options.stateConfig?.hashAlgorithm ?? DEFAULT_STATE_HASH_ALGORITHM,
1372
+ allowedRedirectHosts: options.stateConfig?.allowedRedirectHosts ?? []
1328
1373
  };
1329
1374
  }
1330
1375
  registerProvider(name, config) {
@@ -1354,13 +1399,24 @@ var OAuthManager = class {
1354
1399
  return { url, state, expiresAt };
1355
1400
  }
1356
1401
  async user(providerName, payload) {
1402
+ const { profile } = await this.handleCallback(providerName, payload);
1403
+ return profile;
1404
+ }
1405
+ /**
1406
+ * Verify the callback and return the user profile together with the
1407
+ * sanitized `redirectTo` stored at authorize time. `redirectTo` is safe to
1408
+ * pass to a redirect response: app-relative paths and allowlisted hosts
1409
+ * only.
1410
+ */
1411
+ async handleCallback(providerName, payload) {
1357
1412
  const provider = this.getProvider(providerName);
1358
1413
  const verified = await verifyOAuthState(payload.state, providerName, this.stateStore, this.stateConfig);
1359
1414
  if (!verified) {
1360
1415
  throw new AuthenticationException("Invalid or expired OAuth state.");
1361
1416
  }
1362
1417
  const token = await exchangeOAuthCode(provider, payload.code);
1363
- return fetchOAuthUserProfile(provider, token);
1418
+ const profile = await fetchOAuthUserProfile(provider, token);
1419
+ return { profile, redirectTo: verified.redirectTo };
1364
1420
  }
1365
1421
  };
1366
1422
  function createOAuthManager(options = {}) {
@@ -1371,7 +1427,11 @@ async function createOAuthState(provider, store, config = {}, redirectTo, fixedS
1371
1427
  const hashAlgorithm = config.hashAlgorithm ?? DEFAULT_STATE_HASH_ALGORITHM;
1372
1428
  const expiresAt = new Date(Date.now() + (config.expiresIn ?? DEFAULT_STATE_EXPIRES_IN));
1373
1429
  const stateHash = hashToken(state, hashAlgorithm);
1374
- await store.store(stateHash, { provider, redirectTo, expiresAt });
1430
+ await store.store(stateHash, {
1431
+ provider,
1432
+ redirectTo: sanitizeOAuthRedirect(redirectTo, config.allowedRedirectHosts),
1433
+ expiresAt
1434
+ });
1375
1435
  return { state, expiresAt };
1376
1436
  }
1377
1437
  async function verifyOAuthState(state, provider, store, config = {}) {
@@ -1381,7 +1441,26 @@ async function verifyOAuthState(state, provider, store, config = {}) {
1381
1441
  if (!payload) return null;
1382
1442
  await store.delete(stateHash);
1383
1443
  if (payload.provider !== provider) return null;
1384
- return payload;
1444
+ return { ...payload, redirectTo: sanitizeOAuthRedirect(payload.redirectTo, config.allowedRedirectHosts) };
1445
+ }
1446
+ function sanitizeOAuthRedirect(redirectTo, allowedHosts = []) {
1447
+ const value = redirectTo?.trim();
1448
+ if (!value) {
1449
+ return void 0;
1450
+ }
1451
+ const normalized = normalizeRedirectTarget(value);
1452
+ if (isAppRelativePath(normalized)) {
1453
+ return normalized;
1454
+ }
1455
+ try {
1456
+ const target = new URL(normalized);
1457
+ if (target.protocol !== "http:" && target.protocol !== "https:") {
1458
+ return void 0;
1459
+ }
1460
+ return hostMatchesAllowlist(target, allowedHosts) ? normalized : void 0;
1461
+ } catch {
1462
+ return void 0;
1463
+ }
1385
1464
  }
1386
1465
  function buildOAuthAuthorizeUrl(provider, state, options = {}) {
1387
1466
  const url = new URL(provider.authorizeUrl);
@@ -1517,6 +1596,9 @@ export {
1517
1596
  parseTokenUrl,
1518
1597
  SessionGuard,
1519
1598
  AuthManager,
1599
+ normalizeRedirectTarget,
1600
+ isAppRelativePath,
1601
+ hostMatchesAllowlist,
1520
1602
  MemoryApiTokenStore,
1521
1603
  createApiToken,
1522
1604
  parseApiToken,
@@ -1536,6 +1618,7 @@ export {
1536
1618
  createOAuthManager,
1537
1619
  createOAuthState,
1538
1620
  verifyOAuthState,
1621
+ sanitizeOAuthRedirect,
1539
1622
  buildOAuthAuthorizeUrl,
1540
1623
  exchangeOAuthCode,
1541
1624
  fetchOAuthUserProfile,
@@ -1,4 +1,4 @@
1
- import { A as Authenticatable, U as UserProvider } from './api-token-BSSCLlFW.js';
1
+ import { A as Authenticatable, U as UserProvider } from './api-token-CB12xnVg.js';
2
2
 
3
3
  /**
4
4
  * Hash a token using SHA-256 or SHA-512.
@@ -1,5 +1,5 @@
1
- import { P as PluralizationRule, T as TranslationLoader, c as TranslationMessages } from '../I18nManager-BiSoczfV.js';
2
- export { a as I18nConfig, I as I18nManager, R as ReplacementValues, b as TranslationLoaderFactory, d as Translator, e as TranslatorOptions, f as createI18n, g as getI18n, s as setI18n, t, h as tc } from '../I18nManager-BiSoczfV.js';
1
+ import { P as PluralizationRule, b as TranslationLoader, d as TranslationMessages } from '../I18nManager-CmUqj6bU.js';
2
+ export { a as I18nConfig, I as I18nManager, R as ReplacementValues, c as TranslationLoaderFactory, T as Translator, e as TranslatorOptions, f as createI18n, g as getI18n, s as setI18n, t, h as tc } from '../I18nManager-CmUqj6bU.js';
3
3
 
4
4
  /**
5
5
  * Default pluralization rules for common languages.
package/dist/index.d.ts CHANGED
@@ -1,14 +1,14 @@
1
- import { E as ErrorResponse, S as ServiceProvider, C as Container, c as ServiceBindings, Q as QueueManager, N as NotificationManager } from './Application-BnsyCKXY.js';
2
- export { A as Application, d as ApplicationListenOptions, e as ApplicationOptions, f as AuthPayload, g as AuthPluginOptions, h as CSRF_FORM_FIELD, i as CSRF_HEADER_NAME, j as CSRF_TOKEN_KEY, P as ContainerProvider, k as ContextualBinding, m as ContextualBindingBuilder, n as ContextualNeedsBuilder, o as Controller, p as ControllerInertiaProps, q as CsrfOptions, r as DatabaseChannelOptions, t as DatabaseNotification, u as ExceptionClass, v as ExceptionHandler, w as ExceptionHandlerOptions, x as ExceptionRenderer, y as ExceptionReporter, F as FailedJob, H as HostAuthorizationOptions, z as HstsOptions, I as InertiaOptions, B as InertiaPagePayload, J as InertiaResponse, K as InertiaSharedProps, L as InertiaSsrContext, M as InertiaSsrOptions, O as InertiaSsrRenderer, R as InertiaSsrResult, T as InferInertiaProps, U as Job, V as JobClass, W as JobFailureHandler, X as JobHandler, Y as JobOptions, Z as Notifiable, _ as Notification, $ as NotificationAttachment, a0 as NotificationChannel, a1 as NotificationChannelFactory, a2 as NotificationClass, a3 as NotificationMailMessage, a4 as NotificationManagerOptions, a5 as ProviderManager, a6 as QueueConfig, a7 as QueueDriver, a8 as QueueDriverFactory, a9 as QueuedJob, aa as RendererRegistration, ab as ResolvedSharedInertiaProps, ac as ResourceRouteOptions, ad as RouteBuilder, ae as RouteContractOptions, af as RouteDefinition, ag as RouteOpenApiMetadata, ah as RouteResourceAction, ai as Router, aj as SecurityHeadersOptions, ak as SentNotification, al as ServiceBinding, am as ServiceClass, an as ServiceFactory, ao as ServiceProviderClass, ap as ServiceProviderConstructor, aq as ServiceProviderOptions, ar as SharedInertiaPropsResolver, as as SlackAttachment, at as SlackBlock, au as SlackMessage, av as VALIDATED_DATA_KEY, aw as ValidateRequestOptions, ax as ValidationSchema, ay as WorkerOptions, az as abort, aA as abortIf, aB as abortUnless, aC as clearJobRegistry, aD as createApp, aE as createContainer, aF as createCsrfMiddleware, aG as createExceptionHandler, aH as createHostAuthorizationMiddleware, aI as createNotificationManager, aJ as createQueueManager, aK as createSecurityHeaders, aL as csrfField, aM as formatValidationErrors, aN as getContainer, aO as getCsrfToken, aP as getExceptionHandler, aQ as getInertiaSharedPropsResolver, aR as getJob, aS as getNotificationManager, aT as getQueueDriver, aU as getRegisteredJobs, aV as getValidatedData, aW as inertia, aX as parseRequestPayload, aY as registerJob, aZ as resolve, a_ as setContainer, a$ as setExceptionHandler, b0 as setInertiaSharedProps, b1 as setNotificationManager, b2 as setQueueDriver, b3 as shareInertiaProps, b4 as validate, b5 as validateRequest, b6 as validateRequestWith, b7 as validateSafe, b8 as verifyCsrfToken } from './Application-BnsyCKXY.js';
1
+ import { E as ErrorResponse, C as Container, c as ServiceProviderConstructor, S as ServiceProvider, d as ServiceBindings, Q as QueueManager, N as NotificationManager } from './Application-DImeY-VW.js';
2
+ export { A as Application, e as ApplicationListenOptions, f as ApplicationOptions, g as AuthPayload, h as AuthPluginOptions, i as CSRF_FORM_FIELD, j as CSRF_HEADER_NAME, k as CSRF_TOKEN_KEY, P as ContainerProvider, m as ContextualBinding, n as ContextualBindingBuilder, o as ContextualNeedsBuilder, p as Controller, q as ControllerInertiaProps, r as CsrfOptions, t as DatabaseChannelOptions, u as DatabaseNotification, v as ExceptionClass, w as ExceptionHandler, x as ExceptionHandlerOptions, y as ExceptionRenderer, z as ExceptionReporter, F as FailedJob, H as HostAuthorizationOptions, B as HstsOptions, I as InertiaOptions, J as InertiaPagePayload, K as InertiaResponse, L as InertiaSharedProps, M as InertiaSsrContext, O as InertiaSsrOptions, R as InertiaSsrRenderer, T as InertiaSsrResult, U as InferInertiaProps, V as Job, W as JobClass, X as JobFailureHandler, Y as JobHandler, Z as JobOptions, _ as Notifiable, $ as Notification, a0 as NotificationAttachment, a1 as NotificationChannel, a2 as NotificationChannelFactory, a3 as NotificationClass, a4 as NotificationMailMessage, a5 as NotificationManagerOptions, a6 as ProviderManager, a7 as QueueConfig, a8 as QueueDriver, a9 as QueueDriverFactory, aa as QueuedJob, ab as RendererRegistration, ac as ResolvedSharedInertiaProps, ad as ResourceRouteOptions, ae as RouteBuilder, af as RouteContractOptions, ag as RouteDefinition, ah as RouteOpenApiMetadata, ai as RouteResourceAction, aj as Router, ak as SecurityHeadersOptions, al as SentNotification, am as ServiceBinding, an as ServiceClass, ao as ServiceFactory, ap as ServiceProviderClass, aq as ServiceProviderOptions, ar as SharedInertiaPropsResolver, as as SlackAttachment, at as SlackBlock, au as SlackMessage, av as VALIDATED_DATA_KEY, aw as ValidateRequestOptions, ax as ValidationSchema, ay as WorkerOptions, az as abort, aA as abortIf, aB as abortUnless, aC as clearJobRegistry, aD as createApp, aE as createContainer, aF as createCsrfMiddleware, aG as createExceptionHandler, aH as createHostAuthorizationMiddleware, aI as createNotificationManager, aJ as createQueueManager, aK as createSecurityHeaders, aL as csrfField, aM as formatValidationErrors, aN as getContainer, aO as getCsrfToken, aP as getExceptionHandler, aQ as getInertiaSharedPropsResolver, aR as getJob, aS as getNotificationManager, aT as getQueueDriver, aU as getRegisteredJobs, aV as getValidatedData, aW as inertia, aX as parseRequestPayload, aY as registerJob, aZ as resolve, a_ as setContainer, a$ as setExceptionHandler, b0 as setInertiaSharedProps, b1 as setNotificationManager, b2 as setQueueDriver, b3 as shareInertiaProps, b4 as validate, b5 as validateRequest, b6 as validateRequestWith, b7 as validateSafe, b8 as verifyCsrfToken } from './Application-DImeY-VW.js';
3
3
  import * as hono from 'hono';
4
4
  import { Context, MiddlewareHandler } from 'hono';
5
5
  export { Context } from 'hono';
6
- import { P as PasswordHasher } from './AuthManager-SfhCNkAU.js';
7
- export { A as AuthManager, B as BaseUserProvider, M as ModelUserProvider } from './AuthManager-SfhCNkAU.js';
6
+ import { P as PasswordHasher } from './AuthManager-BU8ZvenF.js';
7
+ export { A as AuthManager, B as BaseUserProvider, M as ModelUserProvider } from './AuthManager-BU8ZvenF.js';
8
8
  export { AuthenticatableModel, NodeHasher, ScryptHasher, SessionGuard } from './auth/index.js';
9
- export { b as EmailVerificationConfig, a as EmailVerificationToken, c as EmailVerificationTokenResult, E as EmailVerificationTokenStore, M as MemoryEmailVerificationStore, d as MemoryPasswordResetStore, e as PasswordResetConfig, f as PasswordResetTokenResult, P as PasswordResetTokenStore, g as buildPasswordResetUrl, h as buildVerificationUrl, i as completeEmailVerification, j as completePasswordReset, k as createEmailVerificationToken, l as createPasswordResetToken, m as isEmailVerified, p as parsePasswordResetUrl, n as parseVerificationUrl, r as requireVerifiedEmail, v as verifyEmailToken, o as verifyPasswordResetToken } from './email-verification-CAeArjui.js';
10
- import { g as AuthContext } from './api-token-BSSCLlFW.js';
11
- export { m as API_TOKEN_KEY, j as ApiToken, i as ApiTokenStore, a as AuthCredentials, c as AuthManagerOptions, A as Authenticatable, B as BearerTokenMiddlewareOptions, n as CreateApiTokenOptions, o as CreateApiTokenResult, e as Guard, d as GuardContext, G as GuardFactory, M as MemoryApiTokenStore, p as MemoryOAuthStateStore, q as MemorySessionStore, r as OAuthAuthorizeOptions, s as OAuthCallbackPayload, l as OAuthManager, t as OAuthManagerOptions, u as OAuthProviderConfig, v as OAuthProviderFactoryInput, w as OAuthStateConfig, k as OAuthStatePayload, O as OAuthStateStore, x as OAuthTokenResult, y as OAuthUserProfile, P as ProviderFactory, z as Session, h as SessionData, S as SessionStore, U as UserProvider, D as buildOAuthAuthorizeUrl, E as buildOAuthRedirectUrl, F as createApiToken, H as createBearerTokenMiddleware, I as createDiscordOAuthProviderConfig, J as createGitHubOAuthProviderConfig, K as createGoogleOAuthProviderConfig, L as createOAuthManager, N as createOAuthState, Q as createSessionMiddleware, R as exchangeOAuthCode, T as fetchOAuthUserProfile, V as getApiToken, W as getApiTokenOrFail, X as getSessionFromContext, Y as getUserApiTokens, Z as parseApiToken, _ as parseOAuthRedirectUrl, $ as revokeAllApiTokens, a0 as revokeApiToken, a1 as tokenCan, a2 as tokenCanAll, a3 as tokenCanAny, a4 as verifyApiToken, a5 as verifyOAuthState } from './api-token-BSSCLlFW.js';
9
+ export { b as EmailVerificationConfig, a as EmailVerificationToken, c as EmailVerificationTokenResult, E as EmailVerificationTokenStore, M as MemoryEmailVerificationStore, d as MemoryPasswordResetStore, e as PasswordResetConfig, f as PasswordResetTokenResult, P as PasswordResetTokenStore, g as buildPasswordResetUrl, h as buildVerificationUrl, i as completeEmailVerification, j as completePasswordReset, k as createEmailVerificationToken, l as createPasswordResetToken, m as isEmailVerified, p as parsePasswordResetUrl, n as parseVerificationUrl, r as requireVerifiedEmail, v as verifyEmailToken, o as verifyPasswordResetToken } from './email-verification-D9u7krkn.js';
10
+ import { g as AuthContext } from './api-token-CB12xnVg.js';
11
+ export { m as API_TOKEN_KEY, j as ApiToken, i as ApiTokenStore, a as AuthCredentials, c as AuthManagerOptions, A as Authenticatable, B as BearerTokenMiddlewareOptions, n as CreateApiTokenOptions, o as CreateApiTokenResult, D as DefaultSanitizedKeys, e as Guard, d as GuardContext, G as GuardFactory, M as MemoryApiTokenStore, p as MemoryOAuthStateStore, q as MemorySessionStore, r as OAuthAuthorizeOptions, s as OAuthCallbackPayload, l as OAuthManager, t as OAuthManagerOptions, u as OAuthProviderConfig, v as OAuthProviderFactoryInput, w as OAuthStateConfig, k as OAuthStatePayload, O as OAuthStateStore, x as OAuthTokenResult, y as OAuthUserProfile, P as ProviderFactory, z as Sanitized, E as Session, h as SessionData, S as SessionStore, U as UserProvider, F as buildOAuthAuthorizeUrl, H as buildOAuthRedirectUrl, I as createApiToken, J as createBearerTokenMiddleware, K as createDiscordOAuthProviderConfig, L as createGitHubOAuthProviderConfig, N as createGoogleOAuthProviderConfig, Q as createOAuthManager, R as createOAuthState, T as createSessionMiddleware, V as exchangeOAuthCode, W as fetchOAuthUserProfile, X as getApiToken, Y as getApiTokenOrFail, Z as getSessionFromContext, _ as getUserApiTokens, $ as parseApiToken, a0 as parseOAuthRedirectUrl, a1 as revokeAllApiTokens, a2 as revokeApiToken, a3 as sanitizeOAuthRedirect, a4 as tokenCan, a5 as tokenCanAll, a6 as tokenCanAny, a7 as verifyApiToken, a8 as verifyOAuthState } from './api-token-CB12xnVg.js';
12
12
  export { M as Middleware, d as defineMiddleware, j as jsonResponse } from './index-9_Jzj5jo.js';
13
13
  import { E as EventManager } from './EventManager-CmIoLt7r.js';
14
14
  export { a as Event, b as EventClass, c as EventListener, d as EventSubscription, L as ListenerOptions, R as RegisteredListener, e as createEventManager } from './EventManager-CmIoLt7r.js';
@@ -29,8 +29,8 @@ export { getNextOccurrence, getNextOccurrences, isDue, isDueInTimezone, matchesC
29
29
  import { L as LogManager } from './LogManager-7mxnkaPM.js';
30
30
  export { C as ConsoleChannelConfig, D as DailyFileChannelConfig, F as FileChannelConfig, a as LOG_LEVEL_PRIORITY, b as LogChannel, c as LogChannelConfig, d as LogChannelFactory, e as LogConfig, f as LogContext, g as LogEntry, h as LogFormatter, i as LogLevel, j as Logger, k as LoggerOptions, S as StackChannelConfig, l as createLogManager, m as filterSensitiveData, n as getLogManager, s as setLogManager } from './LogManager-7mxnkaPM.js';
31
31
  export { ConsoleChannel, DailyFileChannel, FileChannel } from './logging/index.js';
32
- import { I as I18nManager } from './I18nManager-BiSoczfV.js';
33
- export { a as I18nConfig, P as PluralizationRule, R as ReplacementValues, T as TranslationLoader, b as TranslationLoaderFactory, c as TranslationMessages, d as Translator, e as TranslatorOptions, f as createI18n, g as getI18n, s as setI18n, t, h as tc } from './I18nManager-BiSoczfV.js';
32
+ import { I as I18nManager, T as Translator } from './I18nManager-CmUqj6bU.js';
33
+ export { a as I18nConfig, P as PluralizationRule, R as ReplacementValues, b as TranslationLoader, c as TranslationLoaderFactory, d as TranslationMessages, e as TranslatorOptions, f as createI18n, g as getI18n, s as setI18n, t, h as tc } from './I18nManager-CmUqj6bU.js';
34
34
  export { JsonLoader, MemoryLoader, getPluralizationRule, pluralizationRules, selectPluralForm } from './i18n/index.js';
35
35
  export { C as CheckResult, a as HealthCheck, b as HealthCheckOptions, H as HealthManager, c as HealthMiddlewareOptions, d as HealthReport, e as HealthStatus, f as createHealthManager } from './HealthManager-DUyMIzsZ.js';
36
36
  export { CacheCheck, CacheCheckOptions, CacheStoreInterface, CustomCheck, CustomCheckCallback, DatabaseCheck, DatabaseCheckOptions, DatabaseConnection, MemoryCheck, MemoryCheckOptions, RedisCheck, RedisCheckOptions, RedisClient, StorageCheck, StorageCheckOptions, StorageDriverInterface, customCheck } from './health/index.js';
@@ -38,8 +38,8 @@ export { DatabaseChannel, MailChannel, MailChannelOptions, MemoryChannel, SlackC
38
38
  import { B as BroadcastManager } from './BroadcastManager-CGWl9rUO.js';
39
39
  export { A as AuthMiddlewareOptions, a as BroadcastDriver, b as BroadcastDriverFactory, c as BroadcastEvent, d as BroadcastManagerOptions, e as BroadcastableEvent, C as Channel, f as ChannelAuthorizer, g as ChannelRegistration, P as PresenceBroadcastDriver, h as PresenceChannel, i as PresenceChannelAuthorizer, j as PresenceMember, k as PrivateChannel, S as SSEClient, l as SSEMiddlewareOptions, W as WebSocketClient, m as createBroadcastManager, n as getBroadcastManager, s as setBroadcastManager } from './BroadcastManager-CGWl9rUO.js';
40
40
  export { RedisClient as BroadcastRedisClient, RedisDriverOptions as BroadcastRedisDriverOptions, MemoryDriver as MemoryBroadcastDriver, RedisDriver as RedisBroadcastDriver, createTypedBroadcaster } from './broadcasting/index.js';
41
- import { I as InputInterface, P as ParsedSignature, O as OptionDefinition, a as CommandInstance, b as OutputInterface } from './ConsoleKernel-BDtBETjm.js';
42
- export { A as ArgumentDefinition, c as CommandClass, C as ConsoleKernel, d as ConsoleKernelOptions, e as ProgressInterface, f as PromptInterface, S as ScheduledCommand, g as createConsoleKernel } from './ConsoleKernel-BDtBETjm.js';
41
+ import { I as InputInterface, P as ParsedSignature, O as OptionDefinition, a as CommandInstance, b as OutputInterface } from './ConsoleKernel-COKndy_V.js';
42
+ export { A as ArgumentDefinition, c as CommandClass, C as ConsoleKernel, d as ConsoleKernelOptions, e as ProgressInterface, f as PromptInterface, S as ScheduledCommand, g as createConsoleKernel } from './ConsoleKernel-COKndy_V.js';
43
43
  import * as readline from 'readline';
44
44
  export { R as AuthResponse, A as AuthUser, a as AuthorizationResponse, b as AuthorizeOptions, G as Gate, c as GateCallback, d as GateDefinition, e as GateOptions, P as PolicyClass, f as PolicyInterface, g as PolicyMethod, h as PolicyRegistration, i as ResourceAction, j as ResponseBuilder, k as authorizeAbility, l as can, m as cannot, n as createGate, o as defineGate, p as getGate, s as setGate } from './Gate-CynjZCtS.js';
45
45
  export { AuthorizedContext, Policy, authorizeAllMiddleware, authorizeMiddleware, authorizeResourceMiddleware, definePolicy, withAuthorization } from './authorization/index.js';
@@ -259,6 +259,55 @@ declare function requestIdMiddleware(): hono.MiddlewareHandler<{
259
259
  */
260
260
  declare function requestLoggingMiddleware(): hono.MiddlewareHandler<any, string, {}, Response>;
261
261
 
262
+ /** Context key for the resolved request locale (read by Inertia for `<html lang>`). */
263
+ declare const LOCALE_CONTEXT_KEY = "locale";
264
+ type LocaleSource = 'query' | 'cookie' | 'header';
265
+ /** Context variables set by {@link detectLocaleMiddleware}, for typing Hono apps. */
266
+ type DetectLocaleVariables = {
267
+ locale: string;
268
+ t?: Translator['t'];
269
+ tc?: Translator['tc'];
270
+ };
271
+ interface DetectLocaleOptions {
272
+ /** Locales the app supports. Detection only ever resolves to one of these. */
273
+ supported: readonly string[];
274
+ /** Locale used when no source matches (defaults to the first supported locale). */
275
+ fallback?: string;
276
+ /** Detection order (defaults to `['query', 'cookie', 'header']`). */
277
+ sources?: readonly LocaleSource[];
278
+ /** Query parameter to read (defaults to `locale`). */
279
+ queryParam?: string;
280
+ /** Cookie to read (defaults to `locale`). */
281
+ cookieName?: string;
282
+ /**
283
+ * i18n manager used to bind request-scoped `t`/`tc` translator helpers.
284
+ * Defaults to the global manager when one was registered via `setI18n()`;
285
+ * pass a manager explicitly (e.g. `app.container.make('i18n')`), or `false`
286
+ * to skip the translator binding entirely.
287
+ */
288
+ i18n?: I18nManager | false;
289
+ }
290
+ /**
291
+ * Middleware that resolves the request locale from the query string, a cookie,
292
+ * or the `Accept-Language` header — in that order by default — restricted to
293
+ * the `supported` allowlist.
294
+ *
295
+ * The result is stored as the `locale` context variable, which Inertia
296
+ * responses pick up for the root `<html lang>` attribute. When an i18n manager
297
+ * is available (see the `i18n` option), request-scoped `t`/`tc` translator
298
+ * helpers are attached as well.
299
+ *
300
+ * @example
301
+ * ```ts
302
+ * import { detectLocaleMiddleware } from '@guren/core'
303
+ *
304
+ * app.use('*', detectLocaleMiddleware({ supported: ['en', 'ja'] }))
305
+ * ```
306
+ */
307
+ declare function detectLocaleMiddleware(options: DetectLocaleOptions): hono.MiddlewareHandler<{
308
+ Variables: DetectLocaleVariables;
309
+ }, string, {}, Response>;
310
+
262
311
  /**
263
312
  * Base HTTP exception class.
264
313
  *
@@ -557,6 +606,67 @@ declare class MethodNotAllowedException extends HttpException {
557
606
 
558
607
  declare function renderErrorPage(statusCode: number, message?: string): string;
559
608
 
609
+ /**
610
+ * Declarative definition of a plugin's lifecycle hooks.
611
+ *
612
+ * `register` and `boot` mirror the ServiceProvider hooks and receive the
613
+ * container plus the configuration captured by the plugin factory.
614
+ */
615
+ interface PluginDefinition<TConfig = void> {
616
+ /**
617
+ * Diagnostic name for the plugin (e.g. 'analytics').
618
+ * Used to derive the generated provider class name.
619
+ */
620
+ name: string;
621
+ /**
622
+ * Register services into the container.
623
+ * Called before any provider has booted.
624
+ */
625
+ register(container: Container, config: TConfig): void | Promise<void>;
626
+ /**
627
+ * Bootstrap after all providers have registered.
628
+ */
629
+ boot?(container: Container, config: TConfig): void | Promise<void>;
630
+ /**
631
+ * Defer instantiation until one of the `provides` services is resolved.
632
+ * Maps to `ServiceProvider.deferred`.
633
+ */
634
+ deferred?: boolean;
635
+ /**
636
+ * Services bound by this plugin (required when `deferred` is true).
637
+ * Maps to `ServiceProvider.provides`.
638
+ */
639
+ provides?: string[];
640
+ }
641
+ /**
642
+ * A factory returned by `definePlugin()`. Each call produces an independent
643
+ * ServiceProvider subclass with the given configuration captured in a closure.
644
+ */
645
+ type PluginFactory<TConfig = void> = (config: TConfig) => ServiceProviderConstructor;
646
+ /**
647
+ * Define a configurable Guren plugin without ServiceProvider boilerplate.
648
+ *
649
+ * Unlike storing configuration on a static class property, each factory call
650
+ * returns a fresh provider class, so the same plugin can be registered twice
651
+ * with different configurations.
652
+ *
653
+ * @example
654
+ * ```typescript
655
+ * export const analyticsPlugin = definePlugin<AnalyticsConfig>({
656
+ * name: 'analytics',
657
+ * register(container, config) {
658
+ * container.singleton('analytics', () => new AnalyticsClient(config))
659
+ * },
660
+ * })
661
+ *
662
+ * // In the application:
663
+ * createApp({
664
+ * providers: [analyticsPlugin({ apiKey: process.env.ANALYTICS_API_KEY! })],
665
+ * })
666
+ * ```
667
+ */
668
+ declare function definePlugin<TConfig = void>(definition: PluginDefinition<TConfig>): PluginFactory<TConfig>;
669
+
560
670
  type ViewRenderer = (template: string, props: Record<string, unknown>) => Response | Promise<Response>;
561
671
  /**
562
672
  * Registry for view renderers. Engines are typically registered via service
@@ -2474,4 +2584,4 @@ declare class DefaultHasher implements PasswordHasher {
2474
2584
  verify(hashed: string, plain: string): Promise<boolean>;
2475
2585
  }
2476
2586
 
2477
- export { AUTH_CONTEXT_KEY, AuthContext, AuthContext as AuthRuntimeContext, AuthServiceProvider, AuthenticationException, AuthorizationException, AuthorizationServiceProvider, AutoDiscovery, BaseFactory, type BaseResource, BaseSeeder, BroadcastManager, BroadcastServiceProvider, BufferedOutput, CSP_NONCE_KEY, CacheManager, CacheServiceProvider, Command, CommandInstance, Container, type CorsOptions, type CspDirectives, type CspOptions, type CursorPaginatedResponse, type CursorPaginationMeta, CursorPaginator, type CursorPaginatorOptions, DefaultHasher, type DiscoveryOptions, type DiscoveryResult, EncryptionServiceProvider, ErrorResponse, ErrorServiceProvider, EventManager, EventServiceProvider, BaseFactory as Factory, type FactoryClass, type Factory as FactoryInterface, FieldValidator, type FileLike, type FileValidationOptions, type ForceHttpsOptions, FormRequest, DefaultHasher as Hash, HealthServiceProvider, HttpException, I18nManager, I18nServiceProvider, type ImageValidationOptions, InertiaServiceProvider, type InferResourceData, Input, InputInterface, JsonResource, LogManager, LogServiceProvider, MailManager, MailServiceProvider, MethodNotAllowedException, NotFoundHttpException, NotificationManager, NotificationServiceProvider, OAuthServiceProvider, OptionDefinition, Output, OutputInterface, type PaginatedPageProps, type PaginatedResponse, type PaginatedResultLike, type PaginationLinks, type PaginationMeta, type PaginationPageLink, Paginator, type PaginatorOptions, ParsedSignature, QueueManager, QueueServiceProvider, type RedirectSafetyOptions, type RequireAuthOptions, Resource, type ResourceClass, ResourceCollection, type ResourceData, type RuleDefinition, Scheduler, SchedulingServiceProvider, BaseSeeder as Seeder, type SeederClass, type Seeder as SeederInterface, SeederRunner, type SeederRunnerOptions, ServiceBindings, ServiceProvider, StorageManager, StorageServiceProvider, type ValidationErrors, ValidationException, type ValidationResult, type ValidationRule, Validator, type ValidatorOptions, ViewEngine, after, afterOrEqual, alpha, alphaDash, alphaNum, array as arrayRule, attachAuthContext, before, beforeOrEqual, between, boolean as booleanRule, collect, confirmed, createCorsMiddleware, createCspMiddleware, createFacade, createFacades, createForceHttpsMiddleware, createRedirectSafetyMiddleware, createSeederRunner, createValidator, cursorPaginate, custom, dateFormat, date as dateRule, debugErrorMiddleware, decodeCursor, defineFactory, different, email as emailRule, encodeCursor, endsWith, exists, file as fileRule, getCspNonce, image as imageRule, inValues, integer, ip, ipv4, ipv6, isSafeRedirectUrl, json as jsonRule, max, maxFileSize, mimes, min, minFileSize, notIn, nullable, numeric, object as objectRule, paginate, parseSignature, quickValidate, quickValidateOrThrow, regex, renderDebugPage, renderErrorPage, requestIdMiddleware, requestLoggingMiddleware, requireAuthenticated, requireGuest, required, requiredIf, requiredUnless, requiredWith, requiredWithout, resetCalledSeeders, same, size, startsWith, string as stringRule, unique, url as urlRule, uuid as uuidRule };
2587
+ export { AUTH_CONTEXT_KEY, AuthContext, AuthContext as AuthRuntimeContext, AuthServiceProvider, AuthenticationException, AuthorizationException, AuthorizationServiceProvider, AutoDiscovery, BaseFactory, type BaseResource, BaseSeeder, BroadcastManager, BroadcastServiceProvider, BufferedOutput, CSP_NONCE_KEY, CacheManager, CacheServiceProvider, Command, CommandInstance, Container, type CorsOptions, type CspDirectives, type CspOptions, type CursorPaginatedResponse, type CursorPaginationMeta, CursorPaginator, type CursorPaginatorOptions, DefaultHasher, type DetectLocaleOptions, type DetectLocaleVariables, type DiscoveryOptions, type DiscoveryResult, EncryptionServiceProvider, ErrorResponse, ErrorServiceProvider, EventManager, EventServiceProvider, BaseFactory as Factory, type FactoryClass, type Factory as FactoryInterface, FieldValidator, type FileLike, type FileValidationOptions, type ForceHttpsOptions, FormRequest, DefaultHasher as Hash, HealthServiceProvider, HttpException, I18nManager, I18nServiceProvider, type ImageValidationOptions, InertiaServiceProvider, type InferResourceData, Input, InputInterface, JsonResource, LOCALE_CONTEXT_KEY, type LocaleSource, LogManager, LogServiceProvider, MailManager, MailServiceProvider, MethodNotAllowedException, NotFoundHttpException, NotificationManager, NotificationServiceProvider, OAuthServiceProvider, OptionDefinition, Output, OutputInterface, type PaginatedPageProps, type PaginatedResponse, type PaginatedResultLike, type PaginationLinks, type PaginationMeta, type PaginationPageLink, Paginator, type PaginatorOptions, ParsedSignature, type PluginDefinition, type PluginFactory, QueueManager, QueueServiceProvider, type RedirectSafetyOptions, type RequireAuthOptions, Resource, type ResourceClass, ResourceCollection, type ResourceData, type RuleDefinition, Scheduler, SchedulingServiceProvider, BaseSeeder as Seeder, type SeederClass, type Seeder as SeederInterface, SeederRunner, type SeederRunnerOptions, ServiceBindings, ServiceProvider, ServiceProviderConstructor, StorageManager, StorageServiceProvider, Translator, type ValidationErrors, ValidationException, type ValidationResult, type ValidationRule, Validator, type ValidatorOptions, ViewEngine, after, afterOrEqual, alpha, alphaDash, alphaNum, array as arrayRule, attachAuthContext, before, beforeOrEqual, between, boolean as booleanRule, collect, confirmed, createCorsMiddleware, createCspMiddleware, createFacade, createFacades, createForceHttpsMiddleware, createRedirectSafetyMiddleware, createSeederRunner, createValidator, cursorPaginate, custom, dateFormat, date as dateRule, debugErrorMiddleware, decodeCursor, defineFactory, definePlugin, detectLocaleMiddleware, different, email as emailRule, encodeCursor, endsWith, exists, file as fileRule, getCspNonce, image as imageRule, inValues, integer, ip, ipv4, ipv6, isSafeRedirectUrl, json as jsonRule, max, maxFileSize, mimes, min, minFileSize, notIn, nullable, numeric, object as objectRule, paginate, parseSignature, quickValidate, quickValidateOrThrow, regex, renderDebugPage, renderErrorPage, requestIdMiddleware, requestLoggingMiddleware, requireAuthenticated, requireGuest, required, requiredIf, requiredUnless, requiredWith, requiredWithout, resetCalledSeeders, same, size, startsWith, string as stringRule, unique, url as urlRule, uuid as uuidRule };
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  logDevServerBanner,
3
3
  parseImportMap,
4
4
  startViteDevServer
5
- } from "./chunk-QH4NUKSV.js";
5
+ } from "./chunk-CSP7W3WY.js";
6
6
  import {
7
7
  PendingSchedule,
8
8
  Schedule,
@@ -65,7 +65,7 @@ import {
65
65
  import {
66
66
  ProviderManager,
67
67
  ServiceProvider
68
- } from "./chunk-VT5KRDPH.js";
68
+ } from "./chunk-STZUODC3.js";
69
69
  import {
70
70
  DatabaseChannel,
71
71
  MailChannel,
@@ -143,7 +143,10 @@ import {
143
143
  getApiTokenOrFail,
144
144
  getSessionFromContext,
145
145
  getUserApiTokens,
146
+ hostMatchesAllowlist,
147
+ isAppRelativePath,
146
148
  isEmailVerified,
149
+ normalizeRedirectTarget,
147
150
  parseApiToken,
148
151
  parseOAuthRedirectUrl,
149
152
  parsePasswordResetUrl,
@@ -151,6 +154,7 @@ import {
151
154
  requireVerifiedEmail,
152
155
  revokeAllApiTokens,
153
156
  revokeApiToken,
157
+ sanitizeOAuthRedirect,
154
158
  tokenCan,
155
159
  tokenCanAll,
156
160
  tokenCanAny,
@@ -158,7 +162,7 @@ import {
158
162
  verifyEmailToken,
159
163
  verifyOAuthState,
160
164
  verifyPasswordResetToken
161
- } from "./chunk-2T6JN4VR.js";
165
+ } from "./chunk-VY2JPS6V.js";
162
166
  import {
163
167
  Gate,
164
168
  Policy,
@@ -832,6 +836,25 @@ function joinPaths(prefixStack, path) {
832
836
  return "/" + combined.replace(/\/+/gu, "/");
833
837
  }
834
838
 
839
+ // src/container/definePlugin.ts
840
+ function definePlugin(definition) {
841
+ const className = `${definition.name}PluginProvider`;
842
+ return (config) => {
843
+ class ConfiguredPluginProvider extends ServiceProvider {
844
+ static deferred = definition.deferred ?? false;
845
+ static provides = definition.provides ?? [];
846
+ register() {
847
+ return definition.register(this.container, config);
848
+ }
849
+ boot() {
850
+ return definition.boot?.(this.container, config);
851
+ }
852
+ }
853
+ Object.defineProperty(ConfiguredPluginProvider, "name", { value: className });
854
+ return ConfiguredPluginProvider;
855
+ };
856
+ }
857
+
835
858
  // src/http/middleware/csrf.ts
836
859
  var CSRF_TOKEN_KEY = "_csrf_token";
837
860
  var CSRF_HEADER_NAME = "X-CSRF-TOKEN";
@@ -1443,8 +1466,8 @@ function createRedirectSafetyMiddleware(options = {}) {
1443
1466
  };
1444
1467
  }
1445
1468
  function isSafeRedirectUrl(url2, requestUrl, allowedHosts = []) {
1446
- const normalized = url2.replace(/\\/g, "/");
1447
- if (normalized.startsWith("/") && !normalized.startsWith("//")) {
1469
+ const normalized = normalizeRedirectTarget(url2);
1470
+ if (isAppRelativePath(normalized)) {
1448
1471
  return true;
1449
1472
  }
1450
1473
  try {
@@ -1453,20 +1476,10 @@ function isSafeRedirectUrl(url2, requestUrl, allowedHosts = []) {
1453
1476
  if (target.host === current.host && target.protocol === current.protocol) {
1454
1477
  return true;
1455
1478
  }
1456
- for (const allowed of allowedHosts) {
1457
- if (allowed.startsWith("*.")) {
1458
- const suffix = allowed.slice(1).toLowerCase();
1459
- if (target.hostname.toLowerCase().endsWith(suffix) && target.hostname.length > suffix.length) {
1460
- return true;
1461
- }
1462
- } else if (target.host.toLowerCase() === allowed.toLowerCase()) {
1463
- return true;
1464
- }
1465
- }
1479
+ return hostMatchesAllowlist(target, allowedHosts);
1466
1480
  } catch {
1467
1481
  return false;
1468
1482
  }
1469
- return false;
1470
1483
  }
1471
1484
 
1472
1485
  // src/http/middleware/force-https.ts
@@ -1538,6 +1551,98 @@ function requestLoggingMiddleware() {
1538
1551
  });
1539
1552
  }
1540
1553
 
1554
+ // src/http/middleware/detect-locale.ts
1555
+ import { createMiddleware as createMiddleware3 } from "hono/factory";
1556
+ import { getCookie } from "hono/cookie";
1557
+ import { parseAccept } from "hono/utils/accept";
1558
+ var LOCALE_CONTEXT_KEY = "locale";
1559
+ var DEFAULT_SOURCES = ["query", "cookie", "header"];
1560
+ function detectLocaleMiddleware(options) {
1561
+ const supported = options.supported.map((locale) => locale.trim()).filter(Boolean);
1562
+ if (supported.length === 0) {
1563
+ throw new Error("detectLocaleMiddleware requires at least one supported locale.");
1564
+ }
1565
+ const fallback = options.fallback ?? supported[0];
1566
+ const sources = options.sources ?? DEFAULT_SOURCES;
1567
+ const queryParam = options.queryParam ?? "locale";
1568
+ const cookieName = options.cookieName ?? "locale";
1569
+ const byLowerCase = new Map(supported.map((locale) => [locale.toLowerCase(), locale]));
1570
+ const match = (candidate) => {
1571
+ const value = candidate?.trim().toLowerCase();
1572
+ if (!value) {
1573
+ return void 0;
1574
+ }
1575
+ return byLowerCase.get(value) ?? byLowerCase.get(value.split("-")[0]);
1576
+ };
1577
+ const matchHeader = (header) => {
1578
+ if (!header) {
1579
+ return void 0;
1580
+ }
1581
+ for (const accept of parseAccept(header)) {
1582
+ if (accept.type === "*" || accept.q <= 0) {
1583
+ continue;
1584
+ }
1585
+ const matched = match(accept.type);
1586
+ if (matched) {
1587
+ return matched;
1588
+ }
1589
+ }
1590
+ return void 0;
1591
+ };
1592
+ const translators = /* @__PURE__ */ new Map();
1593
+ const resolveTranslator = async (locale) => {
1594
+ if (options.i18n === false) {
1595
+ return void 0;
1596
+ }
1597
+ const cached = translators.get(locale);
1598
+ if (cached) {
1599
+ return cached;
1600
+ }
1601
+ const i18n = options.i18n ?? tryGlobalI18n();
1602
+ if (!i18n) {
1603
+ return void 0;
1604
+ }
1605
+ await i18n.loadLocale(locale).catch(() => {
1606
+ });
1607
+ const translator = i18n.forLocale(locale);
1608
+ const binding = {
1609
+ t: translator.t.bind(translator),
1610
+ tc: translator.tc.bind(translator)
1611
+ };
1612
+ translators.set(locale, binding);
1613
+ return binding;
1614
+ };
1615
+ return createMiddleware3(async (c, next) => {
1616
+ const readers = {
1617
+ query: () => match(c.req.query(queryParam)),
1618
+ cookie: () => match(getCookie(c, cookieName)),
1619
+ header: () => matchHeader(c.req.header("Accept-Language"))
1620
+ };
1621
+ let resolved = fallback;
1622
+ for (const source of sources) {
1623
+ const matched = readers[source]();
1624
+ if (matched) {
1625
+ resolved = matched;
1626
+ break;
1627
+ }
1628
+ }
1629
+ c.set(LOCALE_CONTEXT_KEY, resolved);
1630
+ const binding = await resolveTranslator(resolved);
1631
+ if (binding) {
1632
+ c.set("t", binding.t);
1633
+ c.set("tc", binding.tc);
1634
+ }
1635
+ await next();
1636
+ });
1637
+ }
1638
+ function tryGlobalI18n() {
1639
+ try {
1640
+ return getI18n();
1641
+ } catch {
1642
+ return void 0;
1643
+ }
1644
+ }
1645
+
1541
1646
  // src/http/middleware/index.ts
1542
1647
  function defineMiddleware(handler) {
1543
1648
  return handler;
@@ -1803,8 +1908,9 @@ async function renderDocument(page, options) {
1803
1908
  const appMarkup = ssrResult?.body ?? `<script data-page="app" type="application/json">${serializedPage}</script><div id="app"></div>`;
1804
1909
  const bodyClass = resolveBodyClass(page.component);
1805
1910
  const bodyAttributes = bodyClass ? ` class="${escapeAttribute(bodyClass)}"` : "";
1911
+ const lang = escapeAttribute(options.lang ?? "en");
1806
1912
  return `<!DOCTYPE html>
1807
- <html lang="en">
1913
+ <html lang="${lang}">
1808
1914
  <head>
1809
1915
  ${headSegments.join("\n ")}
1810
1916
  </head>
@@ -2292,7 +2398,7 @@ var Application = class {
2292
2398
  return;
2293
2399
  }
2294
2400
  try {
2295
- const { McpServiceProvider } = await import("./McpServiceProvider-JW6PDVMD.js");
2401
+ const { McpServiceProvider } = await import("./McpServiceProvider-RNTDHZAZ.js");
2296
2402
  const provider = new McpServiceProvider(this.container);
2297
2403
  provider.register();
2298
2404
  await provider.boot();
@@ -2603,6 +2709,7 @@ var Controller = class {
2603
2709
  const propsWithShared = { ...sharedProps, ...props };
2604
2710
  const response = await inertia(component, propsWithShared, {
2605
2711
  ...rest,
2712
+ lang: rest.lang ?? this.#defaultInertiaLang(),
2606
2713
  url: url2,
2607
2714
  request: ctx.req.raw
2608
2715
  });
@@ -2612,6 +2719,31 @@ var Controller = class {
2612
2719
  };
2613
2720
  return response;
2614
2721
  }
2722
+ /**
2723
+ * Default `<html lang>` for Inertia responses when `options.lang` is not
2724
+ * provided: the request-scoped `locale` context variable (set by locale
2725
+ * middleware) wins over the app-wide i18n locale (the router-injected
2726
+ * container binding, then the `setI18n()` global).
2727
+ */
2728
+ #defaultInertiaLang() {
2729
+ const vars = this.ctx.var;
2730
+ const requestLocale = vars?.[LOCALE_CONTEXT_KEY];
2731
+ if (typeof requestLocale === "string" && requestLocale.length > 0) {
2732
+ return requestLocale;
2733
+ }
2734
+ let i18n;
2735
+ if (this._container?.has?.("i18n")) {
2736
+ i18n = this._container.make("i18n");
2737
+ } else {
2738
+ try {
2739
+ i18n = getI18n();
2740
+ } catch {
2741
+ i18n = void 0;
2742
+ }
2743
+ }
2744
+ const locale = i18n?.getLocale?.();
2745
+ return typeof locale === "string" && locale.length > 0 ? locale : void 0;
2746
+ }
2615
2747
  json(data, init = {}) {
2616
2748
  return new Response(JSON.stringify(data), {
2617
2749
  ...init,
@@ -6324,6 +6456,7 @@ export {
6324
6456
  JobProcessed,
6325
6457
  JsonLoader,
6326
6458
  JsonResource,
6459
+ LOCALE_CONTEXT_KEY,
6327
6460
  LOG_LEVEL_PRIORITY,
6328
6461
  Listener,
6329
6462
  LocalDriver as LocalStorageDriver,
@@ -6492,9 +6625,11 @@ export {
6492
6625
  defineFactory,
6493
6626
  defineGate,
6494
6627
  defineMiddleware,
6628
+ definePlugin,
6495
6629
  definePolicy,
6496
6630
  deriveAppKey,
6497
6631
  deriveAppKeyring,
6632
+ detectLocaleMiddleware,
6498
6633
  different,
6499
6634
  email as emailRule,
6500
6635
  encodeCursor,
@@ -6608,6 +6743,7 @@ export {
6608
6743
  revokeApiToken,
6609
6744
  same,
6610
6745
  sample,
6746
+ sanitizeOAuthRedirect,
6611
6747
  secureCompare,
6612
6748
  selectPluralForm,
6613
6749
  setBroadcastManager,
@@ -1,18 +1,18 @@
1
1
  import * as hono_aws_lambda from 'hono/aws-lambda';
2
2
  export { APIGatewayProxyResult, LambdaEvent } from 'hono/aws-lambda';
3
- import { A as Application } from '../Application-BnsyCKXY.js';
3
+ import { A as Application } from '../Application-DImeY-VW.js';
4
4
  import { S as Scheduler } from '../Scheduler-BstvSca7.js';
5
- import { C as ConsoleKernel } from '../ConsoleKernel-BDtBETjm.js';
5
+ import { C as ConsoleKernel } from '../ConsoleKernel-COKndy_V.js';
6
6
  import 'hono';
7
- import '../api-token-BSSCLlFW.js';
8
- import '../AuthManager-SfhCNkAU.js';
7
+ import '../api-token-CB12xnVg.js';
8
+ import '../AuthManager-BU8ZvenF.js';
9
9
  import '@guren/orm';
10
10
  import 'vite';
11
11
  import '../EventManager-CmIoLt7r.js';
12
12
  import '../CacheManager-BkvHEOZX.js';
13
13
  import '../MailManager-DpMvYiP9.js';
14
14
  import '../LogManager-7mxnkaPM.js';
15
- import '../I18nManager-BiSoczfV.js';
15
+ import '../I18nManager-CmUqj6bU.js';
16
16
  import '../BroadcastManager-CGWl9rUO.js';
17
17
  import '../index-9_Jzj5jo.js';
18
18
  import '../app-key-CsBfRC_Q.js';
@@ -1,15 +1,15 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import { S as ServiceProvider } from '../Application-BnsyCKXY.js';
2
+ import { S as ServiceProvider } from '../Application-DImeY-VW.js';
3
3
  import 'hono';
4
- import '../api-token-BSSCLlFW.js';
5
- import '../AuthManager-SfhCNkAU.js';
4
+ import '../api-token-CB12xnVg.js';
5
+ import '../AuthManager-BU8ZvenF.js';
6
6
  import '@guren/orm';
7
7
  import 'vite';
8
8
  import '../EventManager-CmIoLt7r.js';
9
9
  import '../CacheManager-BkvHEOZX.js';
10
10
  import '../MailManager-DpMvYiP9.js';
11
11
  import '../LogManager-7mxnkaPM.js';
12
- import '../I18nManager-BiSoczfV.js';
12
+ import '../I18nManager-CmUqj6bU.js';
13
13
  import '../BroadcastManager-CGWl9rUO.js';
14
14
  import '../index-9_Jzj5jo.js';
15
15
  import '../app-key-CsBfRC_Q.js';
package/dist/mcp/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  McpServiceProvider,
3
3
  createMcpServer
4
- } from "../chunk-WVY45EIW.js";
5
- import "../chunk-VT5KRDPH.js";
4
+ } from "../chunk-J753UWAY.js";
5
+ import "../chunk-STZUODC3.js";
6
6
  export {
7
7
  McpServiceProvider,
8
8
  createMcpServer
@@ -1,15 +1,15 @@
1
- import { a0 as NotificationChannel, Z as Notifiable, _ as Notification, t as DatabaseNotification, r as DatabaseChannelOptions, au as SlackMessage, ak as SentNotification } from '../Application-BnsyCKXY.js';
2
- export { $ as NotificationAttachment, a1 as NotificationChannelFactory, a2 as NotificationClass, a3 as NotificationMailMessage, N as NotificationManager, a4 as NotificationManagerOptions, as as SlackAttachment, at as SlackBlock, aI as createNotificationManager, aS as getNotificationManager, b1 as setNotificationManager } from '../Application-BnsyCKXY.js';
1
+ import { a1 as NotificationChannel, _ as Notifiable, $ as Notification, u as DatabaseNotification, t as DatabaseChannelOptions, au as SlackMessage, al as SentNotification } from '../Application-DImeY-VW.js';
2
+ export { a0 as NotificationAttachment, a2 as NotificationChannelFactory, a3 as NotificationClass, a4 as NotificationMailMessage, N as NotificationManager, a5 as NotificationManagerOptions, as as SlackAttachment, at as SlackBlock, aI as createNotificationManager, aS as getNotificationManager, b1 as setNotificationManager } from '../Application-DImeY-VW.js';
3
3
  import { M as MailManager } from '../MailManager-DpMvYiP9.js';
4
4
  import 'hono';
5
- import '../api-token-BSSCLlFW.js';
6
- import '../AuthManager-SfhCNkAU.js';
5
+ import '../api-token-CB12xnVg.js';
6
+ import '../AuthManager-BU8ZvenF.js';
7
7
  import '@guren/orm';
8
8
  import 'vite';
9
9
  import '../EventManager-CmIoLt7r.js';
10
10
  import '../CacheManager-BkvHEOZX.js';
11
11
  import '../LogManager-7mxnkaPM.js';
12
- import '../I18nManager-BiSoczfV.js';
12
+ import '../I18nManager-CmUqj6bU.js';
13
13
  import '../BroadcastManager-CGWl9rUO.js';
14
14
  import '../index-9_Jzj5jo.js';
15
15
  import '../app-key-CsBfRC_Q.js';
@@ -1,16 +1,16 @@
1
- import { a7 as QueueDriver, a9 as QueuedJob, F as FailedJob, ay as WorkerOptions } from '../Application-BnsyCKXY.js';
2
- export { U as Job, V as JobClass, W as JobFailureHandler, X as JobHandler, Y as JobOptions, a6 as QueueConfig, a8 as QueueDriverFactory, Q as QueueManager, aC as clearJobRegistry, aJ as createQueueManager, aR as getJob, aT as getQueueDriver, aU as getRegisteredJobs, aY as registerJob, b2 as setQueueDriver } from '../Application-BnsyCKXY.js';
1
+ import { a8 as QueueDriver, aa as QueuedJob, F as FailedJob, ay as WorkerOptions } from '../Application-DImeY-VW.js';
2
+ export { V as Job, W as JobClass, X as JobFailureHandler, Y as JobHandler, Z as JobOptions, a7 as QueueConfig, a9 as QueueDriverFactory, Q as QueueManager, aC as clearJobRegistry, aJ as createQueueManager, aR as getJob, aT as getQueueDriver, aU as getRegisteredJobs, aY as registerJob, b2 as setQueueDriver } from '../Application-DImeY-VW.js';
3
3
  import { Redis } from 'ioredis';
4
4
  import 'hono';
5
- import '../api-token-BSSCLlFW.js';
6
- import '../AuthManager-SfhCNkAU.js';
5
+ import '../api-token-CB12xnVg.js';
6
+ import '../AuthManager-BU8ZvenF.js';
7
7
  import '@guren/orm';
8
8
  import 'vite';
9
9
  import '../EventManager-CmIoLt7r.js';
10
10
  import '../CacheManager-BkvHEOZX.js';
11
11
  import '../MailManager-DpMvYiP9.js';
12
12
  import '../LogManager-7mxnkaPM.js';
13
- import '../I18nManager-BiSoczfV.js';
13
+ import '../I18nManager-CmUqj6bU.js';
14
14
  import '../BroadcastManager-CGWl9rUO.js';
15
15
  import '../index-9_Jzj5jo.js';
16
16
  import '../app-key-CsBfRC_Q.js';
@@ -2,8 +2,8 @@ import { R as RateLimitStore, a as RateLimitEntry } from '../client-CKXJLsTe.js'
2
2
  export { b as RedisClientOptions, c as createRedisClient } from '../client-CKXJLsTe.js';
3
3
  import { Redis } from 'ioredis';
4
4
  export { default as Redis, RedisOptions } from 'ioredis';
5
- import { S as SessionStore, h as SessionData, i as ApiTokenStore, j as ApiToken, O as OAuthStateStore, k as OAuthStatePayload } from '../api-token-BSSCLlFW.js';
6
- import { P as PasswordResetTokenStore, E as EmailVerificationTokenStore, a as EmailVerificationToken } from '../email-verification-CAeArjui.js';
5
+ import { S as SessionStore, h as SessionData, i as ApiTokenStore, j as ApiToken, O as OAuthStateStore, k as OAuthStatePayload } from '../api-token-CB12xnVg.js';
6
+ import { P as PasswordResetTokenStore, E as EmailVerificationTokenStore, a as EmailVerificationToken } from '../email-verification-D9u7krkn.js';
7
7
  import 'hono';
8
8
 
9
9
  /**
@@ -1,15 +1,15 @@
1
- import { A as Application } from '../Application-BnsyCKXY.js';
2
- export { D as DevBannerOptions, G as GUREN_ASCII_ART, a as StartViteDevServerOptions, b as StartedViteDevServer, l as logDevServerBanner, s as startViteDevServer } from '../Application-BnsyCKXY.js';
1
+ import { A as Application } from '../Application-DImeY-VW.js';
2
+ export { D as DevBannerOptions, G as GUREN_ASCII_ART, a as StartViteDevServerOptions, b as StartedViteDevServer, l as logDevServerBanner, s as startViteDevServer } from '../Application-DImeY-VW.js';
3
3
  import 'hono';
4
- import '../api-token-BSSCLlFW.js';
5
- import '../AuthManager-SfhCNkAU.js';
4
+ import '../api-token-CB12xnVg.js';
5
+ import '../AuthManager-BU8ZvenF.js';
6
6
  import '@guren/orm';
7
7
  import 'vite';
8
8
  import '../EventManager-CmIoLt7r.js';
9
9
  import '../CacheManager-BkvHEOZX.js';
10
10
  import '../MailManager-DpMvYiP9.js';
11
11
  import '../LogManager-7mxnkaPM.js';
12
- import '../I18nManager-BiSoczfV.js';
12
+ import '../I18nManager-CmUqj6bU.js';
13
13
  import '../BroadcastManager-CGWl9rUO.js';
14
14
  import '../index-9_Jzj5jo.js';
15
15
  import '../app-key-CsBfRC_Q.js';
@@ -3,7 +3,7 @@ import {
3
3
  logDevServerBanner,
4
4
  parseImportMap,
5
5
  startViteDevServer
6
- } from "../chunk-QH4NUKSV.js";
6
+ } from "../chunk-CSP7W3WY.js";
7
7
  import {
8
8
  hash
9
9
  } from "../chunk-QQKTH5KX.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guren/server",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {