Skip to content

Commit 536fb73

Browse files
committed
feat(0.1.0): release candidate for Router
This change pulls the core ideas of pick into a single router.ts file and implements what I think is a decent interface for the original Router. In the process I moved the indexed async state either monad, now called Effect, to the fun library. Effect is now variadic in a clever but very useful way. Many of the interfaces for Handler, Route, and Router have breaking changes. I've implemented a very basic but quite performant default router that does fallthrough routing after matching on the request method. There is also the majority of the initial SiteBuilder work done. The idea behind a site builder was to point it at a directory and have it turn .tsx files into a SPA, .ts files into api routes, and everything else into static assets. It's construction should be extensible enough that new RouteBuilders can be created externally so that other filetypes can be turned into routes. ie. Markdown files can be pre-rendered, image files can be compressed, etc etc. It's also meant to be generic enough that it can be run on any platform and not just on Deno. Currently, the SiteBuilder only has a static route builder. The following is intended to be done soon: * Server route builders should be straightforward, but I'd like to add some degree of type checking to make them break at app start and not at runtime. * Client route builders are simple enough to build, since they will all serve the same index file.. but there is a chicken and egg issue here. * I need to spec types for a ClientBuilder/ClientBundler that takes the array of ClientRoutes and turns them into a bundle! Hopefully it's easy enough. Once these features are complete and test coverage is at 100% I will likey take a break from pick for awhile, but I do want to return at a later date to implement the following features: * Routes should take optional input/output schemas that match OpenAPI definitions. Or default to unknown. * Routers should have a get_description method that returns a sorted list of all Routes without their Handlers. * We should have some tooling for converting a Route list to a sitemap or an OpenAPI spec. * SiteBuilder should have an option to mount an OpenAPI spec or a sitemap at unused paths (and probably write those files into the root site path). All in all, a good amount of cleanup and work is done now. Should have the bare minimum of what I need soon. I think capnweb is going to pull me away from swagger/openapi generators and client generators for awhile. This SiteBuilder approach resolves tons of issues with cors, versioning, and type safety.
1 parent c386a04 commit 536fb73

30 files changed

+2247
-801
lines changed

.github/workflows/format.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Check Format
2+
3+
on: [workflow_call]
4+
5+
jobs:
6+
format:
7+
strategy:
8+
matrix:
9+
deno: ["v2.2.12"]
10+
os: [ubuntu-latest]
11+
12+
runs-on: ${{ matrix.os }}
13+
14+
steps:
15+
- name: Setup repo
16+
uses: actions/checkout@v2
17+
18+
- name: Setup Deno
19+
uses: denoland/setup-deno@v1
20+
with:
21+
deno-version: ${{ matrix.deno }}
22+
23+
- name: Run Deno Check Formatting
24+
run: deno fmt --check

.github/workflows/publish.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: Publish
2+
3+
on: [workflow_call]
4+
5+
jobs:
6+
publish:
7+
permissions:
8+
contents: read
9+
id-token: write
10+
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Setup repo
15+
uses: actions/checkout@v4
16+
17+
- name: Setup Deno
18+
uses: denoland/setup-deno@v1
19+
with:
20+
deno-version: "v2.2.12"
21+
22+
- name: Publish package
23+
run: deno publish

.github/workflows/pull_request.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: PR
2+
3+
on: [pull_request]
4+
5+
jobs:
6+
call-format:
7+
uses: ./.github/workflows/format.yml
8+
9+
call-test:
10+
uses: ./.github/workflows/test.yml

.github/workflows/push.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Push Main
2+
3+
on:
4+
push:
5+
branches: [main]
6+
7+
jobs:
8+
call-format:
9+
uses: ./.github/workflows/format.yml
10+
11+
call-test:
12+
uses: ./.github/workflows/test.yml
13+
secrets: inherit
14+
15+
call-publish:
16+
needs: [call-format, call-test]
17+
uses: ./.github/workflows/publish.yml
18+
permissions:
19+
contents: read
20+
id-token: write

