release: WRNexusJS 0.8.0
Quality / quality (ubuntu-latest) (push) Failing after 21s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
2026-08-02 23:18:51 +05:30
parent 87507edf59
commit 586a6db8ff
625 changed files with 243608 additions and 11210 deletions
+6 -1
View File
@@ -8,6 +8,11 @@ export const GET = async () => {
};
export const POST = async (ctx: Context) => {
const body = await ctx.req.json();
const body = await ctx.req.json().catch(() => undefined);
if (body === undefined)
return Response.json(
{ error: "Request body must be valid JSON." },
{ status: 400, headers: { "cache-control": "no-store" } },
);
return Response.json({ received: body });
};
@@ -0,0 +1,14 @@
import { createGraphqlHandler } from "@wrnexus/graphql";
const graphql = createGraphqlHandler({
maxDepth: 8,
maxAliases: 20,
allowIntrospection: false,
async execute(request) {
return { data: { example: request.operationName ?? "anonymous" } };
},
});
export function POST(ctx: { req: Request }): Promise<Response> {
return graphql(ctx.req);
}
+12
View File
@@ -0,0 +1,12 @@
import { defineEndpoint } from "@wrnexus/core";
import { CreateUserSchema } from "../schemas/create-user.ts";
/** Schema-derived request and response types are emitted into wrnexus.generated.d.ts. */
export const POST = defineEndpoint({
input: CreateUserSchema,
output: CreateUserSchema,
description: "Validate and echo a typed user payload.",
handler(input) {
return input;
},
});
@@ -0,0 +1,11 @@
export const webhook = {
event: "payment.completed",
summary: "Payment completed",
description: "Sent after a payment reaches its settled state.",
payloadSchema: "#/components/schemas/PaymentCompleted",
signatureHeader: "x-payment-signature",
};
export async function POST(): Promise<Response> {
return Response.json({ accepted: true });
}
+38
View File
@@ -73,4 +73,42 @@ describe("full app", () => {
expect(typeof body.message).toBe("string");
expect(body.lang).toBeTruthy();
});
test("language-server playground renders its real interactive output", async () => {
const res = await app.fetch("/language-tools");
expect(res.status).toBe(200);
const html = await res.text();
expect(html).toContain("Language server playground");
expect(html).toContain("computed double:");
expect(html).toContain('data-text="doubled">0</span>');
expect(html).toContain('aria-live="polite"');
});
test("login renders real accessible fields without an accidental Card component", async () => {
const res = await app.fetch("/login?next=/dashboard");
expect(res.status).toBe(200);
const html = await res.text();
expect(html).toContain('name="email"');
expect(html).toContain('name="password"');
expect(html).toContain('autocomplete="current-password"');
expect(html).not.toContain("Card title");
});
test("platform showcase uses the public layout and styled reactive primitives", async () => {
const res = await app.fetch("/platform-showcase");
expect(res.status).toBe(200);
const html = await res.text();
expect(html).toContain("WrNexus");
expect(html).toContain('data-wrn-dynamic-component="Admin"');
expect(html).toContain('data-wrn-portal="#showcase-modal"');
expect(html).toContain("platform-showcase");
});
test("async data page renders its server loader without a cache failure", async () => {
const res = await app.fetch("/async-data");
expect(res.status).toBe(200);
const html = await res.text();
expect(html).toContain("Rendered by the server loader");
expect(html).toContain("Loading the browser profile");
});
});
+20 -2
View File
@@ -3,9 +3,27 @@
component AuthLayout {
view {
<div class="auth-wrap">
<div data-component="card" class="auth-card">
<section class="auth-card" aria-label="Authentication">
<slot></slot>
</div>
</section>
</div>
}
style {
.auth-wrap {
display: grid;
min-height: 100vh;
place-items: center;
padding: 1.5rem;
}
.auth-card {
width: min(100%, 28rem);
padding: 2rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-lg);
background: var(--wire-color-surface);
box-shadow: 0 1.25rem 3rem rgb(0 0 0 / 0.18);
}
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ component DashboardLayout {
<a href="/ui">Components</a>
<a href="/">Home</a>
</nav>
<div data-component="theme-toggle" label="Toggle theme"></div>
<button type="button" data-wire-theme-toggle aria-label="Toggle color theme">Theme</button>
</aside>
<main class="dash-main">
+1 -1
View File
@@ -11,7 +11,7 @@ component PublicLayout {
<a href="/chat">{t:nav.chat}</a>
<a href="/dashboard">{t:nav.dashboard}</a>
<a href="/about">{t:nav.about}</a>
<div data-component="theme-toggle" label="Theme"></div>
<button type="button" data-wire-theme-toggle aria-label="Toggle color theme">Theme</button>
<button class="wire-btn wire-btn--ghost wire-btn--sm" data-wire-lang-set="en">EN</button>
<button class="wire-btn wire-btn--ghost wire-btn--sm" data-wire-lang-set="es">ES</button>
</nav>
@@ -0,0 +1,29 @@
page AsyncData {
layout = "public"
load server summary {
return { message: "Rendered by the server loader" }
}
load client profile {
return { name: "Ada", role: "Administrator" }
}
view {
<section class="stack">
<h1>Async data boundaries</h1>
<Async source="summary">
<Loading><p>Preparing the server summary…</p></Loading>
<Success data="summary"><p>{summary.message}</p></Success>
<Error error="error"><p>{error.message}</p></Error>
</Async>
<Async source="profile" retries="2">
<Loading><p>Loading the browser profile…</p></Loading>
<Success data="profile"><p>Welcome {profile.name} — {profile.role}</p></Success>
<Error error="error"><p>Profile failed: {error.message}</p></Error>
</Async>
</section>
}
}
@@ -0,0 +1,15 @@
page ClientOnly {
layout = "public"
render = "client"
hydrate = "load"
state count: number = 0
view {
<section class="wire-card stack">
<p class="wire-badge">Mounted in the browser</p>
<h1>Client-only page</h1>
<button type="button" class="wire-btn" @click="count++">Count: {count}</button>
</section>
}
}
+2 -1
View File
@@ -27,10 +27,11 @@ page Home {
restyles instantly when the theme changes. -->
<div class="themed-box">
This box uses theme tokens. Current theme controls its colors.
<button data-wire-theme-toggle class="theme-toggle">Toggle theme</button>
<button type="button" data-wire-theme-toggle class="theme-toggle">Toggle theme</button>
</div>
<p><a class="text-red-600 hover:underline" href="/about">About</a></p>
<p><a class="hover:underline" href="/language-tools">Language server playground</a></p>
}
style {
@@ -0,0 +1,104 @@
// Real page for checking compiler, formatter, LSP navigation, rename, and reactivity.
page LanguageTools {
layout = "public"
state count: number = 0
state message: string = "The shared language server is active."
computed doubled: number = count * 2
seo {
title = "WRN language tools"
description = "Interactive WRN page used to verify editor and compiler features."
canonical = "/language-tools"
}
functions {
function increment() {
count++
message = `Renaming count updates all ${count} references safely.`
}
function reset() {
count = 0
message = "State reset. Try hover, definition, references, and rename in your editor."
}
}
view {
<main class="language-tools">
<header>
<span class="eyebrow">Editor-neutral WRN tooling</span>
<h1>Language server playground</h1>
<p>{message}</p>
</header>
<section aria-labelledby="counter-heading" class="demo-card">
<h2 id="counter-heading">Reactive Data symbol test</h2>
<p aria-live="polite">Count: {count}; computed double: {doubled}</p>
<div class="actions">
<button type="button" @click="increment()">Increment</button>
<button type="button" class="secondary" @click="reset()">Reset</button>
</div>
</section>
<section aria-labelledby="features-heading" class="demo-card">
<h2 id="features-heading">Features exercised by this file</h2>
<ul>
<li>Compiler and accessibility diagnostics</li>
<li>Canonical formatting and completion</li>
<li>Hover, symbols, definition, references, and rename</li>
<li>Typed state, computed values, and browser events</li>
</ul>
</section>
</main>
}
style {
.language-tools {
display: grid;
max-width: 58rem;
gap: 1.25rem;
margin: 3rem auto;
}
.language-tools header,
.demo-card {
display: grid;
gap: 0.75rem;
}
.eyebrow {
color: var(--wire-color-primary);
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.demo-card {
padding: 1.25rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-lg);
background: var(--wire-color-surface);
}
.actions {
display: flex;
gap: 0.75rem;
}
.actions button {
padding: 0.65rem 1rem;
border: 1px solid var(--wire-color-primary);
border-radius: var(--wire-radius-md);
background: var(--wire-color-primary);
color: var(--wire-color-primary-contrast);
cursor: pointer;
}
.actions .secondary {
background: transparent;
color: var(--wire-color-primary);
}
}
}
+32 -7
View File
@@ -11,24 +11,49 @@ page Login {
view {
<form data-schema="login" method="post" action="/api/login" data-redirect="/dashboard">
<div data-component="stack" gap="4">
<div class="auth-stack">
<h1>Sign in</h1>
<div class="wire-alert wire-alert--success" data-success="Signed in! The form posted JSON and was validated server-side." hidden></div>
<div>
<div data-component="input" type="email" name="email" placeholder="you@example.com"></div>
<label class="auth-field">
<span>Email address</span>
<input type="email" name="email" autocomplete="email" placeholder="you@example.com" required />
<span class="wire-field-error" data-error="email"></span>
</div>
</label>
<div>
<div data-component="input" type="password" name="password" placeholder="Password"></div>
<label class="auth-field">
<span>Password</span>
<input type="password" name="password" autocomplete="current-password" placeholder="Password" required />
<span class="wire-field-error" data-error="password"></span>
</div>
</label>
<button type="submit" class="wire-btn wire-btn--primary wire-btn--lg">Sign in</button>
<p><a href="/">Back home</a></p>
</div>
</form>
}
style {
form,
.auth-stack,
.auth-field {
display: grid;
gap: 0.5rem;
}
form,
.auth-stack {
gap: 1.25rem;
}
.auth-field input {
min-height: 2.75rem;
padding: 0.7rem 0.8rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-md);
background: var(--wire-color-surface);
color: inherit;
}
}
}
@@ -0,0 +1,27 @@
page PartialStatic {
layout = "public"
render = "partial-static"
seo {
title = "Partial-static streaming"
description = "A build-time WRNexus shell with request-streamed dynamic content."
}
view {
<Static>
<section class="wire-card stack">
<p class="wire-badge">Built once</p>
<h1>Build-time static shell</h1>
<p>This heading and page structure are emitted into dist/partial-shells.json.</p>
</section>
</Static>
<Dynamic>
<section class="wire-card stack" aria-live="polite">
<p class="wire-badge">Streamed per request</p>
<h2>Dynamic account region</h2>
<p>Personalized data belongs inside this boundary.</p>
</section>
</Dynamic>
}
}
@@ -0,0 +1,77 @@
page PlatformShowcase {
layout = "public"
render = "hybrid"
hydrate = "load"
state active = "Admin"
view {
<main class="platform-showcase">
<header>
<span class="eyebrow">Reactive primitives</span>
<h1>Reactive platform showcase</h1>
<p>Switch a dynamic component and verify transition and portal rendering.</p>
</header>
<section class="showcase-card">
<button type="button" @click='active = active === "Admin" ? "Guest" : "Admin"'>Switch component</button>
<Transition name="fade">
<Component is={active}>
<section class="component-case" data-component-case="Admin"><strong>Administrator tools</strong><p>Manage users, permissions, and platform settings.</p></section>
<section class="component-case" data-component-case="Guest"><strong>Guest dashboard</strong><p>Explore the public workspace and available resources.</p></section>
</Component>
</Transition>
</section>
<div id="showcase-modal" class="portal-target" aria-live="polite"></div>
<Portal to="#showcase-modal"><p>Portaled content rendered in its target.</p></Portal>
</main>
}
style {
.platform-showcase {
display: grid;
max-width: 58rem;
gap: 1.25rem;
margin: 3rem auto;
}
.platform-showcase header,
.showcase-card,
.component-case {
display: grid;
gap: 0.75rem;
}
.eyebrow {
color: var(--wire-color-primary);
font-weight: 700;
text-transform: uppercase;
}
.showcase-card,
.portal-target {
padding: 1.25rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-lg);
background: var(--wire-color-surface);
}
.showcase-card button {
width: fit-content;
padding: 0.7rem 1rem;
border: 0;
border-radius: var(--wire-radius-md);
background: var(--wire-color-primary);
color: var(--wire-color-primary-contrast);
cursor: pointer;
}
.fade-enter-active {
animation: platform-fade 180ms ease-out;
}
@keyframes platform-fade {
from { opacity: 0; transform: translateY(0.25rem); }
to { opacity: 1; transform: translateY(0); }
}
}
}
@@ -0,0 +1,27 @@
import { CreateUserSchema } from "../schemas/create-user";
page ServerActions {
seo {
title = "Typed server actions"
description = "Schema validation, CSRF, invalidation and progressive enhancement"
}
action createUser using CreateUserSchema {
invalidate("users")
return { id: crypto.randomUUID(), name: input.name, email: input.email }
}
view {
<main>
<h1>Typed server actions</h1>
<p>The form works with or without JavaScript.</p>
<form @submit="createUser">
<label for="action-name">Name</label>
<input id="action-name" name="name" autocomplete="name" required />
<label for="action-email">Email</label>
<input id="action-email" name="email" type="email" autocomplete="email" required />
<button type="submit">Create user</button>
</form>
</main>
}
}
@@ -0,0 +1,13 @@
import { defineJob } from "@wrnexus/queue";
export interface WelcomeEmailPayload {
userId: number;
email: string;
}
export default defineJob<WelcomeEmailPayload>({
name: "welcome-email",
async run(job) {
console.log(`Welcome email queued for ${job.data.email}`);
},
});
+84 -10
View File
@@ -4,16 +4,50 @@
export interface Routes {
"/": Record<string, never>;
"/about": Record<string, never>;
"/async-data": Record<string, never>;
"/chat": Record<string, never>;
"/client-only": Record<string, never>;
"/dashboard": Record<string, never>;
"/hello": Record<string, never>;
"/language-tools": Record<string, never>;
"/login": Record<string, never>;
"/modal": Record<string, never>;
"/partial-static": Record<string, never>;
"/platform-showcase": Record<string, never>;
"/reactive": Record<string, never>;
"/server-actions": Record<string, never>;
"/test": Record<string, never>;
"/ui": Record<string, never>;
}
export interface RouteNames {
"index": "/";
"about": "/about";
"async.data": "/async-data";
"chat": "/chat";
"client.only": "/client-only";
"dashboard": "/dashboard";
"hello": "/hello";
"language.tools": "/language-tools";
"login": "/login";
"modal": "/modal";
"partial.static": "/partial-static";
"platform.showcase": "/platform-showcase";
"reactive": "/reactive";
"server.actions": "/server-actions";
"test": "/test";
"ui": "/ui";
}
export interface RouteQueries {
[path: string]: Record<string, string | number | boolean | null | undefined>;
}
export type RoutePath = keyof Routes;
export type RouteName = keyof RouteNames;
export type RouteQuery<P extends RoutePath> = P extends keyof RouteQueries
? RouteQueries[P]
: Record<string, string | number | boolean | null | undefined>;
type RouteValue = string | readonly string[] | undefined;
function encodeRouteValue(value: RouteValue, catchAll: boolean): string {
@@ -22,17 +56,9 @@ function encodeRouteValue(value: RouteValue, catchAll: boolean): string {
return values.map((part) => encodeURIComponent(part)).join("/");
}
export function href<P extends RoutePath>(
path: P,
...args: keyof Routes[P] extends never
? []
: Record<string, never> extends Routes[P]
? [params?: Routes[P]]
: [params: Routes[P]]
): string {
const params = (args[0] ?? {}) as Record<string, RouteValue>;
function buildHref(path: string, params: Record<string, RouteValue> = {}): string {
const output: string[] = [];
for (const segment of String(path).split("/").filter(Boolean)) {
for (const segment of path.split("/").filter(Boolean)) {
let name: string | undefined;
let optional = false;
let catchAll = false;
@@ -61,3 +87,51 @@ export function href<P extends RoutePath>(
}
return "/" + output.filter(Boolean).join("/");
}
export function href<P extends RoutePath>(
path: P,
...args: keyof Routes[P] extends never
? []
: Record<string, never> extends Routes[P]
? [params?: Routes[P]]
: [params: Routes[P]]
): string {
const params = (args[0] ?? {}) as Record<string, RouteValue>;
return buildHref(String(path), params);
}
export function route<N extends RouteName>(
name: N,
...args: keyof Routes[RouteNames[N]] extends never
? [params?: Routes[RouteNames[N]], query?: RouteQuery<RouteNames[N]>]
: Record<string, never> extends Routes[RouteNames[N]]
? [params?: Routes[RouteNames[N]], query?: RouteQuery<RouteNames[N]>]
: [params: Routes[RouteNames[N]], query?: RouteQuery<RouteNames[N]>]
): string {
const paths: Record<RouteName, RoutePath> = {
"index": "/",
"about": "/about",
"async.data": "/async-data",
"chat": "/chat",
"client.only": "/client-only",
"dashboard": "/dashboard",
"hello": "/hello",
"language.tools": "/language-tools",
"login": "/login",
"modal": "/modal",
"partial.static": "/partial-static",
"platform.showcase": "/platform-showcase",
"reactive": "/reactive",
"server.actions": "/server-actions",
"test": "/test",
"ui": "/ui"
} as Record<RouteName, RoutePath>;
const output = buildHref(paths[name], (args[0] ?? {}) as Record<string, RouteValue>);
const query = args[1];
if (!query) return output;
const search = new URLSearchParams();
for (const [key, value] of Object.entries(query))
if (value !== undefined && value !== null) search.set(key, String(value));
const text = search.toString();
return text ? `${output}?${text}` : output;
}
@@ -0,0 +1,6 @@
import { v } from "@wrnexus/validation";
export const CreateUserSchema = v.object({
name: v.string().trim().min(2),
email: v.string().trim().email(),
});
@@ -0,0 +1,86 @@
import {
afterAll,
beforeAll,
createHarness,
describe,
expect,
test,
type Harness,
} from "@wrnexus/test";
describe("real application security abuse checks", () => {
let app: Harness;
beforeAll(async () => {
app = await createHarness(import.meta.dir + "/..");
});
afterAll(() => app?.close());
test("sets browser hardening headers on rendered pages", async () => {
const response = await app.fetch("/");
expect(response.headers.get("content-security-policy")).toContain("default-src");
expect(response.headers.get("x-content-type-options")).toBe("nosniff");
expect(response.headers.get("x-frame-options")).toBeTruthy();
expect(response.headers.get("referrer-policy")).toBeTruthy();
});
test("does not reflect script payloads into HTML", async () => {
const payload = `<script>globalThis.__attacked=true</script>`;
const response = await app.fetch(`/?search=${encodeURIComponent(payload)}`);
expect(response.status).toBe(200);
expect(await response.text()).not.toContain(payload);
});
test("rejects traversal attempts without exposing source files", async () => {
for (const path of ["/../../package.json", "/%2e%2e/%2e%2e/package.json", "/..%5c..%5c.env"]) {
const response = await app.fetch(path);
expect([400, 404]).toContain(response.status);
const body = await response.text();
expect(body).not.toContain("DATABASE_URL");
expect(body).not.toContain('"workspaces"');
}
});
test("does not grant CORS credentials to an untrusted origin", async () => {
const response = await app.fetch("/api/hello", {
headers: { origin: "https://evil.example" },
});
expect(response.headers.get("access-control-allow-origin")).toBeNull();
expect(response.headers.get("access-control-allow-credentials")).toBeNull();
});
test("requires CSRF for login and avoids credential oracle details", async () => {
const response = await app.fetch("/api/login", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ email: "victim@example.com", password: "wrong-password" }),
});
expect(response.status).toBe(403);
expect(await response.text()).not.toContain("passwordHash");
});
test("handles malformed JSON without a stack trace", async () => {
const response = await app.fetch("/api/echo", {
method: "POST",
headers: { "content-type": "application/json" },
body: "{broken",
});
expect(response.status).toBe(400);
const body = await response.text();
expect(body).not.toContain(" at ");
expect(body).not.toContain("node_modules");
});
test("refuses actual request bodies over the configured limit", async () => {
try {
const response = await app.fetch("/api/echo", {
method: "POST",
headers: { "content-type": "application/json" },
body: `"${"x".repeat(10 * 1024 * 1024)}"`,
});
expect(response.status).toBe(413);
} catch (error) {
// Bun rejects the oversized socket before application dispatch on some versions.
expect(String(error)).toMatch(/ECONNRESET|socket connection was closed/i);
}
});
});
@@ -0,0 +1,30 @@
import type { Context } from "@wrnexus/core";
const apiInput: WRNexusGenerated.ApiContracts["/api/typed-user"]["POST"]["input"] = {
name: "Ada",
email: "ada@example.test",
};
const queuePayload: WRNexusGenerated.QueuePayloads["welcome-email"] = {
userId: 42,
email: "ada@example.test",
};
const queryArgs: WRNexusGenerated.DatabaseQueries["GetUserByEmail"]["args"] = {
email: "ada@example.test",
};
const realtimeMessage: WRNexusGenerated.RealtimeMessages["/realtime/chat"] = {
user: "ada",
text: "hello",
};
const middlewareContext: WRNexusGenerated.MiddlewareContexts["auth"] = {} as Context;
void [apiInput, queuePayload, queryArgs, realtimeMessage, middlewareContext];
// @ts-expect-error schema-derived input requires an email.
const invalidApiInput: WRNexusGenerated.ApiContracts["/api/typed-user"]["POST"]["input"] = {
name: "Missing email",
};
void invalidApiInput;
// @ts-expect-error generated SQL query arguments require an email string.
const invalidQuery: WRNexusGenerated.DatabaseQueries["GetUserByEmail"]["args"] = {};
void invalidQuery;
+59
View File
@@ -0,0 +1,59 @@
// AUTO-GENERATED by `wrnexus generate types` - do not edit.
declare namespace WRNexusGenerated {
type ApiContract<T> = T extends import("@wrnexus/core").DefinedEndpoint<infer I, infer O>
? { input: I; output: O }
: T extends (...args: infer A) => infer R
? { input: A extends [any, infer I, ...any[]] ? I : unknown; output: Awaited<R> }
: { input: unknown; output: unknown };
type MiddlewareContext<T> = T extends (ctx: infer C, ...args: any[]) => any ? C : never;
type QueryContract<T> = T extends (db: any, args: infer A, ...rest: any[]) => infer R
? { args: A; result: Awaited<R> }
: T extends (db: any, ...rest: any[]) => infer R
? { args: Record<string, never>; result: Awaited<R> }
: never;
type RealtimeMessage<T> = T extends import("@wrnexus/core").RoomDefinition<any, infer M> ? M : unknown;
type QueuePayload<T> = T extends import("@wrnexus/queue").JobDefinition<infer I> ? I : unknown;
type RouteName = "about" | "async.data" | "chat" | "client.only" | "dashboard" | "hello" | "index" | "language.tools" | "login" | "modal" | "partial.static" | "platform.showcase" | "reactive" | "server.actions" | "test" | "ui";
type ApiRoute = "/api/echo" | "/api/graphql-example" | "/api/hello" | "/api/login" | "/api/logout" | "/api/me" | "/api/typed-user" | "/api/users/csr" | "/api/users/ssr" | "/api/webhooks/payment";
type RealtimeRoute = "/realtime/chat" | "/realtime/hello";
type EnvironmentKey = "APP_LABEL" | "DATABASE_URL" | "DEMO_SHARED" | "HOST" | "NODE_ENV" | "PORT" | "SESSION_SECRET" | "UAT_ONLY";
type TranslationKey = "api.greeting" | "home.intro" | "home.title" | "nav.about" | "nav.chat" | "nav.dashboard" | "nav.home" | "nav.ui";
type QueueName = "welcome-email";
type CacheKey = "users";
interface Components {
"Modal": { props: Record<string, never>; outputs: Record<string, never> };
"Counter": { props: { "start"?: unknown; "label"?: unknown }; outputs: Record<string, never> };
}
interface ApiContracts {
"/api/webhooks/payment": { POST: ApiContract<typeof import("../api/webhooks/payment.ts")["POST"]> };
"/api/users/csr": { GET: ApiContract<typeof import("../api/users/csr.ts")["GET"]> };
"/api/users/ssr": { GET: ApiContract<typeof import("../api/users/ssr.ts")["GET"]> };
"/api/graphql-example": { POST: ApiContract<typeof import("../api/graphql-example.ts")["POST"]> };
"/api/typed-user": { POST: ApiContract<typeof import("../api/typed-user.ts")["POST"]> };
"/api/logout": { POST: ApiContract<typeof import("../api/logout.ts")["POST"]> };
"/api/hello": { GET: ApiContract<typeof import("../api/hello.ts")["GET"]> };
"/api/login": { POST: ApiContract<typeof import("../api/login.ts")["POST"]> };
"/api/echo": { GET: ApiContract<typeof import("../api/echo.ts")["GET"]>; POST: ApiContract<typeof import("../api/echo.ts")["POST"]> };
"/api/me": { GET: ApiContract<typeof import("../api/me.ts")["GET"]> };
}
interface MiddlewareContexts {
"auth": MiddlewareContext<(typeof import("../middleware/auth.ts"))["default"]>;
"logger": MiddlewareContext<(typeof import("../middleware/logger.ts"))["default"]>;
"ratelimit": MiddlewareContext<(typeof import("../middleware/ratelimit.ts"))["default"]>;
}
interface DatabaseQueries {
"GetUserByEmail": QueryContract<typeof import("../db/queries.gen.ts")["GetUserByEmail"]>;
"ListUsers": QueryContract<typeof import("../db/queries.gen.ts")["ListUsers"]>;
"CountActive": QueryContract<typeof import("../db/queries.gen.ts")["CountActive"]>;
"CreateUser": QueryContract<typeof import("../db/queries.gen.ts")["CreateUser"]>;
"DeactivateUser": QueryContract<typeof import("../db/queries.gen.ts")["DeactivateUser"]>;
}
interface RealtimeMessages {
"/realtime/hello": RealtimeMessage<(typeof import("../pages/hello.wrn"))["default"]>;
"/realtime/chat": RealtimeMessage<(typeof import("../realtime/chat.ts"))["default"]>;
}
interface QueuePayloads {
"welcome-email": QueuePayload<(typeof import("../queues/welcome-email.ts"))["default"]>;
}
type ApplicationConfig = (typeof import("../../wrnexus.config.ts"))["default"];
}
@@ -0,0 +1 @@
// AUTO-GENERATED plugin type aggregation - do not edit.