Add Declarative Web Framework Design
parent
181849d640
commit
ae5ece7c13
1 changed files with 127 additions and 0 deletions
127
Declarative-Web-Framework-Design.md
Normal file
127
Declarative-Web-Framework-Design.md
Normal file
|
@ -0,0 +1,127 @@
|
|||
# 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
|
||||
* 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 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
|
||||
|
||||
1. A non-Turing complete step is used to collect everything into a flat set of resources
|
||||
2. 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
|
||||
*
|
Loading…
Reference in a new issue