feat(examples): worked example for typed api blocks
- Add examples/basic-app/app/api/directory.ts and app/pages/api-block-demo.wrn as the end-to-end worked example for typed api blocks (Task 6). - Add **/*.generated.api-checks.ts to .prettierignore: this generated file must match the CLI's raw output byte-for-byte for check:generated-types, and prettier was reformatting it. - Fix packages/csr/test/api-call.test.ts: no-unsafe-function-type lint error from the raw Function type, uncovered while running the full gate. - Rebuild editors/vscode bundles (packages/compiler and packages/syntax changed in Tasks 1-5). - Regenerate docs/public-api-0.8.json (additive: isIdentPart, isIdentStart, skipLiteralOrComment newly exported from packages/syntax). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
import { defineEndpoint } from "@wrnexus/core";
|
||||
import type { Context } from "@wrnexus/core";
|
||||
|
||||
const ALL = [
|
||||
{ name: "Ajay", designation: "UI" },
|
||||
{ name: "Asha", designation: "Backend" },
|
||||
{ name: "Chen", designation: "UI" },
|
||||
];
|
||||
|
||||
export const POST = async (ctx: Context) => {
|
||||
const body = (await ctx.req.json().catch(() => ({}))) as { name?: string };
|
||||
const needle = String(body.name ?? "").toLowerCase();
|
||||
return Response.json({ users: ALL.filter((user) => user.name.toLowerCase().includes(needle)) });
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
page ApiBlockDemo {
|
||||
state nameFilter = "a"
|
||||
state found = ""
|
||||
state failed = ""
|
||||
|
||||
client {
|
||||
api searchDirectory POST /api/directory {
|
||||
request {
|
||||
body {
|
||||
name?: string
|
||||
}
|
||||
}
|
||||
|
||||
response {
|
||||
return data.users
|
||||
}
|
||||
|
||||
error {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
functions {
|
||||
client async function search(): Promise<void> {
|
||||
const users = await api.searchDirectory({ name: nameFilter })
|
||||
found = users.map((user) => user.name).join(", ")
|
||||
}
|
||||
}
|
||||
|
||||
view {
|
||||
<main>
|
||||
<button @click="search()">Search</button>
|
||||
<p class="found" data-text="found">{found}</p>
|
||||
<p class="failed" data-text="failed">{failed}</p>
|
||||
</main>
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
export interface Routes {
|
||||
"/": Record<string, never>;
|
||||
"/about": Record<string, never>;
|
||||
"/api-block-demo": Record<string, never>;
|
||||
"/async-data": Record<string, never>;
|
||||
"/chat": Record<string, never>;
|
||||
"/client-only": Record<string, never>;
|
||||
@@ -27,6 +28,7 @@ export interface Routes {
|
||||
export interface RouteNames {
|
||||
"index": "/";
|
||||
"about": "/about";
|
||||
"api.block.demo": "/api-block-demo";
|
||||
"async.data": "/async-data";
|
||||
"chat": "/chat";
|
||||
"client.only": "/client-only";
|
||||
@@ -119,6 +121,7 @@ export function route<N extends RouteName>(
|
||||
const paths: Record<RouteName, RoutePath> = {
|
||||
"index": "/",
|
||||
"about": "/about",
|
||||
"api.block.demo": "/api-block-demo",
|
||||
"async.data": "/async-data",
|
||||
"chat": "/chat",
|
||||
"client.only": "/client-only",
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
// Type-only assertions for sectioned `api` blocks. Kept as a real .ts file (not
|
||||
// wrnexus.generated.d.ts) because `skipLibCheck` exempts .d.ts contents from being
|
||||
// checked; this file is compiled and checked normally by the project's own tsc.
|
||||
|
||||
type __wrn_api_check_searchDirectory = WRNexusGenerated.__wrn_expect_true<WRNexusGenerated.AssertAssignable<{ name?: string }, WRNexusGenerated.ApiInput<"/api/directory", "POST">>>;
|
||||
export {};
|
||||
|
||||
+3
-2
@@ -13,8 +13,8 @@ declare namespace WRNexusGenerated {
|
||||
: 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" | "island.demo" | "language.tools" | "layout" | "login" | "modal" | "navigation" | "partial.static" | "platform.showcase" | "reactive" | "server.actions" | "table" | "test" | "ui";
|
||||
type ApiRoute = "/api/accounts" | "/api/echo" | "/api/graphql-example" | "/api/hello" | "/api/invite" | "/api/login" | "/api/logout" | "/api/me" | "/api/typed-user" | "/api/users/csr" | "/api/users/ssr" | "/api/webhooks/payment";
|
||||
type RouteName = "about" | "api.block.demo" | "async.data" | "chat" | "client.only" | "dashboard" | "hello" | "index" | "island.demo" | "language.tools" | "layout" | "login" | "modal" | "navigation" | "partial.static" | "platform.showcase" | "reactive" | "server.actions" | "table" | "test" | "ui";
|
||||
type ApiRoute = "/api/accounts" | "/api/directory" | "/api/echo" | "/api/graphql-example" | "/api/hello" | "/api/invite" | "/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.layout" | "nav.navigation" | "nav.ui";
|
||||
@@ -29,6 +29,7 @@ declare namespace WRNexusGenerated {
|
||||
"/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/directory": { POST: ApiContract<typeof import("../api/directory.ts")["POST"]> };
|
||||
"/api/accounts": { GET: ApiContract<typeof import("../api/accounts.ts")["GET"]> };
|
||||
"/api/invite": { POST: ApiContract<typeof import("../api/invite.ts")["POST"]> };
|
||||
"/api/logout": { POST: ApiContract<typeof import("../api/logout.ts")["POST"]> };
|
||||
|
||||
Reference in New Issue
Block a user