The rewrite
This commit is contained in:
7
packages/backend/package.json
Normal file
7
packages/backend/package.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "@make-api/backend",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest"
|
||||
}
|
||||
}
|
107
packages/backend/src/Api.ts
Normal file
107
packages/backend/src/Api.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
/*!
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Schema as S } from "@effect/schema";
|
||||
import * as Api from "@make-api/api/Api";
|
||||
import * as Query from "@make-api/api/Query";
|
||||
import * as RequestBody from "@make-api/api/RequestBody";
|
||||
import * as Route from "@make-api/api/Route";
|
||||
import { Array as A, Effect, Match, Option as O, Record as R, pipe } from "effect";
|
||||
|
||||
const getUsers = pipe(
|
||||
Api.make("GET", "/users"),
|
||||
Api.responseBodyJson(200, S.Array(S.Struct({
|
||||
userId: S.UUID,
|
||||
username: S.String,
|
||||
email: S.String,
|
||||
}))),
|
||||
Api.responseBodyJson(500, S.Struct({
|
||||
error: S.String,
|
||||
stackTrace: S.optional(S.String, { exact: true }),
|
||||
})),
|
||||
);
|
||||
|
||||
type RouteParams<T extends Route.Route.Any> = { readonly [_: string]: unknown }; // TODO
|
||||
|
||||
const matchesRoute = <T extends Route.Route.Any>(route: T, pathComponents: readonly string[]): boolean => {
|
||||
if (route.length !== pathComponents.length) return false;
|
||||
return pipe(
|
||||
A.zip(route, pathComponents),
|
||||
A.every(([token, pathComponent]) => token._tag !== "Literal" || token.literal === pathComponent),
|
||||
);
|
||||
};
|
||||
|
||||
const parseRoute = <T extends Route.Route.Any>(route: T) => {
|
||||
const decoder = pipe(
|
||||
route,
|
||||
A.filterMap((r) => r._tag === "Param" ? O.some([r.name, r.schema] as const) : O.none()),
|
||||
R.fromEntries,
|
||||
S.Struct,
|
||||
(_) => S.decode(_, { errors: "all" }),
|
||||
);
|
||||
|
||||
return (pathComponents: readonly string[]) => pipe(
|
||||
pathComponents,
|
||||
A.filterMap((c, i) => {
|
||||
const r = route[i];
|
||||
return r._tag === "Param" ? O.some([r.name, c] as const) : O.none();
|
||||
}),
|
||||
R.fromEntries,
|
||||
decoder,
|
||||
);
|
||||
};
|
||||
|
||||
const parseQuery = <T extends Query.Query.Any>(query: T) => {
|
||||
const decoder = pipe();
|
||||
|
||||
return (searchParams: URLSearchParams) => pipe();
|
||||
};
|
||||
|
||||
const parseBody = <T extends RequestBody.RequestBody.Any>(requestBody: T) => {
|
||||
const decoder = pipe(
|
||||
Match.value<RequestBody.RequestBody.Any>(requestBody),
|
||||
Match.tags({
|
||||
None: () => {},
|
||||
Text: () => {},
|
||||
Json: () => {},
|
||||
UrlEncoded: () => {},
|
||||
Multipart: () => {},
|
||||
File: () => {},
|
||||
}),
|
||||
);
|
||||
|
||||
return (req: Request) => pipe();
|
||||
};
|
||||
|
||||
const routeParser = parseRoute(getUsers.props.route);
|
||||
const queryParser = parseQuery(getUsers.props.query);
|
||||
const bodyParser = parseBody(getUsers.props.request);
|
||||
|
||||
Bun.serve({
|
||||
fetch: (req) => {
|
||||
const url = new URL(req.url);
|
||||
|
||||
const pathname = url.pathname;
|
||||
const pathComponents = pathname.replaceAll(/^\/|\/$/g, "").split("/");
|
||||
|
||||
const searchParams = url.searchParams;
|
||||
|
||||
if (matchesRoute(getUsers.props.route, pathComponents)) {
|
||||
const paramsEffect = routeParser(pathComponents);
|
||||
const queryEffect = queryParser(searchParams);
|
||||
const bodyEffect = bodyParser(req);
|
||||
|
||||
const effect = Effect.all({
|
||||
params: paramsEffect,
|
||||
query: queryEffect,
|
||||
body: bodyEffect,
|
||||
});
|
||||
}
|
||||
},
|
||||
websocket: {
|
||||
message: (ws, message) => {}
|
||||
},
|
||||
});
|
Reference in New Issue
Block a user