liminal 0.17.15 → 0.17.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Actor.ts +22 -33
- package/{workerd/ActorHandle.ts → ActorHandle.ts} +7 -2
- package/{workerd/WorkerdActorNamespace.ts → ActorNamespace.ts} +46 -32
- package/{workerd/WorkerdActorRuntime.ts → ActorRuntime.ts} +71 -44
- package/ActorTransport.ts +2 -2
- package/Audition.ts +1 -1
- package/BrowserActorNamespace.ts +257 -0
- package/CHANGELOG.md +28 -0
- package/Client.ts +127 -76
- package/ClientDirectory.ts +40 -32
- package/ClientHandle.ts +9 -7
- package/Fn.ts +39 -0
- package/Tracing.ts +11 -3
- package/dist/Actor.d.ts +3 -5
- package/dist/Actor.js +5 -9
- package/dist/Actor.js.map +1 -1
- package/dist/{workerd/ActorHandle.d.ts → ActorHandle.d.ts} +6 -3
- package/dist/ActorHandle.js.map +1 -0
- package/dist/{workerd/WorkerdActorNamespace.d.ts → ActorNamespace.d.ts} +12 -10
- package/dist/{workerd/WorkerdActorNamespace.js → ActorNamespace.js} +16 -10
- package/dist/ActorNamespace.js.map +1 -0
- package/dist/{workerd/WorkerdActorRuntime.d.ts → ActorRuntime.d.ts} +10 -9
- package/dist/{workerd/WorkerdActorRuntime.js → ActorRuntime.js} +40 -34
- package/dist/ActorRuntime.js.map +1 -0
- package/dist/ActorTransport.d.ts +2 -2
- package/dist/Audition.js +1 -1
- package/dist/Audition.js.map +1 -1
- package/dist/BrowserActorNamespace.d.ts +39 -0
- package/dist/BrowserActorNamespace.js +134 -0
- package/dist/BrowserActorNamespace.js.map +1 -0
- package/dist/Client.d.ts +7 -4
- package/dist/Client.js +78 -48
- package/dist/Client.js.map +1 -1
- package/dist/ClientDirectory.d.ts +1 -1
- package/dist/ClientDirectory.js +11 -5
- package/dist/ClientDirectory.js.map +1 -1
- package/dist/ClientHandle.d.ts +5 -4
- package/dist/Fn.d.ts +8 -0
- package/dist/Tracing.js +6 -2
- package/dist/Tracing.js.map +1 -1
- package/dist/experimental/L/append.js +1 -1
- package/dist/experimental/L/append.js.map +1 -1
- package/dist/experimental/L/history.js +1 -1
- package/dist/experimental/L/history.js.map +1 -1
- package/dist/index.common.d.ts +12 -0
- package/dist/index.common.js +13 -0
- package/dist/index.common.js.map +1 -0
- package/dist/index.d.ts +4 -11
- package/dist/index.js +4 -11
- package/dist/index.js.map +1 -1
- package/dist/index.non-workerd.d.ts +1 -0
- package/dist/index.non-workerd.js +2 -0
- package/dist/index.non-workerd.js.map +1 -0
- package/dist/package.json +13 -7
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/experimental/L/append.ts +1 -1
- package/experimental/L/history.ts +1 -1
- package/index.common.ts +12 -0
- package/index.non-workerd.ts +1 -0
- package/index.ts +4 -11
- package/package.json +12 -9
- package/_util/schema.ts +0 -7
- package/browser/BrowserActorNamespace.ts +0 -248
- package/browser/index.ts +0 -1
- package/dist/_util/schema.d.ts +0 -4
- package/dist/_util/schema.js +0 -5
- package/dist/_util/schema.js.map +0 -1
- package/dist/browser/BrowserActorNamespace.d.ts +0 -16
- package/dist/browser/BrowserActorNamespace.js +0 -133
- package/dist/browser/BrowserActorNamespace.js.map +0 -1
- package/dist/browser/index.d.ts +0 -1
- package/dist/browser/index.js +0 -2
- package/dist/browser/index.js.map +0 -1
- package/dist/workerd/ActorHandle.js.map +0 -1
- package/dist/workerd/WorkerdActorNamespace.js.map +0 -1
- package/dist/workerd/WorkerdActorRuntime.js.map +0 -1
- package/dist/workerd/index.d.ts +0 -3
- package/dist/workerd/index.js +0 -4
- package/dist/workerd/index.js.map +0 -1
- package/workerd/index.ts +0 -3
- /package/dist/{workerd/ActorHandle.js → ActorHandle.js} +0 -0
package/Actor.ts
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { Context, Schema as S, Effect } from "effect"
|
|
2
|
-
import * as
|
|
2
|
+
import * as Boundary from "liminal-util/Boundary"
|
|
3
|
+
import type { TopFromString } from "liminal-util/schema"
|
|
3
4
|
|
|
4
|
-
import type { TopFromString } from "./_util/schema.ts"
|
|
5
5
|
import type * as ActorClient from "./Client.ts"
|
|
6
6
|
import type { ClientHandle, Sender } from "./ClientHandle.ts"
|
|
7
|
-
import * as Method from "./Method.ts"
|
|
8
7
|
import { type ProtocolDefinition } from "./Protocol.ts"
|
|
9
8
|
|
|
10
|
-
const span = Spanner.make(import.meta.url)
|
|
11
|
-
|
|
12
9
|
export const TypeId = "~liminal/Actor" as const
|
|
13
10
|
|
|
14
11
|
export interface Service<
|
|
@@ -53,14 +50,9 @@ export interface Actor<
|
|
|
53
50
|
|
|
54
51
|
readonly definition: ActorDefinition<Name, AttachmentFields, ActorClientSelf, ActorClientId, D>
|
|
55
52
|
|
|
56
|
-
readonly all: Sender<
|
|
57
|
-
|
|
58
|
-
readonly others: Sender<ActorSelf, D>
|
|
53
|
+
readonly all: Sender<D, ActorSelf>
|
|
59
54
|
|
|
60
|
-
readonly
|
|
61
|
-
tag: K,
|
|
62
|
-
f: Method.Handler<D["external"][K], R>,
|
|
63
|
-
) => Method.Handler<D["external"][K], R>
|
|
55
|
+
readonly others: Sender<D, ActorSelf>
|
|
64
56
|
}
|
|
65
57
|
|
|
66
58
|
export const Service =
|
|
@@ -78,45 +70,42 @@ export const Service =
|
|
|
78
70
|
): Actor<ActorSelf, ActorId, Name, AttachmentFields, ClientSelf, ClientId, D> => {
|
|
79
71
|
const tag = Context.Service<ActorSelf, Service<ActorSelf, Name, AttachmentFields, D>>()(id)
|
|
80
72
|
|
|
81
|
-
const all: Sender<
|
|
73
|
+
const all: Sender<D, ActorSelf> = {
|
|
82
74
|
send: (key, payload) =>
|
|
83
|
-
tag.
|
|
75
|
+
tag.pipe(
|
|
84
76
|
Effect.flatMap(({ clients }) =>
|
|
85
77
|
Effect.forEach(clients, (client) => client.send(key, payload), { concurrency: "unbounded" }),
|
|
86
78
|
),
|
|
87
|
-
span("send-all"),
|
|
79
|
+
Boundary.span("send-all", import.meta.url),
|
|
88
80
|
),
|
|
89
|
-
disconnect: tag.
|
|
81
|
+
disconnect: tag.pipe(
|
|
90
82
|
Effect.flatMap(({ clients }) => Effect.forEach(clients, ({ disconnect }) => disconnect)),
|
|
91
|
-
span("disconnect-all"),
|
|
83
|
+
Boundary.span("disconnect-all", import.meta.url),
|
|
92
84
|
),
|
|
93
85
|
}
|
|
94
86
|
|
|
95
|
-
const others: Sender<
|
|
96
|
-
send: Effect.fnUntraced(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
87
|
+
const others: Sender<D, ActorSelf> = {
|
|
88
|
+
send: Effect.fnUntraced(
|
|
89
|
+
function* (key, payload) {
|
|
90
|
+
const { clients, currentClient } = yield* tag
|
|
91
|
+
yield* Effect.forEach(
|
|
92
|
+
clients,
|
|
93
|
+
(client) => (client === currentClient ? Effect.void : client.send(key, payload)),
|
|
94
|
+
{ concurrency: "unbounded" },
|
|
95
|
+
)
|
|
96
|
+
},
|
|
97
|
+
Boundary.span("send-others", import.meta.url),
|
|
98
|
+
),
|
|
104
99
|
disconnect: Effect.gen(function* () {
|
|
105
100
|
const { clients, currentClient } = yield* tag
|
|
106
101
|
yield* Effect.forEach(clients, (client) => (client === currentClient ? Effect.void : client.disconnect))
|
|
107
|
-
}).pipe(span("disconnect-others")),
|
|
102
|
+
}).pipe(Boundary.span("disconnect-others", import.meta.url)),
|
|
108
103
|
}
|
|
109
104
|
|
|
110
|
-
const handler = <K extends keyof D["external"], R>(
|
|
111
|
-
_tag: K,
|
|
112
|
-
f: Method.Handler<D["external"][K], R>,
|
|
113
|
-
): Method.Handler<D["external"][K], R> => f
|
|
114
|
-
|
|
115
105
|
return Object.assign(tag, {
|
|
116
106
|
[TypeId]: TypeId,
|
|
117
107
|
definition,
|
|
118
108
|
all,
|
|
119
109
|
others,
|
|
120
|
-
handler,
|
|
121
110
|
})
|
|
122
111
|
}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { Schema as S, Effect, Cause, Encoding } from "effect"
|
|
2
2
|
import { NativeRequest } from "effect-workerd"
|
|
3
3
|
import { HttpServerResponse } from "effect/unstable/http"
|
|
4
|
+
import type { TopFromString } from "liminal-util/schema"
|
|
4
5
|
|
|
5
|
-
import type {
|
|
6
|
-
import type { Methods } from "
|
|
6
|
+
import type { Send } from "./ClientHandle.ts"
|
|
7
|
+
import type { Methods } from "./Method.ts"
|
|
8
|
+
import type { ProtocolDefinition } from "./Protocol.ts"
|
|
7
9
|
|
|
8
10
|
export interface ActorHandle<
|
|
9
11
|
NamespaceSelf,
|
|
10
12
|
Internal extends Methods,
|
|
11
13
|
Name extends TopFromString,
|
|
12
14
|
AttachmentFields extends S.Struct.Fields,
|
|
15
|
+
D extends ProtocolDefinition,
|
|
13
16
|
> {
|
|
14
17
|
readonly upgrade: (
|
|
15
18
|
attachments: S.Struct<AttachmentFields>["Type"],
|
|
@@ -26,4 +29,6 @@ export interface ActorHandle<
|
|
|
26
29
|
method: K,
|
|
27
30
|
payload: M["payload"]["Type"],
|
|
28
31
|
) => Effect.Effect<M["success"]["Type"], M["failure"]["Type"], NamespaceSelf>
|
|
32
|
+
|
|
33
|
+
readonly proxySendAll: Send<D, NamespaceSelf>
|
|
29
34
|
}
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { Layer, Effect, Schema as S, Context, flow, String, Array, Encoding, Exit } from "effect"
|
|
2
|
-
import { Binding, NativeRequest } from "effect-workerd"
|
|
2
|
+
import { Binding, Env, NativeRequest } from "effect-workerd"
|
|
3
3
|
import { SecWebSocketProtocol, close } from "effect-workerd/socket_util"
|
|
4
4
|
import { HttpServerResponse, HttpTraceContext } from "effect/unstable/http"
|
|
5
|
-
import * as
|
|
5
|
+
import * as Boundary from "liminal-util/Boundary"
|
|
6
|
+
import { type TopFromString, encodeJsonString } from "liminal-util/schema"
|
|
6
7
|
|
|
7
|
-
import
|
|
8
|
-
import type { Actor } from "../Actor.ts"
|
|
9
|
-
import type { Methods } from "../Method.ts"
|
|
10
|
-
import type { ProtocolDefinition } from "../Protocol.ts"
|
|
8
|
+
import type { Actor } from "./Actor.ts"
|
|
11
9
|
import type { ActorHandle } from "./ActorHandle.ts"
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
import type { Methods } from "./Method.ts"
|
|
11
|
+
import type { ProtocolDefinition } from "./Protocol.ts"
|
|
14
12
|
|
|
15
13
|
export interface ActorNamespaceDefinition<
|
|
16
14
|
Internal extends Methods,
|
|
@@ -45,7 +43,7 @@ export interface ActorNamespace<
|
|
|
45
43
|
_: never,
|
|
46
44
|
): Context.ServiceClass.Shape<
|
|
47
45
|
NamespaceId,
|
|
48
|
-
DurableObjectNamespace<Rpc.DurableObjectBranded &
|
|
46
|
+
DurableObjectNamespace<Rpc.DurableObjectBranded & ActorNamespace.MakeRpc<Internal, D>>
|
|
49
47
|
>
|
|
50
48
|
|
|
51
49
|
readonly definition: ActorNamespaceDefinition<
|
|
@@ -59,17 +57,19 @@ export interface ActorNamespace<
|
|
|
59
57
|
D
|
|
60
58
|
>
|
|
61
59
|
|
|
62
|
-
readonly bind: (name: Name["Type"]) => ActorHandle<NamespaceSelf, Internal, Name, AttachmentFields>
|
|
60
|
+
readonly bind: (name: Name["Type"]) => ActorHandle<NamespaceSelf, Internal, Name, AttachmentFields, D>
|
|
63
61
|
|
|
64
|
-
readonly layer: Layer.Layer<NamespaceSelf, S.SchemaError,
|
|
62
|
+
readonly layer: Layer.Layer<NamespaceSelf, S.SchemaError, Env>
|
|
65
63
|
}
|
|
66
64
|
|
|
67
|
-
export declare namespace
|
|
68
|
-
export type MakeRpc<
|
|
69
|
-
rpc: <K extends keyof
|
|
65
|
+
export declare namespace ActorNamespace {
|
|
66
|
+
export type MakeRpc<Internal extends Methods, D extends ProtocolDefinition> = {
|
|
67
|
+
rpc: <K extends keyof Internal>(
|
|
70
68
|
method: K,
|
|
71
|
-
payload:
|
|
72
|
-
) => Promise<Exit.Exit<
|
|
69
|
+
payload: Internal[K]["payload"]["Type"],
|
|
70
|
+
) => Promise<Exit.Exit<Internal[K]["success"]["Type"], Internal[K]["failure"]["Type"]>>
|
|
71
|
+
|
|
72
|
+
proxySendAll<K extends keyof D["events"]>(event: K, payload: S.Struct<D["events"][K]>["Type"]): void
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
@@ -111,7 +111,7 @@ export const Service =
|
|
|
111
111
|
|
|
112
112
|
const tag = Context.Service<
|
|
113
113
|
NamespaceSelf,
|
|
114
|
-
DurableObjectNamespace<Rpc.DurableObjectBranded &
|
|
114
|
+
DurableObjectNamespace<Rpc.DurableObjectBranded & ActorNamespace.MakeRpc<Internal, D>>
|
|
115
115
|
>()(id)
|
|
116
116
|
|
|
117
117
|
const encodeName = S.encodeEffect(Name)
|
|
@@ -119,7 +119,7 @@ export const Service =
|
|
|
119
119
|
const encodeAttachmentsString = encodeJsonString(Attachments)
|
|
120
120
|
const encodeAuditionFailure = encodeJsonString(P.Audition.Failure)
|
|
121
121
|
|
|
122
|
-
const bind = (name: Name["Type"]): ActorHandle<NamespaceSelf, Internal, Name, AttachmentFields> => {
|
|
122
|
+
const bind = (name: Name["Type"]): ActorHandle<NamespaceSelf, Internal, Name, AttachmentFields, D> => {
|
|
123
123
|
const getStub = Effect.gen(function* () {
|
|
124
124
|
const namespace = yield* tag
|
|
125
125
|
const nameEncoded = yield* encodeName(name)
|
|
@@ -132,9 +132,10 @@ export const Service =
|
|
|
132
132
|
const protocols = yield* Effect.fromNullishOr(request.headers.get(SecWebSocketProtocol)).pipe(
|
|
133
133
|
Effect.map(flow(String.split(","), Array.map(String.trim))),
|
|
134
134
|
)
|
|
135
|
-
const liminalTokenI = yield* Array.findFirstIndex(protocols, (v) => v === "liminal")
|
|
136
|
-
const
|
|
137
|
-
|
|
135
|
+
const liminalTokenI = yield* Array.findFirstIndex(protocols, (v) => v === "liminal").pipe(Effect.fromOption)
|
|
136
|
+
const liminalClientId = yield* Effect.fromNullishOr(protocols[liminalTokenI + 1])
|
|
137
|
+
const requestClientId = yield* Effect.fromNullishOr(protocols[liminalTokenI + 2]).pipe(
|
|
138
|
+
Effect.flatMap((v) => Encoding.decodeBase64UrlString(v).pipe(Effect.fromResult)),
|
|
138
139
|
)
|
|
139
140
|
if (requestClientId !== clientId) {
|
|
140
141
|
return close(
|
|
@@ -147,6 +148,7 @@ export const Service =
|
|
|
147
148
|
}
|
|
148
149
|
const url = new URL(request.url)
|
|
149
150
|
url.searchParams.set("__liminal_attachments", yield* encodeAttachmentsString(attachments))
|
|
151
|
+
url.searchParams.set("__liminal_client_id", liminalClientId)
|
|
150
152
|
const actorRequest = new Request(url, request)
|
|
151
153
|
const traceHeaders = yield* Effect.currentSpan.pipe(Effect.map(HttpTraceContext.toHeaders))
|
|
152
154
|
for (const [key, value] of Object.entries(traceHeaders)) {
|
|
@@ -154,18 +156,30 @@ export const Service =
|
|
|
154
156
|
}
|
|
155
157
|
const stub = yield* getStub
|
|
156
158
|
return yield* Effect.promise(() => stub.fetch(actorRequest)).pipe(Effect.map(HttpServerResponse.raw))
|
|
157
|
-
}).pipe(span("upgrade", { kind: "client" }))
|
|
158
|
-
|
|
159
|
-
const call = Effect.fnUntraced(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
159
|
+
}).pipe(Boundary.span("upgrade", import.meta.url, { kind: "client" }))
|
|
160
|
+
|
|
161
|
+
const call = Effect.fnUntraced(
|
|
162
|
+
function* <K extends keyof Internal, M extends Internal[K]>(
|
|
163
|
+
method: K,
|
|
164
|
+
payload: M["payload"]["Type"],
|
|
165
|
+
): Effect.fn.Return<M["success"]["Type"], M["failure"]["Type"], NamespaceSelf> {
|
|
166
|
+
const stub = yield* getStub
|
|
167
|
+
const exit = yield* Effect.promise(() => stub.rpc(method as never, payload as never))
|
|
168
|
+
return yield* exit as any
|
|
169
|
+
},
|
|
170
|
+
Boundary.span("call", import.meta.url),
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
// TODO:
|
|
174
|
+
const proxySendAll = Effect.fnUntraced(
|
|
175
|
+
function* <K extends keyof D["events"]>(event: K, payload: S.Struct<D["events"][K]>["Type"]) {
|
|
176
|
+
const stub = yield* getStub
|
|
177
|
+
yield* Effect.promise(() => stub.proxySendAll(event as never, payload as never))
|
|
178
|
+
},
|
|
179
|
+
Boundary.span("proxySendAll", import.meta.url),
|
|
180
|
+
) as never
|
|
167
181
|
|
|
168
|
-
return { upgrade, call }
|
|
182
|
+
return { upgrade, call, proxySendAll }
|
|
169
183
|
}
|
|
170
184
|
|
|
171
185
|
const layer = Binding.layer(tag, ["idFromName", "idFromString", "newUniqueId", "get", "getByName"])(binding)
|
|
@@ -15,24 +15,21 @@ import {
|
|
|
15
15
|
Exit,
|
|
16
16
|
} from "effect"
|
|
17
17
|
import { DoState } from "effect-workerd"
|
|
18
|
+
import { Env } from "effect-workerd"
|
|
18
19
|
import { Clock } from "effect-workerd/platform"
|
|
19
20
|
import { SecWebSocketProtocol } from "effect-workerd/socket_util"
|
|
20
21
|
import { Headers, FetchHttpClient, HttpClient, HttpTraceContext } from "effect/unstable/http"
|
|
21
|
-
import
|
|
22
|
-
import {
|
|
23
|
-
import * as Spanner from "liminal-util/Spanner"
|
|
22
|
+
import * as Boundary from "liminal-util/Boundary"
|
|
23
|
+
import { type TopFromString, encodeJsonString, decodeJsonString } from "liminal-util/schema"
|
|
24
24
|
|
|
25
|
-
import
|
|
26
|
-
import type { ActorTransport } from "
|
|
27
|
-
import * as ClientDirectory from "
|
|
28
|
-
import type { ClientHandle } from "
|
|
29
|
-
import type { Handlers, Methods } from "
|
|
30
|
-
import type { ProtocolDefinition } from "
|
|
31
|
-
import * as Tracing from "
|
|
32
|
-
import { sessionAttributes, SessionId, sessionLink } from "
|
|
33
|
-
import type { ActorNamespace } from "./WorkerdActorNamespace.ts"
|
|
34
|
-
|
|
35
|
-
const span = Spanner.make(import.meta.url)
|
|
25
|
+
import type { ActorNamespace } from "./ActorNamespace.ts"
|
|
26
|
+
import type { ActorTransport } from "./ActorTransport.ts"
|
|
27
|
+
import * as ClientDirectory from "./ClientDirectory.ts"
|
|
28
|
+
import type { ClientHandle } from "./ClientHandle.ts"
|
|
29
|
+
import type { Handlers, Methods } from "./Method.ts"
|
|
30
|
+
import type { ProtocolDefinition } from "./Protocol.ts"
|
|
31
|
+
import * as Tracing from "./Tracing.ts"
|
|
32
|
+
import { sessionAttributes, SessionId, sessionLink } from "./Tracing.ts"
|
|
36
33
|
|
|
37
34
|
export interface ActorRuntimeDefinition<
|
|
38
35
|
NamespaceSelf,
|
|
@@ -76,25 +73,28 @@ export interface ActorRuntimeDefinition<
|
|
|
76
73
|
| Name["EncodingServices"]
|
|
77
74
|
| Name["DecodingServices"],
|
|
78
75
|
PreludeE,
|
|
79
|
-
HttpClient.HttpClient
|
|
76
|
+
HttpClient.HttpClient | Env
|
|
80
77
|
>
|
|
81
78
|
|
|
82
|
-
readonly layer: Layer.Layer<RunROut, RunE, ActorSelf | HttpClient.HttpClient | PreludeROut>
|
|
79
|
+
readonly layer: Layer.Layer<RunROut, RunE, ActorSelf | HttpClient.HttpClient | Env | PreludeROut>
|
|
83
80
|
|
|
84
|
-
readonly external: Handlers<
|
|
81
|
+
readonly external: Handlers<
|
|
82
|
+
D["external"],
|
|
83
|
+
ActorSelf | HttpClient.HttpClient | Env | PreludeROut | RunROut | Scope.Scope
|
|
84
|
+
>
|
|
85
85
|
|
|
86
86
|
readonly internal: Handlers<Internal, ActorSelf | HttpClient.HttpClient | PreludeROut | RunROut | Scope.Scope>
|
|
87
87
|
|
|
88
88
|
readonly hydrate: Effect.Effect<
|
|
89
89
|
S.Struct<D["state"]>["Type"],
|
|
90
90
|
never,
|
|
91
|
-
ActorSelf | HttpClient.HttpClient | PreludeROut | RunROut | Scope.Scope
|
|
91
|
+
ActorSelf | HttpClient.HttpClient | Env | PreludeROut | RunROut | Scope.Scope
|
|
92
92
|
>
|
|
93
93
|
|
|
94
94
|
readonly onDisconnect: Effect.Effect<
|
|
95
95
|
void,
|
|
96
96
|
never,
|
|
97
|
-
ActorSelf | HttpClient.HttpClient | PreludeROut | RunROut | Scope.Scope
|
|
97
|
+
ActorSelf | HttpClient.HttpClient | Env | PreludeROut | RunROut | Scope.Scope
|
|
98
98
|
>
|
|
99
99
|
|
|
100
100
|
readonly hibernation?: Duration.Input | undefined
|
|
@@ -181,17 +181,18 @@ export const make = <
|
|
|
181
181
|
const { _tag } = event.event as never
|
|
182
182
|
return Effect.gen(function* () {
|
|
183
183
|
const trace = yield* Tracing.currentTrace
|
|
184
|
-
const encoded = yield* encodeEvent({
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
184
|
+
const encoded = yield* encodeEvent({ ...event, ...(trace && { trace }) }).pipe(
|
|
185
|
+
Effect.catchTags({
|
|
186
|
+
SchemaError: Effect.die,
|
|
187
|
+
}),
|
|
188
|
+
)
|
|
188
189
|
// @effect-diagnostics-next-line tryCatchInEffectGen:off
|
|
189
190
|
try {
|
|
190
191
|
socket.send(encoded)
|
|
191
192
|
// oxlint-disable-next-line no-unused-vars
|
|
192
193
|
} catch (_e) {}
|
|
193
194
|
}).pipe(
|
|
194
|
-
span("send", {
|
|
195
|
+
Boundary.span("send", import.meta.url, {
|
|
195
196
|
attributes: { _tag, ...sessionAttributes(session) },
|
|
196
197
|
kind: "producer",
|
|
197
198
|
links: [sessionLink(session)],
|
|
@@ -201,11 +202,14 @@ export const make = <
|
|
|
201
202
|
close: ({ socket }) => Effect.sync(() => socket.close(1000)),
|
|
202
203
|
snapshot: ({ socket, session }, attachments) =>
|
|
203
204
|
encodeSocketAttachment({ attachments, session }).pipe(
|
|
205
|
+
Effect.catchTags({
|
|
206
|
+
SchemaError: Effect.die,
|
|
207
|
+
}),
|
|
204
208
|
Effect.andThen((v) => Effect.sync(() => socket.serializeAttachment(v))),
|
|
205
209
|
),
|
|
206
210
|
}
|
|
207
211
|
|
|
208
|
-
class NameDecoded extends Context.Service<NameDecoded, Name["Type"]>()("liminal/
|
|
212
|
+
class NameDecoded extends Context.Service<NameDecoded, Name["Type"]>()("liminal/ActorNamespace/NameDecoded") {}
|
|
209
213
|
|
|
210
214
|
return class extends DurableObject {
|
|
211
215
|
readonly run
|
|
@@ -239,12 +243,17 @@ export const make = <
|
|
|
239
243
|
const Live = Layer.mergeAll(
|
|
240
244
|
FetchHttpClient.layer,
|
|
241
245
|
Layer.succeed(DoState.DoState, state),
|
|
246
|
+
Layer.succeed(Env, env as never),
|
|
242
247
|
Layer.effect(NameDecoded, S.decodeUnknownEffect(Name)(state.id.name)),
|
|
243
248
|
).pipe(
|
|
244
249
|
Layer.provideMerge(
|
|
245
250
|
prelude.pipe(
|
|
246
251
|
Layer.provideMerge(
|
|
247
|
-
Layer.mergeAll(
|
|
252
|
+
Layer.mergeAll(
|
|
253
|
+
FetchHttpClient.layer,
|
|
254
|
+
ConfigProvider.layer(ConfigProvider.fromUnknown(env)),
|
|
255
|
+
Layer.succeed(Env, env as never),
|
|
256
|
+
),
|
|
248
257
|
),
|
|
249
258
|
),
|
|
250
259
|
),
|
|
@@ -258,28 +267,32 @@ export const make = <
|
|
|
258
267
|
.register({ socket, session }, attachments)
|
|
259
268
|
.pipe(Effect.linkSpans(Tracer.externalSpan(session.trace), sessionLink(session).attributes))
|
|
260
269
|
}
|
|
261
|
-
}).pipe(span("hydrate"), Layer.effectDiscard)
|
|
262
|
-
|
|
263
|
-
const runtime = ManagedRuntime.make(HydrateClientsLive.pipe(Layer.provideMerge(Live), boundLayer("actor")))
|
|
270
|
+
}).pipe(Boundary.span("hydrate", import.meta.url), Layer.effectDiscard)
|
|
264
271
|
|
|
265
|
-
|
|
272
|
+
const runtime = ManagedRuntime.make(
|
|
273
|
+
HydrateClientsLive.pipe(Layer.provideMerge(Live), Boundary.layer("actor", import.meta.url)),
|
|
274
|
+
)
|
|
275
|
+
this.run = <A, E, R extends ManagedRuntime.ManagedRuntime.Services<typeof runtime>>(
|
|
276
|
+
effect: Effect.Effect<A, E, R>,
|
|
277
|
+
) => Effect.onError(effect, Effect.logError).pipe(runtime.runPromise)
|
|
266
278
|
}
|
|
267
279
|
|
|
268
280
|
override fetch(request: Request): Promise<Response> {
|
|
269
281
|
return Effect.gen({ self: this }, function* () {
|
|
270
282
|
const url = new URL(request.url)
|
|
271
283
|
const attachments = yield* decodeAttachmentsString(url.searchParams.get("__liminal_attachments"))
|
|
284
|
+
const clientId = yield* Effect.fromNullishOr(url.searchParams.get("__liminal_client_id"))
|
|
272
285
|
const { 0: webSocket, 1: server } = new WebSocketPair()
|
|
273
286
|
const state = yield* DoState.DoState
|
|
274
287
|
const session = {
|
|
275
|
-
id: SessionId.make(
|
|
288
|
+
id: SessionId.make(clientId),
|
|
276
289
|
trace: yield* Effect.currentSpan.pipe(Effect.map(Tracing.toTraceEnvelope)),
|
|
277
290
|
}
|
|
278
291
|
const currentClient = yield* this.directory.register({ socket: server, session }, attachments)
|
|
279
292
|
state.acceptWebSocket(server)
|
|
280
293
|
const initial = yield* hydrate.pipe(
|
|
281
294
|
this.provideActor(currentClient),
|
|
282
|
-
span("hydrate", {
|
|
295
|
+
Boundary.span("hydrate", import.meta.url, {
|
|
283
296
|
attributes: sessionAttributes(session),
|
|
284
297
|
links: [sessionLink(session)],
|
|
285
298
|
}),
|
|
@@ -291,7 +304,7 @@ export const make = <
|
|
|
291
304
|
headers: { [SecWebSocketProtocol]: "liminal" },
|
|
292
305
|
})
|
|
293
306
|
}).pipe(
|
|
294
|
-
span("fetch", {
|
|
307
|
+
Boundary.span("fetch", import.meta.url, {
|
|
295
308
|
kind: "server",
|
|
296
309
|
parent: pipe(request.headers, Headers.fromInput, HttpTraceContext.fromHeaders, Option.getOrUndefined),
|
|
297
310
|
}),
|
|
@@ -312,7 +325,7 @@ export const make = <
|
|
|
312
325
|
yield* currentClient.disconnect
|
|
313
326
|
return yield* onDisconnect.pipe(
|
|
314
327
|
this.provideActor(currentClient),
|
|
315
|
-
span("disconnect", {
|
|
328
|
+
Boundary.span("disconnect", import.meta.url, {
|
|
316
329
|
attributes: sessionAttributes(session),
|
|
317
330
|
links: [sessionLink(session)],
|
|
318
331
|
}),
|
|
@@ -358,21 +371,23 @@ export const make = <
|
|
|
358
371
|
}),
|
|
359
372
|
),
|
|
360
373
|
this.provideActor(currentClient),
|
|
361
|
-
span("handler", {
|
|
374
|
+
Boundary.span("handler", import.meta.url, {
|
|
362
375
|
attributes: { _tag, ...sessionAttributes(session) },
|
|
363
376
|
kind: "server",
|
|
364
377
|
parent,
|
|
365
378
|
links,
|
|
366
379
|
}),
|
|
367
380
|
)
|
|
368
|
-
}).pipe(span("socket-message"), this.run)
|
|
381
|
+
}).pipe(Boundary.span("socket-message", import.meta.url), this.run)
|
|
369
382
|
}
|
|
370
383
|
|
|
371
384
|
override webSocketClose(socket: WebSocket, _code: number, _reason: string, _wasClean: boolean) {
|
|
372
385
|
Effect.gen({ self: this }, function* () {
|
|
373
|
-
const entry = yield* this.directory
|
|
374
|
-
.
|
|
375
|
-
|
|
386
|
+
const entry = yield* this.directory.entry(socket).pipe(
|
|
387
|
+
Effect.catchTags({
|
|
388
|
+
NoSuchElementError: () => Effect.undefined,
|
|
389
|
+
}),
|
|
390
|
+
)
|
|
376
391
|
if (!entry) {
|
|
377
392
|
return
|
|
378
393
|
}
|
|
@@ -384,12 +399,12 @@ export const make = <
|
|
|
384
399
|
yield* this.directory.unregister(socket)
|
|
385
400
|
yield* onDisconnect.pipe(
|
|
386
401
|
this.provideActor(currentClient),
|
|
387
|
-
span("disconnect", {
|
|
402
|
+
Boundary.span("disconnect", import.meta.url, {
|
|
388
403
|
attributes: sessionAttributes(session),
|
|
389
404
|
links: [sessionLink(session)],
|
|
390
405
|
}),
|
|
391
406
|
)
|
|
392
|
-
}).pipe(span("socket-close"), this.run)
|
|
407
|
+
}).pipe(Boundary.span("socket-close", import.meta.url), this.run)
|
|
393
408
|
}
|
|
394
409
|
|
|
395
410
|
override webSocketError(socket: WebSocket, cause: unknown) {
|
|
@@ -402,13 +417,13 @@ export const make = <
|
|
|
402
417
|
yield* this.directory.unregister(socket)
|
|
403
418
|
yield* onDisconnect.pipe(
|
|
404
419
|
this.provideActor(currentClient),
|
|
405
|
-
span("disconnect", {
|
|
420
|
+
Boundary.span("disconnect", import.meta.url, {
|
|
406
421
|
attributes: sessionAttributes(session),
|
|
407
422
|
links: [sessionLink(session)],
|
|
408
423
|
}),
|
|
409
424
|
)
|
|
410
425
|
yield* Effect.annotateLogs(Effect.logDebug("SocketErrored"), { cause })
|
|
411
|
-
}).pipe(span("socket-error"), this.run)
|
|
426
|
+
}).pipe(Boundary.span("socket-error", import.meta.url), this.run)
|
|
412
427
|
}
|
|
413
428
|
|
|
414
429
|
async rpc<K extends keyof Internal>(
|
|
@@ -416,7 +431,19 @@ export const make = <
|
|
|
416
431
|
payload: Internal[K]["payload"]["Type"],
|
|
417
432
|
): Promise<Exit.Exit<Internal[K]["success"]["Type"], Internal[K]["failure"]["Type"]>> {
|
|
418
433
|
const handler = internal[method]
|
|
419
|
-
return await handler(payload).pipe(
|
|
434
|
+
return await handler(payload).pipe(
|
|
435
|
+
this.provideActor(null!),
|
|
436
|
+
Boundary.span("fn-internal", import.meta.url),
|
|
437
|
+
Effect.exit,
|
|
438
|
+
this.run,
|
|
439
|
+
)
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
async proxySendAll<K extends keyof D["events"]>(event: K, payload: S.Struct<D["events"][K]>["Type"]) {
|
|
443
|
+
await Effect.gen(function* () {
|
|
444
|
+
const { clients } = yield* actor
|
|
445
|
+
yield* Effect.forEach(clients, ({ send }) => send(event, payload), { concurrency: "unbounded" })
|
|
446
|
+
}).pipe(this.provideActor(null!), Boundary.span("fn-internal", import.meta.url), this.run)
|
|
420
447
|
}
|
|
421
448
|
}
|
|
422
449
|
}
|
package/ActorTransport.ts
CHANGED
|
@@ -8,12 +8,12 @@ export interface ActorTransport<Key, Client, AttachmentFields extends S.Struct.F
|
|
|
8
8
|
readonly send: (
|
|
9
9
|
client: Client,
|
|
10
10
|
event: Protocol<D>["Event"]["Type"],
|
|
11
|
-
) => Effect.Effect<void,
|
|
11
|
+
) => Effect.Effect<void, never, Protocol<D>["Event"]["EncodingServices"]>
|
|
12
12
|
|
|
13
13
|
readonly close: (client: Client) => Effect.Effect<void>
|
|
14
14
|
|
|
15
15
|
readonly snapshot: (
|
|
16
16
|
client: Client,
|
|
17
17
|
attachments: S.Struct<AttachmentFields>["Type"],
|
|
18
|
-
) => Effect.Effect<void,
|
|
18
|
+
) => Effect.Effect<void, never, S.Struct<AttachmentFields>["EncodingServices"]>
|
|
19
19
|
}
|
package/Audition.ts
CHANGED
|
@@ -35,7 +35,7 @@ export const empty: Audition<never, never, {}, never> = {
|
|
|
35
35
|
return Pipeable.pipeArguments(this, arguments)
|
|
36
36
|
},
|
|
37
37
|
state: Stream.fail(new AuditionError()),
|
|
38
|
-
fn: () => () => new AuditionError()
|
|
38
|
+
fn: () => () => new AuditionError(),
|
|
39
39
|
events: Stream.fail(new AuditionError()),
|
|
40
40
|
}
|
|
41
41
|
|