JUMBO refactor, still work in progress

This commit is contained in:
2025-10-07 00:14:31 +02:00
parent 3694492e1a
commit dc0ec5c635
50 changed files with 4283 additions and 3698 deletions

106
packages/backend/src/api.ts Normal file
View File

@@ -0,0 +1,106 @@
import * as Api from "common/Api";
import * as Cbor from "common/Cbor";
import * as Client from "common/Client";
import { Cause, Effect, Either, HashMap, Inspectable, Option, pipe } from "effect";
/* NOTE We shouldn't need to extract this to a separate type, but if we don't do
* it the TypeScript parser in VS Code sort of blows up and the syntax colors
* turn wrong.
*/
type Return<Impl extends Api.ApiBundleImpl<any>> = Effect.Effect<Response, never, Effect.Effect.Context<ReturnType<Impl[keyof Impl]>>>;
const catchToResponse = Effect.catchAll((error: unknown) => pipe(
error,
Inspectable.toJSON,
Cbor.encode,
Effect.map(Client.cborBody),
Effect.matchEffect({
onSuccess: ({ body, headers }) => Effect.fail(new Response(body, { status: 500, headers })),
onFailure: () => Effect.fail(new Response(null, { status: 500 })),
}),
));
export const implement = <
const Record extends { readonly [_: string]: Api.ApiAny },
const Impl extends Api.ApiBundleImpl<Record>,
>(
bundle: Api.ApiBundle<Record>,
impl: Impl,
): (key: string, request: Request) => Return<Impl> => {
return (key, requestObject) => {
/* Force both return types to be `Response`. We can use the error route
* for it's short-circuit capabilities.
*/
const effect: Effect.Effect<Response, Response, any> = Effect.gen(function* () {
const maybeApi = HashMap.get(bundle.map, key);
if (Option.isNone(maybeApi)) {
return RESPONSE_API_NOT_FOUND;
}
const { value: api } = maybeApi;
const fn = impl[key];
if (fn === undefined) {
return RESPONSE_API_NOT_IMPLEMENTED;
}
const request = yield* pipe(
requestObject,
Client.decodeBody(api.request),
catchToResponse,
);
const result = yield* pipe(
request,
fn,
Effect.sandbox,
Effect.either,
);
const { status, response } = pipe(
result,
Either.match({
onLeft: (cause) => pipe(
cause,
Cause.failureOrCause,
Either.match({
onLeft: (error) => ({
status: 400,
response: pipe(
error,
Client.encodeBody(api.error),
catchToResponse,
),
}),
onRight: (die) => ({
status: 500,
response: pipe(
die,
Inspectable.toJSON,
Cbor.encode,
Effect.map(Client.cborBody),
catchToResponse,
),
}),
}),
),
onRight: (response) => ({
status: 200,
response: pipe(
response,
Client.encodeBody(api.response),
catchToResponse,
),
}),
}),
);
const { body, headers } = yield* response;
return new Response(body, { status, headers });
});
return Effect.catchAll(effect, Effect.succeed);
};
};
const RESPONSE_API_NOT_FOUND = new Response(null, { status: 404 });
const RESPONSE_API_NOT_IMPLEMENTED = new Response(null, { status: 501 });