Skip to content

Commit a00b8aa

Browse files
authored
refactor worker entry, kv, general module structure (#418)
1 parent 8ccf0e5 commit a00b8aa

97 files changed

Lines changed: 551 additions & 1440 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/three-cats-decide.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"effect-workerd": patch
3+
"liminal-util": patch
4+
"liminal": patch
5+
---
6+
7+
Move workerd-specifics into dedicated package.

.github/workflows/deploy-docs.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Deploy liminal.actor
1+
name: Deploy Docs
22

33
on:
44
workflow_dispatch:
@@ -12,7 +12,7 @@ concurrency:
1212
group: ${{ github.workflow }}
1313

1414
jobs:
15-
deploy:
15+
deploy-docs:
1616
runs-on: ubuntu-latest
1717
environment: docs
1818
steps:

actions/sync-env/action.yml

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

docs/pages/accumulator.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ An accumulator is a tagged service parameterized by its state type.
1111

1212
```ts
1313
import { Stream } from "effect"
14-
import * as Accumulator from "liminal/util/Accumulator"
14+
import { Accumulator } from "liminal"
1515

1616
import { TicTacToeClient, Player } from "./TicTacToeClient.ts"
1717

docs/pages/actors-handlers-and-lifecycle.mdx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ An actor ties together three things:
1818

1919
```ts
2020
import { Schema as S } from "effect"
21-
import { Actor } from "liminal/actor"
21+
import { Actor } from "liminal"
2222

2323
import { Player, TicTacToeClient } from "./TicTacToeClient.ts"
2424

@@ -190,13 +190,13 @@ That is useful for values like:
190190
- the caller's role in a game
191191
- per-socket cursors or last-seen sequence numbers
192192

193-
Do not use attachments as whole-actor storage. Persist shared state elsewhere (D1, Durable Object storage, Postgres via
194-
Hyperdrive, and so on).
193+
Do not use attachments as whole-actor storage. Persist shared state elsewhere — for example, through Effect's
194+
`KeyValueStore` interface backed by an R2 bucket.
195195

196196
## Use `runLayer` for request-local services
197197

198198
Many handlers want convenient access to values derived from actor context.
199199

200-
Use `runLayer` (on the Cloudflare `ActorRegistry`) to derive services like `CurrentUserId` or `Authorization` from the
201-
actor name and current client attachments. See [Cloudflare Registry and Routing](./cloudflare/registry-and-routing.mdx)
202-
for the full pattern.
200+
Use `layer` (on the Cloudflare `WorkerdActorNamespace`) to derive services like `CurrentUserId` or `Authorization` from
201+
the actor name and current client attachments. See [Cloudflare Actor Namespace](./cloudflare/actor-namespace.mdx) for
202+
the full pattern.

docs/pages/audition.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ example, from a lobby into a chat room).
88
## Merge multiple clients with `Audition`
99

1010
```ts
11-
import { Audition } from "liminal/actor"
11+
import { Audition } from "liminal"
1212

1313
import { ChatClient } from "./chat/ChatClient.ts"
1414
import { LobbyClient } from "./lobby/LobbyClient.ts"

docs/pages/clients.mdx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ A client is just a named set of methods and events.
1414

1515
```ts
1616
import { Schema as S } from "effect"
17-
import { Client } from "liminal/actor"
17+
import { Client } from "liminal"
1818

1919
export const Player = S.Literals(["X", "O"])
2020
export const Coordinate = S.Literals([0, 1, 2])
@@ -57,7 +57,7 @@ For anything beyond a toy example, you may want to define methods separately.
5757

5858
```ts
5959
import { Schema as S } from "effect"
60-
import { Method } from "liminal/actor"
60+
import { Method } from "liminal"
6161

6262
export const SendMessage = Method.make({
6363
payload: S.Struct({
@@ -80,7 +80,7 @@ If a method is shared, its handler can be shared too.
8080

8181
```ts
8282
import { Effect } from "effect"
83-
import { Method } from "liminal/actor"
83+
import { Method } from "liminal"
8484

8585
import { UpdateProfile } from "../shared/methods/UpdateProfile.ts"
8686

@@ -157,7 +157,7 @@ Liminal ships two client transports:
157157
- `Client.layerWorker(...)` for workers or other `MessagePort` transports
158158

