feat: add application productivity foundations
This commit is contained in:
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user