.github/workflows/test.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Test
2+
3+
on: [workflow_call]
4+
5+
jobs:
6+
test:
7+
strategy:
8+
matrix:
9+
deno: ["v2.2.12"]
10+
os: [macOS-latest, windows-latest, ubuntu-latest]
11+
12+
runs-on: ${{ matrix.os }}
13+
14+
steps:
15+
- name: Setup repo
16+
uses: actions/checkout@v2
17+
18+
- name: Setup Deno
19+
uses: denoland/setup-deno@v1
20+
with:
21+
deno-version: ${{ matrix.deno }}
22+
23+
- name: Run Deno Tests
24+
run: deno test --parallel --coverage=coverage
25+
26+
- name: Generate Coverage
27+
run: deno coverage --unstable ./coverage --lcov > ./coverage/lcov.info
28+
29+
- name: Coveralls
30+
uses: coverallsapp/github-action@master
31+
with:
32+
flag-name: run-${{ matrix.deno }}-${{ matrix.os }}
33+
github-token: ${{ secrets.GITHUB_TOKEN }}
34+
base-path: /

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
/.direnv
22
.jj
3+
.vscode
4+
tmp

LICENSE

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
The MIT License (MIT)
22

3-
Copyright (c) 2017 Giulio Canti
4-
Copyright (c) 2018 Tom Crockett
53
Copyright (c) 2019 Brandon Blaylock
64

75
Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -21,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2119
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2220
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2321
SOFTWARE.
24-

README.md

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -58,19 +58,17 @@ generally, the router in pick expects the more specific Handler that looks like:
5858

5959
```ts
6060
// Context
61-
// Request is the web standard HTTP request type
6261
// S is the application state
63-
// V is a collection of variables parsed from the request path
64-
type Context<S, V> = {
65-
readonly request: Request;
62+
type Context<S> = {
6663
readonly state: S;
67-
readonly path: V;
6864
};
6965

70-
type RouteHandler<S, V, O> = Handler<Context<S, V>, Response, O>;
71-
72-
// Or, if we substitute the types ourselves
73-
type RouteHandlerSub<S, V, O> = (ctx: Context<S, V>) => Promise<[Response, O]>;
66+
// A route handler receives the request, parsed path parameters, and context
67+
type RouteHandler<S, V> = (
68+
request: Request,
69+
path: V,
70+
ctx: Context<S>,
71+
) => Response | Promise<Response>;
7472
```
7573

7674
The Router in pick doesn't really care about the output state `O` of the Handler
@@ -91,25 +89,23 @@ that can be trivial "simplified" to a very useful minimal implementation.
9189
Put this all together and a simple router can be set up like so:
9290

9391
```ts
94-
import * as R from "../router.ts";
95-
import { html } from "../response.ts";
96-
import { pipe } from "fun/fn.ts";
97-
98-
const router = pipe(
99-
// Create a Router
100-
R.router<{ count: number }>(),
101-
// Add a Route
102-
R.respond(
103-
"GET /hello/:name",
104-
(ctx) =>
105-
html(
106-
`<h1>Hello ${ctx.path.name}, you are number ${++ctx.state.count}</h1>`,
107-
),
108-
),
109-
R.withState({ count: 0 }),
110-
);
111-
112-
Deno.serve(router);
92+
import { context, html, right, router } from "./router.ts";
93+
94+
const ctx = context({ count: 0 });
95+
96+
const myRouter = router(ctx, {
97+
routes: [
98+
right("GET /hello/:name", (request, params, ctx) => {
99+
ctx.state.count++;
100+
return html(
101+
`<h1>Hello ${params.name}, you are visitor number ${ctx.state.count}</h1>`,
102+
);
103+
}),
104+
],
105+
});
106+
107+
// Start the server:
108+
// Deno.serve(myRouter.handle);
113109
```
114110

115111
## Contributing

TODO.md

Lines changed: 0 additions & 6 deletions
This file was deleted.

0 commit comments

Comments
 (0)