159159
```ts
160-
import { Client } from "liminal/actor"
160+
import { Client } from "liminal"
161161

162162
const TicTacToeClientLive = Client.layerSocket({
163163
client: TicTacToeClient,

docs/pages/cloudflare/registry-and-routing.mdx renamed to docs/pages/cloudflare/actor-namespace.mdx

Lines changed: 41 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,51 @@
1-
# Registry and Routing
1+
# Cloudflare Actor Namespace
22

3-
`liminal/actor` defines the actor model. `liminal` mounts that model onto Cloudflare Workers and Durable Objects.
3+
`liminal` defines the actor model. `liminal/workerd` mounts that model onto Cloudflare Workers and Durable Objects;
4+
`effect-workerd` provides the Workerd-side glue (`Worker.make`, `Assets.forward`, and so on).
45

56
This guide covers:
67

7-
- `ActorRegistry.Service(...)`
8-
- `Worker.make(...)`
8+
- `WorkerdActorNamespace.Service(...)` — wraps an actor as a Durable Object class.
9+
- `Worker.make(...)` — produces the Worker `fetch` entrypoint.
910

10-
## Define a registry with `ActorRegistry.Service(...)`
11+
## Define a namespace with `WorkerdActorNamespace.Service(...)`
1112

12-
An actor registry wraps one actor type as one Durable Object class.
13+
A namespace wraps one actor type as one Durable Object class.
1314

1415
```ts
15-
import { ActorRegistry } from "liminal/actor"
16+
import { WorkerdActorNamespace } from "liminal/workerd"
1617

1718
import * as CurrentUserId from "../context/CurrentUserId.ts"
1819
import { PreludeLive } from "../PreludeLive.ts"
1920
import { ChatActor } from "./ChatActor.ts"
2021
import * as handlers from "./handlers/handlers.ts"
2122
import { onConnect } from "./lifecycle/onConnect.ts"
2223

