1
Declarative Web Framework Design
Michael Zhang edited this page 2024-10-17 23:49:01 +00:00
Declarative Web Framework
The eventual goal of this is to create a language that compiles down to files, in a very similar way to Nix. The idea is to have a pure language that describes an impure computation.
- Some kind of type that represents "files" on disk. This is the compile output
- Need a way of saying "generate me a path during compile-time" in a way that all of the downstreams still refer to the same path
- Could be a hash based on the file:span of the invocation?
- Lots of tools for performing string parsing and editing
- Need a way of saying "generate me a path during compile-time" in a way that all of the downstreams still refer to the same path
- A higher-level "resource" that may compile to files.
- A resource consists of:
- Arbitrary attribute set, that can include other sub-resources
- Recommended to only have one of the following two:
- A set of "default" sub-resources to include
- A set of files to emit
- A function that operates over and modifies other resources
- Example: a database schema may be a higher-level resource. Compiling this should result in some database files
- To perform migrations, the migration CLI tool that's generated would write a file indicating the new state of the database it just learned back into the source repo to be checked in.
- I think the type signature should look something like this:
-
DatabaseTable : { name: String, files: Set<File>, // Resources is essentially a set of files along with some particular fields of interest // Here, apply is allowed to assume that all the files in the files field exist apply: Resources -> Resources }
-
- A resource consists of:
- A concept of a "singleton" resource
- I think this would mostly be for the post-compilation step, where including for example any DocumentationResource should trigger a post-compilation collection of all documentation resources
- Quoting for various source code languages like Javascript
Stages of compilation
- A non-Turing complete step is used to collect everything into a flat set of resources
- An optional post-compilation step is run, consisting of all the PostCompilationResource
Example components
Auth plugin
let db : SQLDatabase<Sqlite> = ...
let adaptor : SQLWithDb<_> = Adaptor({ db })
let authPlugin = AuthPlugin({
backend: ... // some kind of config for a database adaptor
// one example would be "SQL db w/ password"
// another could be "SQL db w/ magic link"
clientStrategy: ... // some config like session cookie or jwt
allowGuests: true,
})
authPlugin would be {
base: Resource,
usersTable: SQLTableResource,
loginRoute: (LoginRouteOpts) -> EndpointResource,
loginComponent: (LoginComponentOpts) -> WebComponentResource,
registerRoute: (RegisterRouteOpts) -> EndpointResource,
registerComponent: (RegisterComponentOpts) -> WebComponentResource,
}
Comments plugin
let commentsPlugin = CommentsPlugin({
})
result {
base: Resource,
component: (CommentComponentOpts) -> WebComponentResource,
}
Example endpoint
let submitFlagComponent = ({}) => {
let endpoint = EndpointResource({
guards: {
user: UserAuthGuard(),
flags: DbTableGuard(flagTable),
flagChecker: FlagCheckerGuard(user),
submittedFlag: PostJsonFieldGuard("flag", "string"),
},
// This entire next part emits an AST, it does NOT actually run this code
action: do {
result <- flagChecker(submittedFlag);
flags.insert({
userId: user.id,
flag: submittedFlag,
correct: result.correct,
});
return result;
}
})
let inputBox = WebComponentResource(jsxQuote!(
({ postSubmit }) => {
const error = useState();
const onSubmit = () => {
const result = await fetch(#{endpoint.url}, { method: "POST" });
};
return <form onSubmit={onSubmit}>
<input placeholder="Submit flag..." />
</form>;
}
))
Resource.bundle([ endpoint, inputBox ])
}
Deploy to kubernetes plugin
This would be a function that takes a resource consisting of a bunch of endpoints, database tables, etc. and produces a deploy script
Deploy to docker compose plugin
Run a post-compile script that produces a docker image with a particular image name, as well as a docker-compose.yml
file
Documentation
There should be a "documentation resource" type, which gets collected all together with a search index
Open questions
- Need something like multi-stage programs? Functions annotated with @stage(n). Can't imagine a situation needing this yet