release: WRNexusJS 0.5.10

This commit is contained in:
2026-07-30 13:36:29 +05:30
parent 8fc6f15402
commit d1b0c55b53
159 changed files with 7509 additions and 604 deletions
+21 -1
View File
@@ -9,7 +9,11 @@ import {
getAuthUser,
} from "../middleware.ts";
import { resolveAuthSchemas, type AuthSchemaOverrides, type AuthSchemaSet } from "../validation.ts";
import type { AuthSignedInHandler, AuthSignedOutHandler } from "../types.ts";
import type {
AuthSessionVerificationHandler,
AuthSignedInHandler,
AuthSignedOutHandler,
} from "../types.ts";
function text(value: unknown): string {
return typeof value === "string" ? value : value == null ? "" : String(value);
@@ -53,6 +57,7 @@ export interface AuthHttpOptions {
onSignedIn?: AuthSignedInHandler;
/** @deprecated Prefer createAuthEngine({ onSignedOut }). */
onSignedOut?: AuthSignedOutHandler;
onSessionVerification?: AuthSessionVerificationHandler;
}
export function createAuthHttpHandlers(options: AuthHttpOptions) {
@@ -83,6 +88,21 @@ export function createAuthHttpHandlers(options: AuthHttpOptions) {
}
return {
async verifySession(ctx: Context): Promise<Response> {
const user = getAuthUser(ctx);
if (options.onSessionVerification) {
return options.onSessionVerification(ctx, user);
}
if (!user) return json({ ok: false, error: "Unauthorized" }, 401);
if ((ctx.req.headers.get("accept") ?? "").includes("application/json")) {
return json({ ok: true, user });
}
return new Response(null, {
status: 204,
headers: { "cache-control": "no-store" },
});
},
async register(ctx: Context): Promise<Response> {
const validation = await parseBody(schemas.register, ctx.req);
if (!validation.ok) return validation.response;
+9 -2
View File
@@ -12,9 +12,16 @@ function wantsJson(ctx: Context): boolean {
return accept.includes("application/json") && !accept.includes("text/html");
}
export function authSession(engine: AuthEngine): Middleware {
export interface AuthSessionOptions {
/** Optional shared SSO cookie containing an AuthEngine session id. */
cookieName?: string;
}
export function authSession(engine: AuthEngine, options: AuthSessionOptions = {}): Middleware {
return async (ctx, next) => {
const sessionId = ctx.session.get<string>(AUTH_SESSION_KEY);
const sessionId =
(options.cookieName ? ctx.cookies.get(options.cookieName) : undefined) ??
ctx.session.get<string>(AUTH_SESSION_KEY);
if (!sessionId) {
ctx.user = null;
ctx.locals.authUser = null;
+27 -5
View File
@@ -4,7 +4,11 @@ import { fileURLToPath } from "node:url";
import { definePlugin, type PluginContext } from "@wrnexus/plugin";
import type { AuthEngine } from "./engine.ts";
import type { AuthPasskeyHttpOptions } from "./http/index.ts";
import type { AuthSignedInHandler, AuthSignedOutHandler } from "./types.ts";
import type {
AuthSessionVerificationHandler,
AuthSignedInHandler,
AuthSignedOutHandler,
} from "./types.ts";
import { AUTH_ROUTE_DEFINITIONS, type AuthRouteGroup } from "./routes/definitions.ts";
import {
clearDefaultAuthEngine,
@@ -52,6 +56,10 @@ export interface AuthConfig {
onSignedIn?: AuthSignedInHandler;
/** @deprecated Prefer createAuthEngine({ onSignedOut }). */
onSignedOut?: AuthSignedOutHandler;
/** Shared SSO cookie whose value is an AuthEngine session id. */
sessionCookieName?: string;
/** Customize the package forward-auth verification response. */
onSessionVerification?: AuthSessionVerificationHandler;
}
/** Explicit plugin options remain supported for compatibility. Prefer config.auth. */
@@ -88,14 +96,22 @@ interface ResolvedAuthConfig {
passkey?: AuthPasskeyHttpOptions;
onSignedIn?: AuthSignedInHandler;
onSignedOut?: AuthSignedOutHandler;
sessionCookieName?: string;
onSessionVerification?: AuthSessionVerificationHandler;
}
const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
const moduleRoot = dirname(fileURLToPath(import.meta.url));
const packageRoot = dirname(moduleRoot);
const compiledPackage = moduleRoot === join(packageRoot, "dist");
const clientRuntime = join(packageRoot, "assets", "client", "auth.js");
const migrationsFile = join(packageRoot, "migrations", "001_auth.sql");
const otpPurposeMigrationFile = join(packageRoot, "migrations", "002_auth_otp_purpose.sql");
const apiRoutesDir = join(packageRoot, "src", "routes", "api");
const middlewareFile = join(packageRoot, "src", "routes", "middleware.ts");
const apiRoutesDir = compiledPackage
? join(moduleRoot, "routes", "api")
: join(packageRoot, "src", "routes", "api");
const middlewareFile = compiledPackage
? join(moduleRoot, "routes", "middleware.js")
: join(packageRoot, "src", "routes", "middleware.ts");
const resolvedConfigKey = "@wrnexus/auth:resolved-config";
export function authComponentsDir(): string {
@@ -104,7 +120,7 @@ export function authComponentsDir(): string {
function authApiRouteEntry(path: string): string {
const routeName = path.replace(/^\/api\/auth\/?/, "").replace(/\//g, "-") || "index";
return join(apiRoutesDir, routeName + ".ts");
return join(apiRoutesDir, routeName + (compiledPackage ? ".js" : ".ts"));
}
function resolveConfig(
@@ -142,6 +158,8 @@ function resolveConfig(
passkey: raw.passkey,
onSignedIn: raw.onSignedIn ?? raw.engine?.onSignedIn,
onSignedOut: raw.onSignedOut ?? raw.engine?.onSignedOut,
sessionCookieName: raw.sessionCookieName,
onSessionVerification: raw.onSessionVerification,
};
}
@@ -377,6 +395,10 @@ export function authPlugin(options: AuthPluginOptions = {}) {
onSignedIn: value.onSignedIn,
onSignedOut: value.onSignedOut,
sessionCookieName: value.sessionCookieName,
onSessionVerification: value.onSessionVerification,
});
context.metadata.set("@wrnexus/auth:component-dir", value.componentDir);
+1
View File
@@ -62,6 +62,7 @@ function handlersFor(ctx: Context): AuthHttpHandlers | undefined {
passkey: routeOptions.passkey,
onSignedIn: routeOptions.onSignedIn,
onSignedOut: routeOptions.onSignedOut,
onSessionVerification: routeOptions.onSessionVerification,
});
}
@@ -0,0 +1,6 @@
import type { Context } from "@wrnexus/core";
import { invokeAuthHandler } from "../api.ts";
export function GET(ctx: Context): Promise<Response> {
return invokeAuthHandler("verifySession", ctx);
}
+6
View File
@@ -23,6 +23,12 @@ export interface AuthRouteDefinition {
/** Single source of truth for package-contributed auth endpoints. */
export const AUTH_ROUTE_DEFINITIONS = [
{
path: "/api/auth/session/verify",
group: "sessions",
handler: "verifySession",
methods: ["GET"],
},
{ path: "/api/auth/register", group: "registration", handler: "register", methods: ["POST"] },
{ path: "/api/auth/login", group: "login", handler: "login", methods: ["POST"] },
{ path: "/api/auth/logout", group: "login", handler: "logout", methods: ["POST"] },
+8 -2
View File
@@ -1,11 +1,17 @@
import type { Middleware } from "@wrnexus/core";
import { authSession } from "../middleware.ts";
import { getDefaultAuthEngine, hasDefaultAuthEngine } from "../runtime.ts";
import {
getDefaultAuthEngine,
getDefaultAuthRouteOptions,
hasDefaultAuthEngine,
} from "../runtime.ts";
/** Package middleware: hydrates auth state when a default engine is configured. */
const middleware: Middleware = async (ctx, next) => {
if (!hasDefaultAuthEngine()) return next();
return authSession(getDefaultAuthEngine())(ctx, next);
return authSession(getDefaultAuthEngine(), {
cookieName: getDefaultAuthRouteOptions().sessionCookieName,
})(ctx, next);
};
export default middleware;
+3
View File
@@ -1,6 +1,7 @@
import type { Context } from "@wrnexus/core";
import type { AuthEngine } from "./engine.ts";
import type { AuthPasskeyHttpOptions } from "./http/index.ts";
import type { AuthSessionVerificationHandler } from "./types.ts";
import { resolveAuthSchemas, type AuthSchemaOverrides, type AuthSchemaSet } from "./validation.ts";
export interface DefaultAuthRouteOptions {
@@ -11,6 +12,8 @@ export interface DefaultAuthRouteOptions {
onSignedIn?: (ctx: Context, returnTo?: string) => Response | Promise<Response>;
onSignedOut?: (ctx: Context) => Response | Promise<Response>;
sessionCookieName?: string;
onSessionVerification?: AuthSessionVerificationHandler;
}
interface AuthRuntimeState {
+6
View File
@@ -241,6 +241,12 @@ export type AuthSignedInHandler = (ctx: Context, returnTo?: string) => Response
/** HTTP response hook invoked after the auth engine clears a signed-in session. */
export type AuthSignedOutHandler = (ctx: Context) => Response | Promise<Response>;
/** Forward-auth response hook. A null user means the request is unauthenticated. */
export type AuthSessionVerificationHandler = (
ctx: Context,
user: AuthPublicUser | null,
) => Response | Promise<Response>;
export interface AuthSuccessfulSignUpAction {
/**
* Run the normal login policy with the newly registered credentials and