23-
export class ChatRegistry extends ActorRegistry.Service<ChatRegistry>()("ChatRegistry", {
24+
export class ChatNamespace extends WorkerdActorNamespace.Service<ChatNamespace>()("ChatNamespace", {
2425
actor: ChatActor,
2526
prelude: PreludeLive,
26-
runLayer: CurrentUserId.layerChat,
27+
layer: CurrentUserId.layerChat,
2728
onConnect,
2829
handlers,
2930
}) {}
3031
```
3132

32-
## What each registry field means
33+
## What each namespace field means
3334

3435
- `actor`: the Liminal actor definition
3536
- `prelude`: long-lived runtime dependencies for the Durable Object instance
36-
- `runLayer`: short-lived, request-local dependencies derived from actor context
37+
- `layer`: short-lived, request-local dependencies derived from actor context
3738
- `onConnect`: logic that runs for newly upgraded sockets
3839
- `handlers`: the method implementation table
3940
- `hibernation`: optional hibernatable WebSocket timeout (`Duration.Input`)
4041

4142
The Durable Object binding name is not part of the definition — it is supplied later when you call
42-
`ChatRegistry.layer("CHAT_ROOMS")` to provide the registry to your Worker runtime.
43+
`ChatNamespace.layer("CHAT_ROOMS")` to provide the namespace to your Worker runtime.
4344

44-
In practice, `runLayer` is where you derive conveniences like `CurrentUserId` or `Authorization` from the actor name and
45+
In practice, `layer` is where you derive conveniences like `CurrentUserId` or `Authorization` from the actor name and
4546
current client attachments.
4647

47-
## `prelude` versus `runLayer`
48+
## `prelude` versus `layer`
4849

4950
This split is one of the most important concepts to internalize.
5051

@@ -56,13 +57,13 @@ Use `prelude` for long-lived infrastructure:
5657
- asset bindings
5758
- Cloudflare service bindings
5859

59-
Use `runLayer` for short-lived context derived from the current client-specific actor invocation:
60+
Use `layer` for short-lived context derived from the current client-specific actor invocation:
6061

6162
- current user id
6263
- current authorization
6364

64-
The next example shows how to build a `runLayer` that provides an actor-specific `CurrentUserId` derived from two
65-
different actor definitions.
65+
The next example builds a `layer` that provides an actor-specific `CurrentUserId` derived from two different actor
66+
definitions.
6667

6768
```ts
6869
import { Context, Effect, Layer, Schema as S } from "effect"
@@ -93,14 +94,14 @@ export const layerChat = Effect.gen(function* () {
9394
9495
## Upgrade into an actor from HTTP
9596

96-
`Registry.upgrade(name, attachments)` is the handoff from HTTP to the Durable Object.
97+
`Namespace.upgrade(name, attachments)` is the handoff from HTTP to the Durable Object.
9798

9899
```ts
99100
import { Effect, Layer } from "effect"
100101
import { HttpRouter, HttpServerResponse } from "effect/unstable/http"
101102

102-
import { ChatRegistry } from "./chat/ChatRegistry.ts"
103-
import { LobbyRegistry } from "./lobby/LobbyRegistry.ts"
103+
import { ChatNamespace } from "./chat/ChatNamespace.ts"
104+
import { LobbyNamespace } from "./lobby/LobbyNamespace.ts"
104105

105106
export const ApiLive = Layer.mergeAll(
106107
HttpRouter.add(
@@ -112,11 +113,11 @@ export const ApiLive = Layer.mergeAll(
112113

113114
if (user) {
114115
const roomId = yield* getActiveRoom(user.id)
115-
return yield* ChatRegistry.upgrade(roomId, { userId: user.id })
116+
return yield* ChatNamespace.upgrade(roomId, { userId: user.id })
116117
}
117118

118-
return yield* LobbyRegistry.upgrade(sessionToken, {})
119-
}).pipe(Effect.orDie),
119+
return yield* LobbyNamespace.upgrade(sessionToken, {})
120+
}),
120121
),
121122
)
122123
```
@@ -150,64 +151,55 @@ The Worker entrypoint usually exports:
150151

151152
```ts
152153
import { Effect, Layer } from "effect"
153-
import { HttpRouter, HttpServer, HttpServerResponse } from "effect/unstable/http"
154-
import { Worker } from "liminal"
154+
import { Worker } from "effect-workerd"
155+
import { HttpRouter } from "effect/unstable/http"
155156

156157
import { ApiLive } from "./ApiLive.ts"
157-
import { ChatRegistry } from "./chat/ChatRegistry.ts"
158-
import { LobbyRegistry } from "./lobby/LobbyRegistry.ts"
158+
import { ChatNamespace } from "./chat/ChatNamespace.ts"
159+
import { LobbyNamespace } from "./lobby/LobbyNamespace.ts"
159160
import { PreludeLive } from "./PreludeLive.ts"
160161

161-
export { ChatRegistry, LobbyRegistry }
162+
export { ChatNamespace, LobbyNamespace }
162163

163164
export default Worker.make({
164-
handler: ApiLive.pipe(
165-
Layer.provide(HttpServer.layerServices),
166-
HttpRouter.toHttpEffect,
167-
Effect.flatMap((handler) => handler),
168-
Effect.provide(Layer.mergeAll(ChatRegistry.layer("CHAT_ROOMS"), LobbyRegistry.layer("LOBBY"), PreludeLive)),
169-
Effect.catchCause(() => Effect.succeed(HttpServerResponse.empty({ status: 500 }))),
170-
),
171-
prelude: Layer.empty,
165+
handler: ApiLive.pipe(HttpRouter.toHttpEffect, Effect.flatten),
166+
prelude: Layer.mergeAll(PreludeLive, ChatNamespace.layer("CHAT_ROOMS"), LobbyNamespace.layer("LOBBY")),
172167
})
173168
```
174169

175-
Each registry's `.layer(binding)` is what ties it to a specific Durable Object binding in Wrangler. The binding name
176-
lives at the layer call site rather than in the registry definition, which makes it easy to mount the same registry
170+
Each namespace's `.layer(binding)` is what ties it to a specific Durable Object binding in Wrangler. The binding name
171+
lives at the layer call site rather than in the namespace definition, which makes it easy to mount the same namespace
177172
under different bindings.
178173

179-
`Worker.make(...)` supplies the Worker-side runtime for ordinary HTTP handling. `ActorRegistry` separately manages the
180-
Durable Object runtime for actor messages. See [Worker](./worker.mdx) for more on `Worker.make(...)`.
181-
182-
Both sides can share the same prelude layer — either by threading `PreludeLive` through the registry's `prelude` and
183-
providing it here, or by passing it to `Worker.make(...)` as its `prelude`.
174+
`Worker.make(...)` supplies the Worker-side runtime for ordinary HTTP handling. `WorkerdActorNamespace` separately
175+
manages the Durable Object runtime for actor messages.
184176

185177
## Wrangler alignment matters
186178

187-
The registry binding, named export, and Wrangler config must line up.
179+
The namespace binding, named export, and Wrangler config must line up.
188180

189181
```jsonc
190182
{
191183
"durable_objects": {
192184
"bindings": [
193185
{
194186
"name": "CHAT_ROOMS",
195-
"class_name": "ChatRegistry",
187+
"class_name": "ChatNamespace",
196188
},
197189
],
198190
},
199191
"migrations": [
200192
{
201193
"tag": "v1",
202-
"new_classes": ["ChatRegistry"],
194+
"new_classes": ["ChatNamespace"],
203195
},
204196
],
205197
}
206198
```
207199

208200
The important correspondence is:
209201

210-
- `ChatRegistry.layer("CHAT_ROOMS")` in the Worker runtime
202+
- `ChatNamespace.layer("CHAT_ROOMS")` in the Worker runtime
211203
- `name: "CHAT_ROOMS"` in Wrangler
212-
- `export { ChatRegistry }` in the Worker entrypoint
213-
- `class_name: "ChatRegistry"` in Wrangler
204+
- `export { ChatNamespace }` in the Worker entrypoint
205+
- `class_name: "ChatNamespace"` in Wrangler

docs/pages/cloudflare/ai.mdx

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

0 commit comments

Comments
 (0)