liminal 0.17.13 → 0.17.15
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 +12 -13
- package/ActorTransport.ts +6 -4
- package/Audition.ts +87 -40
- package/CHANGELOG.md +16 -0
- package/Client.ts +260 -134
- package/ClientDirectory.ts +50 -36
- package/ClientHandleEncoders.ts +15 -0
- package/Fn.ts +55 -0
- package/Method.ts +11 -21
- package/Protocol.ts +44 -36
- package/Reducer.ts +22 -0
- package/Tracing.ts +37 -0
- package/browser/BrowserActorNamespace.ts +65 -30
- package/dist/Actor.d.ts +1 -1
- package/dist/Actor.js +6 -6
- package/dist/Actor.js.map +1 -1
- package/dist/ActorTransport.d.ts +5 -4
- package/dist/Audition.d.ts +16 -9
- package/dist/Audition.js +25 -9
- package/dist/Audition.js.map +1 -1
- package/dist/Client.d.ts +21 -14
- package/dist/Client.js +147 -100
- package/dist/Client.js.map +1 -1
- package/dist/ClientDirectory.d.ts +14 -6
- package/dist/ClientDirectory.js +25 -22
- package/dist/ClientDirectory.js.map +1 -1
- package/dist/ClientHandleEncoders.d.ts +7 -0
- package/dist/ClientHandleEncoders.js +2 -0
- package/dist/ClientHandleEncoders.js.map +1 -0
- package/dist/Fn.d.ts +16 -0
- package/dist/Fn.js +2 -0
- package/dist/Fn.js.map +1 -0
- package/dist/Method.d.ts +9 -14
- package/dist/Method.js +0 -1
- package/dist/Method.js.map +1 -1
- package/dist/Protocol.d.ts +19 -22
- package/dist/Protocol.js +20 -15
- package/dist/Protocol.js.map +1 -1
- package/dist/Reducer.d.ts +11 -0
- package/dist/Reducer.js +2 -0
- package/dist/Reducer.js.map +1 -0
- package/dist/Tracing.d.ts +37 -0
- package/dist/Tracing.js +29 -0
- package/dist/Tracing.js.map +1 -0
- package/dist/browser/BrowserActorNamespace.d.ts +5 -5
- package/dist/browser/BrowserActorNamespace.js +41 -20
- package/dist/browser/BrowserActorNamespace.js.map +1 -1
- package/dist/errors.d.ts +0 -4
- package/dist/errors.js.map +1 -1
- package/dist/experimental/TaggedTemplateFunction.js +1 -1
- package/dist/experimental/TaggedTemplateFunction.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/package.json +16 -21
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/workerd/ActorHandle.d.ts +9 -0
- package/dist/workerd/ActorHandle.js +4 -0
- package/dist/workerd/ActorHandle.js.map +1 -0
- package/dist/workerd/WorkerdActorNamespace.d.ts +18 -18
- package/dist/workerd/WorkerdActorNamespace.js +43 -141
- package/dist/workerd/WorkerdActorNamespace.js.map +1 -1
- package/dist/workerd/WorkerdActorRuntime.d.ts +19 -0
- package/dist/workerd/WorkerdActorRuntime.js +204 -0
- package/dist/workerd/WorkerdActorRuntime.js.map +1 -0
- package/dist/workerd/index.d.ts +2 -0
- package/dist/workerd/index.js +2 -0
- package/dist/workerd/index.js.map +1 -1
- package/errors.ts +0 -6
- package/experimental/TaggedTemplateFunction.ts +1 -1
- package/index.ts +3 -3
- package/package.json +10 -25
- package/tsconfig.json +1 -1
- package/vitest.config.ts +7 -0
- package/workerd/ActorHandle.ts +29 -0
- package/workerd/WorkerdActorNamespace.ts +86 -273
- package/workerd/WorkerdActorRuntime.ts +422 -0
- package/workerd/index.ts +2 -0
- package/Accumulator.ts +0 -103
- package/F.ts +0 -10
- package/_diagnostic.ts +0 -3
- package/_util/Mutex.ts +0 -13
- package/dist/Accumulator.d.ts +0 -22
- package/dist/Accumulator.js +0 -37
- package/dist/Accumulator.js.map +0 -1
- package/dist/F.d.ts +0 -4
- package/dist/F.js +0 -2
- package/dist/F.js.map +0 -1
- package/dist/_diagnostic.d.ts +0 -4
- package/dist/_diagnostic.js +0 -3
- package/dist/_diagnostic.js.map +0 -1
- package/dist/_util/Mutex.d.ts +0 -7
- package/dist/_util/Mutex.js +0 -9
- package/dist/_util/Mutex.js.map +0 -1
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
import { DurableObject } from "cloudflare:workers"
|
|
2
|
+
import {
|
|
3
|
+
Layer,
|
|
4
|
+
Effect,
|
|
5
|
+
Scope,
|
|
6
|
+
Schema as S,
|
|
7
|
+
Context,
|
|
8
|
+
ManagedRuntime,
|
|
9
|
+
ConfigProvider,
|
|
10
|
+
Duration,
|
|
11
|
+
flow,
|
|
12
|
+
Option,
|
|
13
|
+
Tracer,
|
|
14
|
+
pipe,
|
|
15
|
+
Exit,
|
|
16
|
+
} from "effect"
|
|
17
|
+
import { DoState } from "effect-workerd"
|
|
18
|
+
import { Clock } from "effect-workerd/platform"
|
|
19
|
+
import { SecWebSocketProtocol } from "effect-workerd/socket_util"
|
|
20
|
+
import { Headers, FetchHttpClient, HttpClient, HttpTraceContext } from "effect/unstable/http"
|
|
21
|
+
import { boundLayer } from "liminal-util/boundLayer"
|
|
22
|
+
import { logCause } from "liminal-util/logCause"
|
|
23
|
+
import * as Spanner from "liminal-util/Spanner"
|
|
24
|
+
|
|
25
|
+
import { type TopFromString, encodeJsonString, decodeJsonString } from "../_util/schema.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"
|
|
33
|
+
import type { ActorNamespace } from "./WorkerdActorNamespace.ts"
|
|
34
|
+
|
|
35
|
+
const span = Spanner.make(import.meta.url)
|
|
36
|
+
|
|
37
|
+
export interface ActorRuntimeDefinition<
|
|
38
|
+
NamespaceSelf,
|
|
39
|
+
NamespaceId extends string,
|
|
40
|
+
Internal extends Methods,
|
|
41
|
+
ActorSelf,
|
|
42
|
+
ActorId extends string,
|
|
43
|
+
Name extends TopFromString,
|
|
44
|
+
AttachmentFields extends S.Struct.Fields,
|
|
45
|
+
ClientSelf,
|
|
46
|
+
ClientId extends string,
|
|
47
|
+
D extends ProtocolDefinition,
|
|
48
|
+
PreludeROut,
|
|
49
|
+
PreludeE,
|
|
50
|
+
RunROut,
|
|
51
|
+
RunE,
|
|
52
|
+
> {
|
|
53
|
+
readonly ""?: this["namespace"]["definition"]["actor"]["definition"]["client"]["protocol"]
|
|
54
|
+
|
|
55
|
+
readonly namespace: ActorNamespace<
|
|
56
|
+
NamespaceSelf,
|
|
57
|
+
NamespaceId,
|
|
58
|
+
Internal,
|
|
59
|
+
ActorSelf,
|
|
60
|
+
ActorId,
|
|
61
|
+
Name,
|
|
62
|
+
AttachmentFields,
|
|
63
|
+
ClientSelf,
|
|
64
|
+
ClientId,
|
|
65
|
+
D
|
|
66
|
+
>
|
|
67
|
+
|
|
68
|
+
readonly prelude: Layer.Layer<
|
|
69
|
+
| PreludeROut
|
|
70
|
+
| NonNullable<this[""]>["F"]["Payload"]["DecodingServices"]
|
|
71
|
+
| NonNullable<this[""]>["F"]["Success"]["EncodingServices"]
|
|
72
|
+
| NonNullable<this[""]>["F"]["Failure"]["EncodingServices"]
|
|
73
|
+
| NonNullable<this[""]>["Event"]["EncodingServices"]
|
|
74
|
+
| S.Struct<AttachmentFields>["DecodingServices"]
|
|
75
|
+
| S.Struct<AttachmentFields>["EncodingServices"]
|
|
76
|
+
| Name["EncodingServices"]
|
|
77
|
+
| Name["DecodingServices"],
|
|
78
|
+
PreludeE,
|
|
79
|
+
HttpClient.HttpClient
|
|
80
|
+
>
|
|
81
|
+
|
|
82
|
+
readonly layer: Layer.Layer<RunROut, RunE, ActorSelf | HttpClient.HttpClient | PreludeROut>
|
|
83
|
+
|
|
84
|
+
readonly external: Handlers<D["external"], ActorSelf | HttpClient.HttpClient | PreludeROut | RunROut | Scope.Scope>
|
|
85
|
+
|
|
86
|
+
readonly internal: Handlers<Internal, ActorSelf | HttpClient.HttpClient | PreludeROut | RunROut | Scope.Scope>
|
|
87
|
+
|
|
88
|
+
readonly hydrate: Effect.Effect<
|
|
89
|
+
S.Struct<D["state"]>["Type"],
|
|
90
|
+
never,
|
|
91
|
+
ActorSelf | HttpClient.HttpClient | PreludeROut | RunROut | Scope.Scope
|
|
92
|
+
>
|
|
93
|
+
|
|
94
|
+
readonly onDisconnect: Effect.Effect<
|
|
95
|
+
void,
|
|
96
|
+
never,
|
|
97
|
+
ActorSelf | HttpClient.HttpClient | PreludeROut | RunROut | Scope.Scope
|
|
98
|
+
>
|
|
99
|
+
|
|
100
|
+
readonly hibernation?: Duration.Input | undefined
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export const make = <
|
|
104
|
+
NamespaceSelf,
|
|
105
|
+
NamespaceId extends string,
|
|
106
|
+
Internal extends Methods,
|
|
107
|
+
ActorSelf,
|
|
108
|
+
ActorId extends string,
|
|
109
|
+
Name extends TopFromString,
|
|
110
|
+
AttachmentFields extends S.Struct.Fields,
|
|
111
|
+
ClientSelf,
|
|
112
|
+
ClientId extends string,
|
|
113
|
+
D extends ProtocolDefinition,
|
|
114
|
+
PreludeROut,
|
|
115
|
+
PreludeE,
|
|
116
|
+
RunROut,
|
|
117
|
+
RunE,
|
|
118
|
+
>(
|
|
119
|
+
definition: ActorRuntimeDefinition<
|
|
120
|
+
NamespaceSelf,
|
|
121
|
+
NamespaceId,
|
|
122
|
+
Internal,
|
|
123
|
+
ActorSelf,
|
|
124
|
+
ActorId,
|
|
125
|
+
Name,
|
|
126
|
+
AttachmentFields,
|
|
127
|
+
ClientSelf,
|
|
128
|
+
ClientId,
|
|
129
|
+
D,
|
|
130
|
+
PreludeROut,
|
|
131
|
+
PreludeE,
|
|
132
|
+
RunROut,
|
|
133
|
+
RunE
|
|
134
|
+
>,
|
|
135
|
+
): new (state: DurableObjectState<{}>, env: Cloudflare.Env) => DurableObject => {
|
|
136
|
+
const {
|
|
137
|
+
hibernation,
|
|
138
|
+
prelude,
|
|
139
|
+
external,
|
|
140
|
+
layer,
|
|
141
|
+
hydrate,
|
|
142
|
+
onDisconnect,
|
|
143
|
+
internal,
|
|
144
|
+
namespace: {
|
|
145
|
+
definition: { actor },
|
|
146
|
+
},
|
|
147
|
+
} = definition
|
|
148
|
+
const {
|
|
149
|
+
definition: {
|
|
150
|
+
name: Name,
|
|
151
|
+
client: { protocol: P },
|
|
152
|
+
attachments: AttachmentFields,
|
|
153
|
+
},
|
|
154
|
+
} = actor
|
|
155
|
+
|
|
156
|
+
const Attachments = S.Struct(AttachmentFields)
|
|
157
|
+
const SocketAttachment = S.Struct({
|
|
158
|
+
attachments: S.toCodecJson(Attachments),
|
|
159
|
+
session: Tracing.Session,
|
|
160
|
+
})
|
|
161
|
+
const encodeSocketAttachment = S.encodeEffect(SocketAttachment)
|
|
162
|
+
const decodeSocketAttachment = S.decodeUnknownEffect(SocketAttachment)
|
|
163
|
+
const decodeAttachmentsString = decodeJsonString(Attachments)
|
|
164
|
+
const encodeAuditionSuccess = encodeJsonString(P.Audition.Success)
|
|
165
|
+
const decodeClient = decodeJsonString(P.Client)
|
|
166
|
+
const encodeFSuccess = encodeJsonString(P.F.Success)
|
|
167
|
+
const encodeFFailure = encodeJsonString(P.F.Failure)
|
|
168
|
+
const encodeEvent = encodeJsonString(P.Event)
|
|
169
|
+
|
|
170
|
+
const transport: ActorTransport<
|
|
171
|
+
WebSocket,
|
|
172
|
+
{
|
|
173
|
+
readonly socket: WebSocket
|
|
174
|
+
readonly session: typeof Tracing.Session.Type
|
|
175
|
+
},
|
|
176
|
+
AttachmentFields,
|
|
177
|
+
D
|
|
178
|
+
> = {
|
|
179
|
+
key: ({ socket }) => socket,
|
|
180
|
+
send: ({ socket, session }, event) => {
|
|
181
|
+
const { _tag } = event.event as never
|
|
182
|
+
return Effect.gen(function* () {
|
|
183
|
+
const trace = yield* Tracing.currentTrace
|
|
184
|
+
const encoded = yield* encodeEvent({
|
|
185
|
+
...event,
|
|
186
|
+
...(trace && { trace }),
|
|
187
|
+
})
|
|
188
|
+
// @effect-diagnostics-next-line tryCatchInEffectGen:off
|
|
189
|
+
try {
|
|
190
|
+
socket.send(encoded)
|
|
191
|
+
// oxlint-disable-next-line no-unused-vars
|
|
192
|
+
} catch (_e) {}
|
|
193
|
+
}).pipe(
|
|
194
|
+
span("send", {
|
|
195
|
+
attributes: { _tag, ...sessionAttributes(session) },
|
|
196
|
+
kind: "producer",
|
|
197
|
+
links: [sessionLink(session)],
|
|
198
|
+
}),
|
|
199
|
+
)
|
|
200
|
+
},
|
|
201
|
+
close: ({ socket }) => Effect.sync(() => socket.close(1000)),
|
|
202
|
+
snapshot: ({ socket, session }, attachments) =>
|
|
203
|
+
encodeSocketAttachment({ attachments, session }).pipe(
|
|
204
|
+
Effect.andThen((v) => Effect.sync(() => socket.serializeAttachment(v))),
|
|
205
|
+
),
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
class NameDecoded extends Context.Service<NameDecoded, Name["Type"]>()("liminal/WorkerdActorNamespace/NameDecoded") {}
|
|
209
|
+
|
|
210
|
+
return class extends DurableObject {
|
|
211
|
+
readonly run
|
|
212
|
+
readonly directory = ClientDirectory.make(actor, { transport })
|
|
213
|
+
readonly provideActor = (currentClient: ClientHandle<ActorSelf, AttachmentFields, D>) =>
|
|
214
|
+
flow(
|
|
215
|
+
Effect.provide(
|
|
216
|
+
Layer.provideMerge(
|
|
217
|
+
layer,
|
|
218
|
+
Effect.gen({ self: this }, function* () {
|
|
219
|
+
const name = yield* NameDecoded
|
|
220
|
+
return Layer.succeed(actor, {
|
|
221
|
+
name,
|
|
222
|
+
clients: this.directory.handles,
|
|
223
|
+
currentClient,
|
|
224
|
+
})
|
|
225
|
+
}).pipe(Layer.unwrap),
|
|
226
|
+
),
|
|
227
|
+
),
|
|
228
|
+
Effect.scoped,
|
|
229
|
+
)
|
|
230
|
+
constructor(state: DurableObjectState<{}>, env: Cloudflare.Env) {
|
|
231
|
+
super(state, env)
|
|
232
|
+
if (hibernation) {
|
|
233
|
+
Option.andThen(
|
|
234
|
+
Duration.fromInput(hibernation),
|
|
235
|
+
flow(Duration.toMillis, (timeout) => state.setHibernatableWebSocketEventTimeout(timeout)),
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const Live = Layer.mergeAll(
|
|
240
|
+
FetchHttpClient.layer,
|
|
241
|
+
Layer.succeed(DoState.DoState, state),
|
|
242
|
+
Layer.effect(NameDecoded, S.decodeUnknownEffect(Name)(state.id.name)),
|
|
243
|
+
).pipe(
|
|
244
|
+
Layer.provideMerge(
|
|
245
|
+
prelude.pipe(
|
|
246
|
+
Layer.provideMerge(
|
|
247
|
+
Layer.mergeAll(FetchHttpClient.layer, ConfigProvider.layer(ConfigProvider.fromUnknown(env))),
|
|
248
|
+
),
|
|
249
|
+
),
|
|
250
|
+
),
|
|
251
|
+
Layer.provideMerge(Clock.layer),
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
const HydrateClientsLive = Effect.gen({ self: this }, function* () {
|
|
255
|
+
for (const socket of state.getWebSockets()) {
|
|
256
|
+
const { attachments, session } = yield* decodeSocketAttachment(socket.deserializeAttachment())
|
|
257
|
+
yield* this.directory
|
|
258
|
+
.register({ socket, session }, attachments)
|
|
259
|
+
.pipe(Effect.linkSpans(Tracer.externalSpan(session.trace), sessionLink(session).attributes))
|
|
260
|
+
}
|
|
261
|
+
}).pipe(span("hydrate"), Layer.effectDiscard)
|
|
262
|
+
|
|
263
|
+
const runtime = ManagedRuntime.make(HydrateClientsLive.pipe(Layer.provideMerge(Live), boundLayer("actor")))
|
|
264
|
+
|
|
265
|
+
this.run = flow(Effect.tapCause(logCause), runtime.runPromise)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
override fetch(request: Request): Promise<Response> {
|
|
269
|
+
return Effect.gen({ self: this }, function* () {
|
|
270
|
+
const url = new URL(request.url)
|
|
271
|
+
const attachments = yield* decodeAttachmentsString(url.searchParams.get("__liminal_attachments"))
|
|
272
|
+
const { 0: webSocket, 1: server } = new WebSocketPair()
|
|
273
|
+
const state = yield* DoState.DoState
|
|
274
|
+
const session = {
|
|
275
|
+
id: SessionId.make(crypto.randomUUID()),
|
|
276
|
+
trace: yield* Effect.currentSpan.pipe(Effect.map(Tracing.toTraceEnvelope)),
|
|
277
|
+
}
|
|
278
|
+
const currentClient = yield* this.directory.register({ socket: server, session }, attachments)
|
|
279
|
+
state.acceptWebSocket(server)
|
|
280
|
+
const initial = yield* hydrate.pipe(
|
|
281
|
+
this.provideActor(currentClient),
|
|
282
|
+
span("hydrate", {
|
|
283
|
+
attributes: sessionAttributes(session),
|
|
284
|
+
links: [sessionLink(session)],
|
|
285
|
+
}),
|
|
286
|
+
)
|
|
287
|
+
server.send(yield* encodeAuditionSuccess({ _tag: "Audition.Success", initial }))
|
|
288
|
+
return new Response(null, {
|
|
289
|
+
status: 101,
|
|
290
|
+
webSocket,
|
|
291
|
+
headers: { [SecWebSocketProtocol]: "liminal" },
|
|
292
|
+
})
|
|
293
|
+
}).pipe(
|
|
294
|
+
span("fetch", {
|
|
295
|
+
kind: "server",
|
|
296
|
+
parent: pipe(request.headers, Headers.fromInput, HttpTraceContext.fromHeaders, Option.getOrUndefined),
|
|
297
|
+
}),
|
|
298
|
+
this.run,
|
|
299
|
+
)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
override webSocketMessage(socket: WebSocket, raw: string | ArrayBuffer) {
|
|
303
|
+
Effect.gen({ self: this }, function* () {
|
|
304
|
+
const { client, handle: currentClient } = yield* this.directory.entry(socket)
|
|
305
|
+
const { session } = client
|
|
306
|
+
yield* Effect.annotateCurrentSpan(sessionAttributes(session))
|
|
307
|
+
const message = yield* decodeClient(raw instanceof ArrayBuffer ? new TextDecoder().decode(raw) : raw)
|
|
308
|
+
if (message._tag === "Audition.Payload") {
|
|
309
|
+
return yield* Effect.die(undefined)
|
|
310
|
+
}
|
|
311
|
+
if (message._tag === "Disconnect") {
|
|
312
|
+
yield* currentClient.disconnect
|
|
313
|
+
return yield* onDisconnect.pipe(
|
|
314
|
+
this.provideActor(currentClient),
|
|
315
|
+
span("disconnect", {
|
|
316
|
+
attributes: sessionAttributes(session),
|
|
317
|
+
links: [sessionLink(session)],
|
|
318
|
+
}),
|
|
319
|
+
)
|
|
320
|
+
}
|
|
321
|
+
const { id, payload } = message
|
|
322
|
+
const { _tag, value } = payload as never
|
|
323
|
+
const parent = message.trace ? Tracer.externalSpan(message.trace) : undefined
|
|
324
|
+
const transportSpan = yield* Tracing.parent
|
|
325
|
+
const links = [
|
|
326
|
+
sessionLink(session),
|
|
327
|
+
...(parent && transportSpan
|
|
328
|
+
? [
|
|
329
|
+
{
|
|
330
|
+
span: transportSpan,
|
|
331
|
+
attributes: {
|
|
332
|
+
"liminal.link": "transport",
|
|
333
|
+
"liminal.transport": "websocket",
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
]
|
|
337
|
+
: []),
|
|
338
|
+
]
|
|
339
|
+
yield* external[_tag]!(value).pipe(
|
|
340
|
+
Effect.matchEffect({
|
|
341
|
+
onSuccess: (value) =>
|
|
342
|
+
encodeFSuccess({
|
|
343
|
+
_tag: "F.Success",
|
|
344
|
+
id,
|
|
345
|
+
success: { _tag, value } as never,
|
|
346
|
+
}),
|
|
347
|
+
onFailure: (value) =>
|
|
348
|
+
encodeFFailure({
|
|
349
|
+
_tag: "F.Failure",
|
|
350
|
+
id,
|
|
351
|
+
failure: { _tag, value } as never,
|
|
352
|
+
}),
|
|
353
|
+
}),
|
|
354
|
+
Effect.andThen((v) =>
|
|
355
|
+
Effect.try({
|
|
356
|
+
try: () => socket.send(v),
|
|
357
|
+
catch: () => {},
|
|
358
|
+
}),
|
|
359
|
+
),
|
|
360
|
+
this.provideActor(currentClient),
|
|
361
|
+
span("handler", {
|
|
362
|
+
attributes: { _tag, ...sessionAttributes(session) },
|
|
363
|
+
kind: "server",
|
|
364
|
+
parent,
|
|
365
|
+
links,
|
|
366
|
+
}),
|
|
367
|
+
)
|
|
368
|
+
}).pipe(span("socket-message"), this.run)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
override webSocketClose(socket: WebSocket, _code: number, _reason: string, _wasClean: boolean) {
|
|
372
|
+
Effect.gen({ self: this }, function* () {
|
|
373
|
+
const entry = yield* this.directory
|
|
374
|
+
.entry(socket)
|
|
375
|
+
.pipe(Effect.catchTag("NoSuchElementError", () => Effect.undefined))
|
|
376
|
+
if (!entry) {
|
|
377
|
+
return
|
|
378
|
+
}
|
|
379
|
+
const {
|
|
380
|
+
client: { session },
|
|
381
|
+
handle: currentClient,
|
|
382
|
+
} = entry
|
|
383
|
+
yield* Effect.annotateCurrentSpan(sessionAttributes(session))
|
|
384
|
+
yield* this.directory.unregister(socket)
|
|
385
|
+
yield* onDisconnect.pipe(
|
|
386
|
+
this.provideActor(currentClient),
|
|
387
|
+
span("disconnect", {
|
|
388
|
+
attributes: sessionAttributes(session),
|
|
389
|
+
links: [sessionLink(session)],
|
|
390
|
+
}),
|
|
391
|
+
)
|
|
392
|
+
}).pipe(span("socket-close"), this.run)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
override webSocketError(socket: WebSocket, cause: unknown) {
|
|
396
|
+
Effect.gen({ self: this }, function* () {
|
|
397
|
+
const {
|
|
398
|
+
client: { session },
|
|
399
|
+
handle: currentClient,
|
|
400
|
+
} = yield* this.directory.entry(socket)
|
|
401
|
+
yield* Effect.annotateCurrentSpan(sessionAttributes(session))
|
|
402
|
+
yield* this.directory.unregister(socket)
|
|
403
|
+
yield* onDisconnect.pipe(
|
|
404
|
+
this.provideActor(currentClient),
|
|
405
|
+
span("disconnect", {
|
|
406
|
+
attributes: sessionAttributes(session),
|
|
407
|
+
links: [sessionLink(session)],
|
|
408
|
+
}),
|
|
409
|
+
)
|
|
410
|
+
yield* Effect.annotateLogs(Effect.logDebug("SocketErrored"), { cause })
|
|
411
|
+
}).pipe(span("socket-error"), this.run)
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
async rpc<K extends keyof Internal>(
|
|
415
|
+
method: K,
|
|
416
|
+
payload: Internal[K]["payload"]["Type"],
|
|
417
|
+
): Promise<Exit.Exit<Internal[K]["success"]["Type"], Internal[K]["failure"]["Type"]>> {
|
|
418
|
+
const handler = internal[method]
|
|
419
|
+
return await handler(payload).pipe(this.provideActor(null!), span("fn-internal"), Effect.exit, this.run)
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
package/workerd/index.ts
CHANGED
package/Accumulator.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { Deferred, Types, Option, Ref, PubSub, Stream, Effect, Context, Layer, Semaphore } from "effect"
|
|
2
|
-
|
|
3
|
-
import { diagnostic } from "./_diagnostic.ts"
|
|
4
|
-
|
|
5
|
-
const { debug } = diagnostic("Accumulator")
|
|
6
|
-
|
|
7
|
-
const TypeId = "~liminal/Accumulator" as const
|
|
8
|
-
|
|
9
|
-
export interface Service<State> {
|
|
10
|
-
readonly ref: Ref.Ref<State>
|
|
11
|
-
|
|
12
|
-
readonly pubsub: PubSub.PubSub<State>
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface AccumulatorLayerConfig<Item, E, R, State, E2, R2, E3, R3> {
|
|
16
|
-
readonly source: Stream.Stream<Item, E, R>
|
|
17
|
-
|
|
18
|
-
readonly reduce: (item: Item) => (state: State) => Effect.Effect<State, E2, R2>
|
|
19
|
-
|
|
20
|
-
readonly initial: (item: Item) => Effect.Effect<Option.Option<State>, E3, R3>
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type Reduce<State, Item, K extends Types.Tags<Item> = Types.Tags<Item>, E = never, R = never> = (
|
|
24
|
-
item: Types.ExtractTag<Item, K>,
|
|
25
|
-
) => (accumulator: State) => Effect.Effect<State, E, R>
|
|
26
|
-
|
|
27
|
-
export interface Accumulator<Self, Id extends string, State> extends Context.Service<Self, Service<State>> {
|
|
28
|
-
new (_: never): Context.ServiceClass.Shape<Id, Service<State>>
|
|
29
|
-
|
|
30
|
-
readonly [TypeId]: typeof TypeId
|
|
31
|
-
|
|
32
|
-
readonly get: Effect.Effect<State, never, Self>
|
|
33
|
-
|
|
34
|
-
readonly stream: Stream.Stream<State, never, Self>
|
|
35
|
-
|
|
36
|
-
readonly reducer: <Item>() => <K extends Types.Tags<Item>, E, R>(
|
|
37
|
-
_tag: K,
|
|
38
|
-
f: Reduce<State, Item, K, E, R>,
|
|
39
|
-
) => Reduce<State, Item, K, E, R>
|
|
40
|
-
|
|
41
|
-
readonly layer: <Item, E, R, E2, R2, E3, R3>(
|
|
42
|
-
config: AccumulatorLayerConfig<Item, E, R, State, E2, R2, E3, R3>,
|
|
43
|
-
) => Layer.Layer<Self, E | E2 | E3, R | R2 | R3>
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export const Service =
|
|
47
|
-
<Self, State>() =>
|
|
48
|
-
<Id extends string>(id: Id): Accumulator<Self, Id, State> => {
|
|
49
|
-
const tag = Context.Service<Self, Service<State>>()(id)
|
|
50
|
-
|
|
51
|
-
const get = tag.asEffect().pipe(
|
|
52
|
-
Effect.map(({ ref }) => ref),
|
|
53
|
-
Effect.flatMap(Ref.get),
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
const stream = tag.asEffect().pipe(
|
|
57
|
-
Effect.map(({ pubsub }) => Stream.fromPubSub(pubsub)),
|
|
58
|
-
Stream.unwrap,
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
const reducer =
|
|
62
|
-
<Item>() =>
|
|
63
|
-
<K extends Types.Tags<Item>, E, R>(_tag: K, f: Reduce<State, Item, K, E, R>): Reduce<State, Item, K, E, R> =>
|
|
64
|
-
f
|
|
65
|
-
|
|
66
|
-
const layer = <Item, E, R, E2, R2, E3, R3>({
|
|
67
|
-
source,
|
|
68
|
-
initial,
|
|
69
|
-
reduce,
|
|
70
|
-
}: AccumulatorLayerConfig<Item, E, R, State, E2, R2, E3, R3>): Layer.Layer<Self, E | E2 | E3, R | R2 | R3> =>
|
|
71
|
-
Effect.gen(function* () {
|
|
72
|
-
const semaphore = yield* Semaphore.make(1)
|
|
73
|
-
const deferred = yield* Deferred.make<State>()
|
|
74
|
-
const pubsub = yield* PubSub.unbounded<State>({ replay: 1 })
|
|
75
|
-
yield* source.pipe(
|
|
76
|
-
Stream.runForEach(
|
|
77
|
-
Effect.fn(function* (item) {
|
|
78
|
-
if (!(yield* Deferred.isDone(deferred))) {
|
|
79
|
-
const match = yield* initial(item)
|
|
80
|
-
if (Option.isSome(match)) {
|
|
81
|
-
const { value } = match
|
|
82
|
-
yield* Deferred.succeed(deferred, value)
|
|
83
|
-
yield* debug("InitializedState", { state: value })
|
|
84
|
-
}
|
|
85
|
-
return
|
|
86
|
-
}
|
|
87
|
-
const current = yield* Ref.get(ref)
|
|
88
|
-
const reduced = yield* reduce(item)(current)
|
|
89
|
-
yield* Ref.set(ref, reduced)
|
|
90
|
-
yield* PubSub.publish(pubsub, reduced)
|
|
91
|
-
yield* debug("ReducedState", { item, previous: current, current: reduced })
|
|
92
|
-
}, semaphore.withPermits(1)),
|
|
93
|
-
),
|
|
94
|
-
Effect.forkScoped,
|
|
95
|
-
)
|
|
96
|
-
const initial_ = yield* Deferred.await(deferred)
|
|
97
|
-
const ref = yield* Ref.make(initial_)
|
|
98
|
-
yield* PubSub.publish(pubsub, initial_)
|
|
99
|
-
return { ref, pubsub }
|
|
100
|
-
}).pipe(Layer.effect(tag))
|
|
101
|
-
|
|
102
|
-
return Object.assign(tag, { [TypeId]: TypeId, get, stream, reducer, layer })
|
|
103
|
-
}
|
package/F.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { Effect } from "effect"
|
|
2
|
-
|
|
3
|
-
import type { FError } from "./errors.ts"
|
|
4
|
-
import type { ProtocolDefinition } from "./Protocol.ts"
|
|
5
|
-
|
|
6
|
-
export type F<Self, D extends ProtocolDefinition> = <Method extends keyof D["methods"]>(
|
|
7
|
-
method: Method,
|
|
8
|
-
) => (
|
|
9
|
-
payload: D["methods"][Method]["payload"]["Type"],
|
|
10
|
-
) => Effect.Effect<D["methods"][Method]["success"]["Type"], FError<D>, Self>
|
package/_diagnostic.ts
DELETED
package/_util/Mutex.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Context, Effect, Layer, Semaphore } from "effect"
|
|
2
|
-
|
|
3
|
-
export class Mutex extends Context.Service<
|
|
4
|
-
Mutex,
|
|
5
|
-
<A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
|
|
6
|
-
>()("liminal/Mutex") {}
|
|
7
|
-
|
|
8
|
-
export const layer = Effect.gen(function* () {
|
|
9
|
-
const mutex = yield* Semaphore.make(1)
|
|
10
|
-
return mutex.withPermits(1)
|
|
11
|
-
}).pipe(Layer.effect(Mutex))
|
|
12
|
-
|
|
13
|
-
export const task = <A, E, R>(effect: Effect.Effect<A, E, R>) => Mutex.asEffect().pipe(Effect.flatMap((f) => f(effect)))
|
package/dist/Accumulator.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { Types, Option, Ref, PubSub, Stream, Effect, Context, Layer } from "effect";
|
|
2
|
-
declare const TypeId: "~liminal/Accumulator";
|
|
3
|
-
export interface Service<State> {
|
|
4
|
-
readonly ref: Ref.Ref<State>;
|
|
5
|
-
readonly pubsub: PubSub.PubSub<State>;
|
|
6
|
-
}
|
|
7
|
-
export interface AccumulatorLayerConfig<Item, E, R, State, E2, R2, E3, R3> {
|
|
8
|
-
readonly source: Stream.Stream<Item, E, R>;
|
|
9
|
-
readonly reduce: (item: Item) => (state: State) => Effect.Effect<State, E2, R2>;
|
|
10
|
-
readonly initial: (item: Item) => Effect.Effect<Option.Option<State>, E3, R3>;
|
|
11
|
-
}
|
|
12
|
-
export type Reduce<State, Item, K extends Types.Tags<Item> = Types.Tags<Item>, E = never, R = never> = (item: Types.ExtractTag<Item, K>) => (accumulator: State) => Effect.Effect<State, E, R>;
|
|
13
|
-
export interface Accumulator<Self, Id extends string, State> extends Context.Service<Self, Service<State>> {
|
|
14
|
-
new (_: never): Context.ServiceClass.Shape<Id, Service<State>>;
|
|
15
|
-
readonly [TypeId]: typeof TypeId;
|
|
16
|
-
readonly get: Effect.Effect<State, never, Self>;
|
|
17
|
-
readonly stream: Stream.Stream<State, never, Self>;
|
|
18
|
-
readonly reducer: <Item>() => <K extends Types.Tags<Item>, E, R>(_tag: K, f: Reduce<State, Item, K, E, R>) => Reduce<State, Item, K, E, R>;
|
|
19
|
-
readonly layer: <Item, E, R, E2, R2, E3, R3>(config: AccumulatorLayerConfig<Item, E, R, State, E2, R2, E3, R3>) => Layer.Layer<Self, E | E2 | E3, R | R2 | R3>;
|
|
20
|
-
}
|
|
21
|
-
export declare const Service: <Self, State>() => <Id extends string>(id: Id) => Accumulator<Self, Id, State>;
|
|
22
|
-
export {};
|
package/dist/Accumulator.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { Deferred, Types, Option, Ref, PubSub, Stream, Effect, Context, Layer, Semaphore } from "effect";
|
|
2
|
-
import { diagnostic } from "./_diagnostic.js";
|
|
3
|
-
const { debug } = diagnostic("Accumulator");
|
|
4
|
-
const TypeId = "~liminal/Accumulator";
|
|
5
|
-
export const Service = () => (id) => {
|
|
6
|
-
const tag = Context.Service()(id);
|
|
7
|
-
const get = tag.asEffect().pipe(Effect.map(({ ref }) => ref), Effect.flatMap(Ref.get));
|
|
8
|
-
const stream = tag.asEffect().pipe(Effect.map(({ pubsub }) => Stream.fromPubSub(pubsub)), Stream.unwrap);
|
|
9
|
-
const reducer = () => (_tag, f) => f;
|
|
10
|
-
const layer = ({ source, initial, reduce, }) => Effect.gen(function* () {
|
|
11
|
-
const semaphore = yield* Semaphore.make(1);
|
|
12
|
-
const deferred = yield* Deferred.make();
|
|
13
|
-
const pubsub = yield* PubSub.unbounded({ replay: 1 });
|
|
14
|
-
yield* source.pipe(Stream.runForEach(Effect.fn(function* (item) {
|
|
15
|
-
if (!(yield* Deferred.isDone(deferred))) {
|
|
16
|
-
const match = yield* initial(item);
|
|
17
|
-
if (Option.isSome(match)) {
|
|
18
|
-
const { value } = match;
|
|
19
|
-
yield* Deferred.succeed(deferred, value);
|
|
20
|
-
yield* debug("InitializedState", { state: value });
|
|
21
|
-
}
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
const current = yield* Ref.get(ref);
|
|
25
|
-
const reduced = yield* reduce(item)(current);
|
|
26
|
-
yield* Ref.set(ref, reduced);
|
|
27
|
-
yield* PubSub.publish(pubsub, reduced);
|
|
28
|
-
yield* debug("ReducedState", { item, previous: current, current: reduced });
|
|
29
|
-
}, semaphore.withPermits(1))), Effect.forkScoped);
|
|
30
|
-
const initial_ = yield* Deferred.await(deferred);
|
|
31
|
-
const ref = yield* Ref.make(initial_);
|
|
32
|
-
yield* PubSub.publish(pubsub, initial_);
|
|
33
|
-
return { ref, pubsub };
|
|
34
|
-
}).pipe(Layer.effect(tag));
|
|
35
|
-
return Object.assign(tag, { [TypeId]: TypeId, get, stream, reducer, layer });
|
|
36
|
-
};
|
|
37
|
-
//# sourceMappingURL=Accumulator.js.map
|
package/dist/Accumulator.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Accumulator.js","sourceRoot":"","sources":["../Accumulator.ts"],"names":[],"mappings":"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"}
|
package/dist/F.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { Effect } from "effect";
|
|
2
|
-
import type { FError } from "./errors.ts";
|
|
3
|
-
import type { ProtocolDefinition } from "./Protocol.ts";
|
|
4
|
-
export type F<Self, D extends ProtocolDefinition> = <Method extends keyof D["methods"]>(method: Method) => (payload: D["methods"][Method]["payload"]["Type"]) => Effect.Effect<D["methods"][Method]["success"]["Type"], FError<D>, Self>;
|
package/dist/F.js
DELETED
package/dist/F.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"F.js","sourceRoot":"","sources":["../F.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA"}
|
package/dist/_diagnostic.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export declare const diagnostic: (module: string) => {
|
|
2
|
-
debug: (event: string, annotations?: Record<string, unknown>) => import("effect/Effect").Effect<void, never, never>;
|
|
3
|
-
span: (operation: string, options?: import("effect/Tracer").SpanOptions | undefined) => <A, E, R>(effect: import("effect/Effect").Effect<A, E, R>) => import("effect/Effect").Effect<A, E, Exclude<R, import("effect/Tracer").ParentSpan>>;
|
|
4
|
-
};
|
package/dist/_diagnostic.js
DELETED
package/dist/_diagnostic.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"_diagnostic.js","sourceRoot":"","sources":["../_diagnostic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA;AAEjD,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAAA"}
|
package/dist/_util/Mutex.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { Context, Effect, Layer } from "effect";
|
|
2
|
-
declare const Mutex_base: Context.ServiceClass<Mutex, "liminal/Mutex", <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>>;
|
|
3
|
-
export declare class Mutex extends Mutex_base {
|
|
4
|
-
}
|
|
5
|
-
export declare const layer: Layer.Layer<Mutex, never, never>;
|
|
6
|
-
export declare const task: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R | Mutex>;
|
|
7
|
-
export {};
|
package/dist/_util/Mutex.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { Context, Effect, Layer, Semaphore } from "effect";
|
|
2
|
-
export class Mutex extends Context.Service()("liminal/Mutex") {
|
|
3
|
-
}
|
|
4
|
-
export const layer = Effect.gen(function* () {
|
|
5
|
-
const mutex = yield* Semaphore.make(1);
|
|
6
|
-
return mutex.withPermits(1);
|
|
7
|
-
}).pipe(Layer.effect(Mutex));
|
|
8
|
-
export const task = (effect) => Mutex.asEffect().pipe(Effect.flatMap((f) => f(effect)));
|
|
9
|
-
//# sourceMappingURL=Mutex.js.map
|