feat: add application productivity foundations
Quality / quality (ubuntu-latest) (push) Failing after 22s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
2026-08-23 11:13:03 +05:30
parent 46195462c3
commit 64ab20cc95
42 changed files with 1533 additions and 40 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/csr",
"version": "0.8.28",
"version": "0.8.29",
"type": "module",
"main": "src/index.ts",
"exports": {
+41
View File
@@ -0,0 +1,41 @@
export interface ApiRequest<Input = unknown> {
method?: string;
params?: Record<string, string | number>;
query?: Record<string, string | number | boolean | undefined>;
body?: Input;
}
export interface ApiClientOptions {
baseUrl?: string;
fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
}
/** Runtime used by generated API clients; throws a typed response error on non-2xx results. */
export function createApiClient(options: ApiClientOptions = {}) {
const fetcher = options.fetch ?? globalThis.fetch;
return async function call<Output, Input = unknown>(
template: string,
request: ApiRequest<Input> = {},
): Promise<Output> {
let path = template;
for (const [name, value] of Object.entries(request.params ?? {})) {
path = path.replace(`[${name}]`, encodeURIComponent(String(value)));
}
const url = new URL(path, options.baseUrl ?? globalThis.location?.origin ?? "http://localhost");
for (const [name, value] of Object.entries(request.query ?? {}))
if (value !== undefined) url.searchParams.set(name, String(value));
const response = await fetcher(url, {
method: request.method ?? (request.body === undefined ? "GET" : "POST"),
credentials: "same-origin",
headers: request.body === undefined ? undefined : { "content-type": "application/json" },
body: request.body === undefined ? undefined : JSON.stringify(request.body),
});
const data = response.status === 204 ? undefined : await response.json();
if (!response.ok)
throw Object.assign(
new Error((data as { error?: string })?.error ?? `HTTP ${response.status}`),
{ status: response.status, data },
);
return data as Output;
};
}
+2
View File
@@ -16,6 +16,8 @@ export { REACTIVE_RUNTIME } from "./reactive-runtime.ts";
export { NAV_RUNTIME } from "./nav-runtime.ts";
export { REALTIME_RUNTIME } from "./realtime-runtime.ts";
export { ACTION_RUNTIME } from "./action-runtime.ts";
export { createApiClient } from "./api-client.ts";
export type { ApiClientOptions, ApiRequest } from "./api-client.ts";
const CONTROLLER_SECTIONS = ["PRIMARY", "UI", "PIN"] as const;
+23
View File
@@ -0,0 +1,23 @@
import { expect, test } from "bun:test";
import { createApiClient } from "../src/index.ts";
test("typed API client expands params, query and JSON input", async () => {
let request: Request | undefined;
const call = createApiClient({
baseUrl: "https://app.test",
fetch: async (input, init) => {
request = new Request(input, init);
return Response.json({ ok: true });
},
});
expect(
await call<{ ok: boolean }, { name: string }>("/api/users/[id]", {
method: "PUT",
params: { id: 7 },
query: { audit: true },
body: { name: "Ada" },
}),
).toEqual({ ok: true });
expect(request?.url).toBe("https://app.test/api/users/7?audit=true");
expect(await request?.json()).toEqual({ name: "Ada" });
});