diff --git a/README.md b/README.md
index 5e371ecdb16..238f0816415 100644
--- a/README.md
+++ b/README.md
@@ -1,24 +1,12 @@
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
**[Liveblocks](https://liveblocks.io) gives you the building blocks and
@@ -90,8 +78,8 @@ notifications, and more.
## License
-Most of this repository is licensed under the Apache License 2.0, Copyright
-© 2021-present [Liveblocks](https://liveblocks.io). Some components are
-licensed under AGPL-3.0-or-later.
+Most of this repository is licensed under the Apache License 2.0, Copyright ©
+2021-present [Liveblocks](https://liveblocks.io). Some components are licensed
+under AGPL-3.0-or-later.
See [LICENSE](./LICENSE) for details.
diff --git a/packages/liveblocks-client/README.md b/packages/liveblocks-client/README.md
index c5abbd19429..1f46fceabf0 100644
--- a/packages/liveblocks-client/README.md
+++ b/packages/liveblocks-client/README.md
@@ -1,24 +1,14 @@
-
-
-
-
-
-
-
+
+
+
# `@liveblocks/client`
-
-
-
-
-
-
-
-
-
+
+
+
`@liveblocks/client` provides the APIs to integrate with Liveblocks—a platform
diff --git a/packages/liveblocks-emails/README.md b/packages/liveblocks-emails/README.md
index a2bf6f76603..9fd5b1324de 100644
--- a/packages/liveblocks-emails/README.md
+++ b/packages/liveblocks-emails/README.md
@@ -1,24 +1,14 @@
-
-
-
-
-
-
-
+
+
+
# `@liveblocks/emails`
-
-
-
-
-
-
-
-
-
+
+
+
`@liveblocks/emails` provides a set of functions and utilities to make sending
diff --git a/packages/liveblocks-node-lexical/README.md b/packages/liveblocks-node-lexical/README.md
index be6ac765f1d..2a18105f02f 100644
--- a/packages/liveblocks-node-lexical/README.md
+++ b/packages/liveblocks-node-lexical/README.md
@@ -1,14 +1,15 @@
-
-
-
-
-
-
-
+
+
+
# `@liveblocks/node-lexical`
+
+
+
+
+
## License
Licensed under the Apache License 2.0, Copyright © 2021-present
diff --git a/packages/liveblocks-node-prosemirror/README.md b/packages/liveblocks-node-prosemirror/README.md
index 8f2e7be7477..9d8b3b62f3d 100644
--- a/packages/liveblocks-node-prosemirror/README.md
+++ b/packages/liveblocks-node-prosemirror/README.md
@@ -1,14 +1,15 @@
-
-
-
-
-
-
-
+
+
+
# `@liveblocks/node-prosemirror`
+
+
+
+
+
## License
Licensed under the Apache License 2.0, Copyright © 2021-present
diff --git a/packages/liveblocks-node/README.md b/packages/liveblocks-node/README.md
index 6e36201f464..b3bcab3a04a 100644
--- a/packages/liveblocks-node/README.md
+++ b/packages/liveblocks-node/README.md
@@ -1,24 +1,14 @@
-
-
-
-
-
-
-
+
+
+
# `@liveblocks/node`
-
-
-
-
-
-
-
-
-
+
+
+
`@liveblocks/node` provides server-side utilities to set up
diff --git a/packages/liveblocks-react-blocknote/README.md b/packages/liveblocks-react-blocknote/README.md
index f08f577b803..0e0c1235640 100644
--- a/packages/liveblocks-react-blocknote/README.md
+++ b/packages/liveblocks-react-blocknote/README.md
@@ -1,24 +1,14 @@
-
-
-
-
-
-
-
+
+
+
-
+
# `@liveblocks/react-blocknote`
-
-
-
-
-
-
-
-
-
+
+
+
`@liveblocks/react-blocknote` provides [React](https://reactjs.org/) APIs to
diff --git a/packages/liveblocks-react-lexical/README.md b/packages/liveblocks-react-lexical/README.md
index 8fc3a2949b0..ac6d7394ca2 100644
--- a/packages/liveblocks-react-lexical/README.md
+++ b/packages/liveblocks-react-lexical/README.md
@@ -1,24 +1,14 @@
-
-
-
-
-
-
-
+
+
+
-
+
# `@liveblocks/react-lexical`
-
-
-
-
-
-
-
-
-
+
+
+
`@liveblocks/react-lexical` provides [React](https://reactjs.org/) APIs to
diff --git a/packages/liveblocks-react-tiptap/README.md b/packages/liveblocks-react-tiptap/README.md
index 5817568e657..929927abea9 100644
--- a/packages/liveblocks-react-tiptap/README.md
+++ b/packages/liveblocks-react-tiptap/README.md
@@ -1,24 +1,14 @@
-
-
-
-
-
-
-
+
+
+
-
+
# `@liveblocks/react-tiptap`
-
-
-
-
-
-
-
-
-
+
+
+
`@liveblocks/react-tiptap` provides [React](https://reactjs.org/) APIs to
diff --git a/packages/liveblocks-react-ui/README.md b/packages/liveblocks-react-ui/README.md
index 2b1231e063a..cc4fa8e3281 100644
--- a/packages/liveblocks-react-ui/README.md
+++ b/packages/liveblocks-react-ui/README.md
@@ -1,24 +1,14 @@
-
-
-
-
-
-
-
+
+
+
# `@liveblocks/react-ui`
-
-
-
-
-
-
-
-
-
+
+
+
`@liveblocks/react-ui` provides [React](https://reactjs.org/) pre-built
diff --git a/packages/liveblocks-react/README.md b/packages/liveblocks-react/README.md
index 07584c44eda..5b72ce31c72 100644
--- a/packages/liveblocks-react/README.md
+++ b/packages/liveblocks-react/README.md
@@ -1,24 +1,14 @@
-
-
-
-
-
-
-
+
+
+
# `@liveblocks/react`
-
-
-
-
-
-
-
-
-
+
+
+
`@liveblocks/react` provides [React](https://reactjs.org/) hooks and providers
diff --git a/packages/liveblocks-redux/README.md b/packages/liveblocks-redux/README.md
index 971045dca45..b5f1e6c9315 100644
--- a/packages/liveblocks-redux/README.md
+++ b/packages/liveblocks-redux/README.md
@@ -1,24 +1,14 @@
-
-
-
-
-
-
-
+
+
+
# `@liveblocks/redux`
-
-
-
-
-
-
-
-
-
+
+
+
`@liveblocks/redux` provides a
diff --git a/packages/liveblocks-server/README.md b/packages/liveblocks-server/README.md
index 0ecc195f2f2..4b4436163d7 100644
--- a/packages/liveblocks-server/README.md
+++ b/packages/liveblocks-server/README.md
@@ -1,24 +1,14 @@
-
-
-
-
-
-
+
+
# `@liveblocks/server`
-
-
-
-
-
-
-
-
-
+
+
+
`@liveblocks/server` provides the core Liveblocks server functionality. It
diff --git a/packages/liveblocks-yjs/README.md b/packages/liveblocks-yjs/README.md
index 4a945171283..bcb76f6bfeb 100644
--- a/packages/liveblocks-yjs/README.md
+++ b/packages/liveblocks-yjs/README.md
@@ -1,14 +1,16 @@
-
-
-
-
-
-
-
+
+
+
# `@liveblocks/yjs`
+
+
+
+
+
+
`@liveblocks/yjs` is a Yjs provider to integrate
[Yjs](https://github.com/yjs/yjs) applications with Liveblocks—a platform to
build, host, and scale collaborative applications with zero configuration, no
diff --git a/packages/liveblocks-zenrouter/README.md b/packages/liveblocks-zenrouter/README.md
index 3b2923e60da..c4caaebd5d3 100644
--- a/packages/liveblocks-zenrouter/README.md
+++ b/packages/liveblocks-zenrouter/README.md
@@ -1,24 +1,14 @@
-
-
-
-
-
-
-
+
+
+
# `@liveblocks/zenrouter`
-
-
-
-
-
-
-
-
-
+
+
+
Zen Router is an opinionated API router with batteries included, encouraging
@@ -40,27 +30,25 @@ The main purpose of this router is to implement an API backend.
import { object, string } from "decoders";
import { Router } from "@liveblocks/zenrouter";
-const app = new Router(/* ... */);
+const zen = new Router(/* ... */);
-app.route(
+zen.route(
"GET /greet/",
({ p }) => ({ result: `Hi, ${p.name}!` })
);
-app.route(
+zen.route(
"POST /greet",
- object({
- name: string,
- }),
+ object({ name: string }),
({ body }) => ({
result: `Hi, ${body.name}!`,
})
);
-export default app;
+export default zen;
```
## The Zen Router pipeline
@@ -69,60 +57,40 @@ export default app;
## Principles
-General principles:
-
-- It should be hard to make a mistake in your router setup.
-- It should be hard to forget something that will bite you later.
-- Secure by default.
-
-Pragmatic:
-
-- Implementing real world endpoints should be joyful, easy, and type-safe.
-- All requests are JSON (by default)
-- All responses are JSON arrays or objects (by default)
-- All error responses will have at least an `{ error }` key with a
- human-readable string in there
-- You can _throw_ any HTTP error or other exception to short-circuit a non-2xx
- response.
-- Will return JSON error responses by default for all known HTTP errors. Can be
- customized.
-- CORS support is built-in, and can be enabled with a simple `{ cors: true }`
- that is a sane default for most cases.
-
-Secure/sane by default:
-
-- Will automatically manage OPTIONS routes and responses
-- All requests must be authorized. Authorization is not opt-in, but opt-out.
-- All params are verified: `/foo//` (strings by default, or possibly
- decoded, always type-safe, available as `p.bar` and `p.qux`)
-- All query strings are type-safely accessible: `/foo?abc=hi` accessible as `q`
- will be `{ abc: 'hi' }`
-- JSON bodies of POST requests will be decoded using a per-request decoder
-- Path params cannot be "empty" strings nor can be optional
-- All routes params are URI-decoded by default, this is not left to userland.
-- CORS can simply be enabled on a Router instance out of the box, following the
- simple philosophy that when you want to enable CORS, you wish to enable it for
- all endpoints in that router.
-
-Maintainability:
-
-- All route patterns are static, and fully qualified, and thus "greppable". This
- keeps them readable and unambigious over time.
-- In particular, this means it won't allow for a "base" prefix URL setup, which
- is often used in other router libraries to DRY up your route strings, but in
- practice this makes the code base harder to user over time (because different
- subrouters can have what seems to be the same route pattern).
-- Routes are registered not just by path, but also by method, as part of the
- definition. This will make the routes much more readable and obvious, e.g.
- `app.route("POST /v2/foo/bar")` instead of `app.post("/v2/foo/bar")`.
-- No complex middlewares. There is just the request context and auth functions.
- Those are the only two places where "middleware" can live. No per-route
- middlewares.
-- No monkey-patching of the request. The request context is the user-defined
- place to carry data alongside the request.
-- Default error handling can be configured on a per-status code basis (used when
- handlers throw a (custom) HttpError), individual requests can always bypass
- this by throwing a custom Response.
+### Pragmatic
+
+- Implementing real-world endpoints should be joyful, easy, and type-safe.
+- All requests and responses are JSON by default.
+- All error responses have at least an `{ error }` key with a human-readable
+ string.
+- You can _throw_ any HTTP error to short-circuit a non-2xx response.
+- JSON error responses for all known HTTP status codes, customizable per status
+ code.
+- CORS support is built-in with a sane `{ cors: true }` default that applies to
+ all endpoints in the router. `OPTIONS` routes and responses are managed
+ automatically.
+
+### Secure by default
+
+- All requests must be authorized. Authorization is opt-out, not opt-in.
+- All path params are verified and type-safe (`/foo//` available as
+ `p.bar` and `p.qux`), cannot be empty, and are URI-decoded automatically.
+- Input JSON bodies of POST requests must be validated, and are made available
+ as a fully-type safe `body` in the handler.
+- All query strings are type-safely accessible (`/foo?abc=hi` as `q.abc`).
+
+### Maintainable
+
+- All route patterns are static, fully qualified, and thus greppable. No "base"
+ prefix URL setup, which in practice makes codebases harder to navigate over
+ time.
+- Routes include the method in the definition (`zen.route("POST /v2/foo/bar")`
+ instead of `zen.post("/v2/foo/bar")`).
+- No complex middlewares. Only the request context and auth functions can carry
+ data alongside a request. No per-route middlewares, no monkey-patching of the
+ request object.
+- Default error handling is configurable per status code; individual handlers
+ can always bypass it by throwing a custom Response.
## License
diff --git a/packages/liveblocks-zenrouter/src/Relay.ts b/packages/liveblocks-zenrouter/src/Relay.ts
index e2a2f5ed44d..27b6e8f2eef 100644
--- a/packages/liveblocks-zenrouter/src/Relay.ts
+++ b/packages/liveblocks-zenrouter/src/Relay.ts
@@ -33,8 +33,8 @@ type RelayOptions = {
* If no matching route is found, it will return a generic 404 error response.
*/
export class ZenRelay {
- #_errorHandler: ErrorHandler;
- #_routers: [prefixMatcher: RegExp, handler: RequestHandler][] = [];
+ readonly #_errorHandler: ErrorHandler;
+ readonly #_routers: [prefixMatcher: RegExp, handler: RequestHandler][] = [];
constructor(options?: RelayOptions) {
this.#_errorHandler = options?.errorHandler ?? new ErrorHandler();
diff --git a/packages/liveblocks-zenrouter/src/Router.ts b/packages/liveblocks-zenrouter/src/Router.ts
index ca078f780c5..211517dfe49 100644
--- a/packages/liveblocks-zenrouter/src/Router.ts
+++ b/packages/liveblocks-zenrouter/src/Router.ts
@@ -153,14 +153,14 @@ export class ZenRouter<
AC,
TParams extends Record = {},
> {
- #_debug: boolean;
- #_contextFn: (req: Request, ...args: readonly any[]) => RC;
- #_defaultAuthFn: AuthFn;
- #_routes: RouteTuple[];
- #_paramDecoders: TParams;
- #_errorHandler: ErrorHandler;
- #_cors: Partial | null;
- #_otel: OtelConfig | undefined;
+ readonly #_debug: boolean;
+ readonly #_contextFn: (req: Request, ...args: readonly any[]) => RC;
+ readonly #_defaultAuthFn: AuthFn;
+ readonly #_routes: RouteTuple[];
+ readonly #_paramDecoders: TParams;
+ readonly #_errorHandler: ErrorHandler;
+ readonly #_cors: Partial | null;
+ readonly #_otel: OtelConfig | undefined;
constructor(options?: RouterOptions) {
this.#_errorHandler = options?.errorHandler ?? new ErrorHandler();
diff --git a/packages/liveblocks-zustand/README.md b/packages/liveblocks-zustand/README.md
index 38cade6326d..78461219a9f 100644
--- a/packages/liveblocks-zustand/README.md
+++ b/packages/liveblocks-zustand/README.md
@@ -1,24 +1,14 @@
-
-
-
-
-
-
-
+
+
+
# `@liveblocks/zustand`
-
-
-
-
-
-
-
-
-
+
+
+
`@liveblocks/zustand` provides a