liminal 0.17.5 → 0.17.7
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 +29 -45
- package/ActorTransport.ts +17 -0
- package/Audition.ts +27 -48
- package/CHANGELOG.md +12 -0
- package/Client.ts +104 -173
- package/ClientDirectory.ts +104 -0
- package/ClientHandle.ts +6 -13
- package/F.ts +5 -7
- package/Method.ts +5 -13
- package/Protocol.ts +85 -68
- package/Send.ts +5 -9
- package/_util/schema.ts +7 -0
- package/dist/Actor.d.ts +15 -17
- package/dist/Actor.js +1 -3
- package/dist/Actor.js.map +1 -1
- package/dist/ActorTransport.d.ts +7 -0
- package/dist/ActorTransport.js +2 -0
- package/dist/ActorTransport.js.map +1 -0
- package/dist/Audition.d.ts +7 -7
- package/dist/Audition.js.map +1 -1
- package/dist/Client.d.ts +23 -28
- package/dist/Client.js +63 -67
- package/dist/Client.js.map +1 -1
- package/dist/ClientDirectory.d.ts +21 -0
- package/dist/ClientDirectory.js +41 -0
- package/dist/ClientDirectory.js.map +1 -0
- package/dist/ClientHandle.d.ts +6 -5
- package/dist/ClientHandle.js.map +1 -1
- package/dist/F.d.ts +2 -2
- package/dist/F.js +1 -1
- package/dist/F.js.map +1 -1
- package/dist/Method.d.ts +5 -10
- package/dist/Method.js +1 -1
- package/dist/Method.js.map +1 -1
- package/dist/Protocol.d.ts +51 -34
- package/dist/Protocol.js +20 -22
- package/dist/Protocol.js.map +1 -1
- package/dist/Send.d.ts +2 -1
- package/dist/_util/schema.d.ts +4 -0
- package/dist/_util/schema.js +5 -0
- package/dist/_util/schema.js.map +1 -0
- package/dist/errors.d.ts +5 -5
- package/dist/errors.js +2 -2
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/errors.ts +5 -5
- package/index.ts +2 -0
- package/package.json +1 -1
package/Client.ts
CHANGED
|
@@ -21,31 +21,20 @@ import {
|
|
|
21
21
|
Cause,
|
|
22
22
|
Result,
|
|
23
23
|
flow,
|
|
24
|
-
identity,
|
|
25
24
|
} from "effect"
|
|
26
25
|
import { Socket } from "effect/unstable/socket"
|
|
27
26
|
import { Worker } from "effect/unstable/workers"
|
|
28
27
|
|
|
29
|
-
import type { MethodDefinition } from "./Method.ts"
|
|
30
|
-
|
|
31
28
|
import * as Diagnostic from "./_util/Diagnostic.ts"
|
|
29
|
+
import { decodeJsonString, encodeJsonString } from "./_util/schema.ts"
|
|
32
30
|
import { type ClientError, AuditionError, ConnectionError, type FError, UnresolvedError } from "./errors.ts"
|
|
33
31
|
import { type F } from "./F.ts"
|
|
34
|
-
import
|
|
32
|
+
import { Protocol, type ProtocolDefinition } from "./Protocol.ts"
|
|
35
33
|
|
|
36
34
|
const { debug, span } = Diagnostic.module("Client")
|
|
37
35
|
|
|
38
36
|
export const TypeId = "~liminal/Client" as const
|
|
39
37
|
|
|
40
|
-
export interface ClientDefinition<
|
|
41
|
-
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
42
|
-
EventDefinitions extends Record<string, S.Struct.Fields>,
|
|
43
|
-
> {
|
|
44
|
-
readonly methods: MethodDefinitions
|
|
45
|
-
|
|
46
|
-
readonly events: EventDefinitions
|
|
47
|
-
}
|
|
48
|
-
|
|
49
38
|
export interface ReplayConfig {
|
|
50
39
|
readonly mode: "startup" | "all-subscribers"
|
|
51
40
|
|
|
@@ -58,60 +47,49 @@ interface EventTake<A, E> {
|
|
|
58
47
|
readonly take: Take.Take<A, E>
|
|
59
48
|
}
|
|
60
49
|
|
|
61
|
-
export interface Session<
|
|
62
|
-
|
|
63
|
-
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
64
|
-
EventDefinitions extends Record<string, S.Struct.Fields>,
|
|
65
|
-
> {
|
|
66
|
-
readonly events: Stream.Stream<ReturnType<typeof S.TaggedUnion<EventDefinitions>>["Type"], ClientError>
|
|
50
|
+
export interface Session<Self, D extends ProtocolDefinition> {
|
|
51
|
+
readonly events: Stream.Stream<ReturnType<typeof S.TaggedUnion<D["events"]>>["Type"], ClientError | S.SchemaError>
|
|
67
52
|
|
|
68
|
-
readonly f: F<
|
|
53
|
+
readonly f: F<Self, D>
|
|
69
54
|
|
|
70
55
|
readonly end: Effect.Effect<void>
|
|
71
56
|
}
|
|
72
57
|
|
|
73
|
-
export type Service<
|
|
74
|
-
ClientSelf,
|
|
75
|
-
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
76
|
-
EventDefinitions extends Record<string, S.Struct.Fields>,
|
|
77
|
-
> = RcRef.RcRef<Session<ClientSelf, MethodDefinitions, EventDefinitions>, ClientError>
|
|
58
|
+
export type Service<ClientSelf, D extends ProtocolDefinition> = RcRef.RcRef<Session<ClientSelf, D>, ClientError>
|
|
78
59
|
|
|
79
|
-
export interface Client<
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
> extends Context.Service<ClientSelf, Service<ClientSelf, MethodDefinitions, EventDefinitions>> {
|
|
85
|
-
new (_: never): Context.ServiceClass.Shape<ClientId, Service<ClientSelf, MethodDefinitions, EventDefinitions>>
|
|
60
|
+
export interface Client<Self, ClientId extends string, D extends ProtocolDefinition> extends Context.Service<
|
|
61
|
+
Self,
|
|
62
|
+
Service<Self, D>
|
|
63
|
+
> {
|
|
64
|
+
new (_: never): Context.ServiceClass.Shape<ClientId, Service<Self, D>>
|
|
86
65
|
|
|
87
66
|
readonly [TypeId]: typeof TypeId
|
|
88
67
|
|
|
89
|
-
readonly definition:
|
|
68
|
+
readonly definition: D
|
|
90
69
|
|
|
91
|
-
readonly
|
|
70
|
+
readonly protocol: Protocol<D>
|
|
92
71
|
|
|
93
|
-
readonly events: Stream.Stream<
|
|
72
|
+
readonly events: Stream.Stream<
|
|
73
|
+
ReturnType<typeof S.TaggedUnion<D["events"]>>["Type"],
|
|
74
|
+
ClientError | S.SchemaError,
|
|
75
|
+
Self
|
|
76
|
+
>
|
|
94
77
|
|
|
95
|
-
readonly f: F<
|
|
78
|
+
readonly f: F<Self, D>
|
|
96
79
|
|
|
97
|
-
readonly invalidate: Effect.Effect<void, never,
|
|
80
|
+
readonly invalidate: Effect.Effect<void, never, Self>
|
|
98
81
|
}
|
|
99
82
|
|
|
100
83
|
export const Service =
|
|
101
|
-
<
|
|
102
|
-
<
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
>(
|
|
107
|
-
id: ClientId,
|
|
108
|
-
definition: ClientDefinition<MethodDefinitions, EventDefinitions>,
|
|
109
|
-
): Client<ClientSelf, ClientId, MethodDefinitions, EventDefinitions> => {
|
|
110
|
-
const tag = Context.Service<ClientSelf, Service<ClientSelf, MethodDefinitions, EventDefinitions>>()(id)
|
|
84
|
+
<Self>() =>
|
|
85
|
+
<Id extends string, D extends ProtocolDefinition>(id: Id, definition: D): Client<Self, Id, D> => {
|
|
86
|
+
const tag = Context.Service<Self, Service<Self, D>>()(id)
|
|
87
|
+
|
|
88
|
+
const protocol = Protocol(definition)
|
|
111
89
|
|
|
112
90
|
const events = tag.asEffect().pipe(Effect.flatMap(RcRef.get), Effect.map(Struct.get("events")), Stream.unwrap)
|
|
113
91
|
|
|
114
|
-
const f: F<
|
|
92
|
+
const f: F<Self, D> = (_tag) =>
|
|
115
93
|
Effect.fnUntraced(function* (value) {
|
|
116
94
|
const { f } = yield* tag.asEffect().pipe(Effect.flatMap(RcRef.get))
|
|
117
95
|
return yield* f(_tag)(value)
|
|
@@ -131,54 +109,33 @@ export const Service =
|
|
|
131
109
|
return Object.assign(tag, {
|
|
132
110
|
[TypeId]: TypeId,
|
|
133
111
|
definition,
|
|
134
|
-
|
|
112
|
+
protocol,
|
|
135
113
|
events,
|
|
136
114
|
f,
|
|
137
115
|
invalidate,
|
|
138
116
|
})
|
|
139
117
|
}
|
|
140
118
|
|
|
141
|
-
export interface
|
|
142
|
-
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
143
|
-
EventDefinitions extends Record<string, S.Struct.Fields>,
|
|
144
|
-
> {
|
|
119
|
+
export interface ClientTransport<D extends ProtocolDefinition> {
|
|
145
120
|
readonly listen: (
|
|
146
|
-
publish: (
|
|
147
|
-
|
|
148
|
-
| Protocol.ProtocolSchemas<MethodDefinitions, EventDefinitions>["actor"]["Type"]
|
|
149
|
-
| typeof Protocol.TransportFailure.Type,
|
|
150
|
-
) => Effect.Effect<void, ClientError>,
|
|
151
|
-
) => Effect.Effect<
|
|
152
|
-
void,
|
|
153
|
-
ClientError,
|
|
154
|
-
Scope.Scope | Protocol.ProtocolSchemas<MethodDefinitions, EventDefinitions>["actor"]["DecodingServices"]
|
|
155
|
-
>
|
|
121
|
+
publish: (message: Protocol<D>["Actor"]["Type"]) => Effect.Effect<void, ClientError>,
|
|
122
|
+
) => Effect.Effect<void, ClientError | S.SchemaError, Scope.Scope | Protocol<D>["Actor"]["DecodingServices"]>
|
|
156
123
|
|
|
157
124
|
readonly send: (
|
|
158
|
-
message: Protocol
|
|
159
|
-
) => Effect.Effect<
|
|
160
|
-
void,
|
|
161
|
-
ConnectionError,
|
|
162
|
-
Protocol.ProtocolSchemas<MethodDefinitions, EventDefinitions>["f"]["payload"]["EncodingServices"]
|
|
163
|
-
>
|
|
125
|
+
message: Protocol<D>["F"]["Payload"]["Type"],
|
|
126
|
+
) => Effect.Effect<void, ClientError | S.SchemaError, Protocol<D>["F"]["Payload"]["EncodingServices"]>
|
|
164
127
|
}
|
|
165
128
|
|
|
166
|
-
const make = <
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
170
|
-
EventDefinitions extends Record<string, S.Struct.Fields>,
|
|
171
|
-
R,
|
|
172
|
-
>(
|
|
173
|
-
client: Client<ClientSelf, ClientId, MethodDefinitions, EventDefinitions>,
|
|
174
|
-
build: Effect.Effect<Transport<MethodDefinitions, EventDefinitions>, ClientError, R | Scope.Scope>,
|
|
129
|
+
const make = <Self, Id extends string, D extends ProtocolDefinition, R>(
|
|
130
|
+
client: Client<Self, Id, D>,
|
|
131
|
+
build: Effect.Effect<ClientTransport<D>, ClientError, R | Scope.Scope>,
|
|
175
132
|
replay?: ReplayConfig | undefined,
|
|
176
133
|
) =>
|
|
177
134
|
Effect.gen(function* () {
|
|
178
|
-
const rcr: RcRef.RcRef<Session<
|
|
135
|
+
const rcr: RcRef.RcRef<Session<Self, D>, ClientError> = yield* RcRef.make({
|
|
179
136
|
acquire: Effect.gen(function* () {
|
|
180
|
-
type _ = typeof client.
|
|
181
|
-
type Event = ReturnType<typeof S.TaggedUnion<
|
|
137
|
+
type _ = typeof client.protocol
|
|
138
|
+
type Event = ReturnType<typeof S.TaggedUnion<D["events"]>>["Type"]
|
|
182
139
|
|
|
183
140
|
yield* debug("AcquisitionStarted")
|
|
184
141
|
|
|
@@ -186,7 +143,7 @@ const make = <
|
|
|
186
143
|
|
|
187
144
|
const audition = yield* Deferred.make<void>()
|
|
188
145
|
|
|
189
|
-
const inflights: Record<string, Deferred.Deferred<_["
|
|
146
|
+
const inflights: Record<string, Deferred.Deferred<_["F"]["Success"]["Type"], FError<D>>> = {}
|
|
190
147
|
let callId = 0
|
|
191
148
|
let takeCount = 0
|
|
192
149
|
const pubsub = yield* PubSub.unbounded<EventTake<Event, ClientError>>()
|
|
@@ -228,33 +185,38 @@ const make = <
|
|
|
228
185
|
const fiber = yield* listen(
|
|
229
186
|
Effect.fnUntraced(function* (message) {
|
|
230
187
|
switch (message._tag) {
|
|
231
|
-
case "
|
|
232
|
-
yield* debug("
|
|
188
|
+
case "Audition.Success": {
|
|
189
|
+
yield* debug("Audition.Succeeded")
|
|
233
190
|
yield* Deferred.succeed(audition, void 0)
|
|
234
191
|
return
|
|
235
192
|
}
|
|
193
|
+
case "Audition.Failure": {
|
|
194
|
+
const { expected, actual } = message
|
|
195
|
+
yield* debug("Audition.Failed", { expected, actual })
|
|
196
|
+
return yield* new AuditionError({ value: { expected, actual } })
|
|
197
|
+
}
|
|
236
198
|
case "Event": {
|
|
237
199
|
const { event } = message
|
|
238
|
-
yield* debug("
|
|
200
|
+
yield* debug("Event.Emitted", { event })
|
|
239
201
|
yield* publishTake([event as never], true)
|
|
240
202
|
return
|
|
241
203
|
}
|
|
242
|
-
case "
|
|
243
|
-
case "
|
|
204
|
+
case "F.Success":
|
|
205
|
+
case "F.Failure": {
|
|
244
206
|
const { id } = message
|
|
245
207
|
const deferred = inflights[id]
|
|
246
208
|
if (deferred) {
|
|
247
209
|
delete inflights[id]
|
|
248
210
|
switch (message._tag) {
|
|
249
|
-
case "
|
|
211
|
+
case "F.Success": {
|
|
250
212
|
const { _tag, value } = message.success as never
|
|
251
|
-
yield* debug("
|
|
213
|
+
yield* debug("Call.Succeeded", { id, _tag, value })
|
|
252
214
|
yield* Deferred.succeed(deferred, value)
|
|
253
215
|
return
|
|
254
216
|
}
|
|
255
|
-
case "
|
|
217
|
+
case "F.Failure": {
|
|
256
218
|
const { _tag, value } = message.failure as never
|
|
257
|
-
yield* debug("
|
|
219
|
+
yield* debug("Call.Failed", { id, _tag, value })
|
|
258
220
|
yield* Deferred.fail(deferred, value)
|
|
259
221
|
return
|
|
260
222
|
}
|
|
@@ -262,27 +224,17 @@ const make = <
|
|
|
262
224
|
}
|
|
263
225
|
return
|
|
264
226
|
}
|
|
265
|
-
case "AuditionFailure": {
|
|
266
|
-
const { client, routed } = message
|
|
267
|
-
yield* debug("AuditionFailed", { client, routed })
|
|
268
|
-
return yield* new AuditionError({ value: { client, routed } })
|
|
269
|
-
}
|
|
270
227
|
case "Disconnect": {
|
|
271
228
|
yield* debug("Disconnected")
|
|
272
229
|
return
|
|
273
230
|
}
|
|
274
|
-
case "TransportFailure": {
|
|
275
|
-
const { cause } = message
|
|
276
|
-
yield* debug("TransportFailed", { cause })
|
|
277
|
-
return yield* new ConnectionError({ cause })
|
|
278
|
-
}
|
|
279
231
|
}
|
|
280
232
|
}),
|
|
281
233
|
).pipe(
|
|
282
234
|
Effect.ensuring(
|
|
283
235
|
Effect.all(
|
|
284
236
|
[
|
|
285
|
-
debug("
|
|
237
|
+
debug("Client.Closed", { unresolved: Record.keys(inflights).length }),
|
|
286
238
|
Deferred.succeed(audition, void 0),
|
|
287
239
|
RcRef.invalidate(rcr),
|
|
288
240
|
],
|
|
@@ -353,9 +305,9 @@ const make = <
|
|
|
353
305
|
|
|
354
306
|
yield* Deferred.await(audition)
|
|
355
307
|
|
|
356
|
-
const encodingServices = yield* Effect.context<_["
|
|
308
|
+
const encodingServices = yield* Effect.context<_["F"]["Payload"]["EncodingServices"]>()
|
|
357
309
|
|
|
358
|
-
const f: F<
|
|
310
|
+
const f: F<Self, D> = (_tag) =>
|
|
359
311
|
Effect.fnUntraced(
|
|
360
312
|
function* (value) {
|
|
361
313
|
const exit = fiber.pollUnsafe()
|
|
@@ -365,17 +317,17 @@ const make = <
|
|
|
365
317
|
onFailure: flow(
|
|
366
318
|
Cause.findError,
|
|
367
319
|
Result.match({
|
|
368
|
-
onSuccess:
|
|
320
|
+
onSuccess: Effect.fail,
|
|
369
321
|
onFailure: () => new UnresolvedError(),
|
|
370
322
|
}),
|
|
371
323
|
),
|
|
372
324
|
})
|
|
373
325
|
}
|
|
374
326
|
const id = callId++
|
|
375
|
-
const inflight = yield* Deferred.make<_["
|
|
327
|
+
const inflight = yield* Deferred.make<_["F"]["Success"]["Type"], FError<D>>()
|
|
376
328
|
inflights[id] = inflight
|
|
377
329
|
yield* send({
|
|
378
|
-
_tag: "
|
|
330
|
+
_tag: "F.Payload",
|
|
379
331
|
id,
|
|
380
332
|
payload: { _tag, value } as never,
|
|
381
333
|
})
|
|
@@ -383,7 +335,7 @@ const make = <
|
|
|
383
335
|
Deferred.await(inflight),
|
|
384
336
|
Fiber.await(fiber).pipe(
|
|
385
337
|
Effect.flatMap(
|
|
386
|
-
(exit): Effect.Effect<never, ClientError | UnresolvedError> =>
|
|
338
|
+
(exit): Effect.Effect<never, ClientError | UnresolvedError | S.SchemaError> =>
|
|
387
339
|
Exit.match(exit, {
|
|
388
340
|
onSuccess: () => new UnresolvedError().asEffect(),
|
|
389
341
|
onFailure: flow(
|
|
@@ -410,29 +362,28 @@ const make = <
|
|
|
410
362
|
return rcr
|
|
411
363
|
}).pipe(Layer.effect(client))
|
|
412
364
|
|
|
413
|
-
export const layerSocket = <
|
|
414
|
-
ClientSelf,
|
|
415
|
-
ClientId extends string,
|
|
416
|
-
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
417
|
-
EventDefinitions extends Record<string, S.Struct.Fields>,
|
|
418
|
-
>({
|
|
365
|
+
export const layerSocket = <Self, Id extends string, D extends ProtocolDefinition>({
|
|
419
366
|
client,
|
|
420
367
|
url,
|
|
421
368
|
protocols,
|
|
422
369
|
replay,
|
|
423
370
|
}: {
|
|
424
|
-
readonly client: Client<
|
|
371
|
+
readonly client: Client<Self, Id, D>
|
|
425
372
|
readonly url?: string | undefined
|
|
426
373
|
readonly protocols?: string | Array<string> | undefined
|
|
427
374
|
readonly replay?: ReplayConfig | undefined
|
|
428
375
|
}): Layer.Layer<
|
|
429
|
-
|
|
376
|
+
Self,
|
|
430
377
|
never,
|
|
431
378
|
| Socket.WebSocketConstructor
|
|
432
|
-
| Protocol
|
|
433
|
-
| Protocol
|
|
434
|
-
> =>
|
|
435
|
-
|
|
379
|
+
| Protocol<D>["Actor"]["DecodingServices"]
|
|
380
|
+
| Protocol<D>["F"]["Payload"]["EncodingServices"]
|
|
381
|
+
> => {
|
|
382
|
+
const { F, Actor } = client.protocol
|
|
383
|
+
const encodeFPayload = encodeJsonString(F.Payload)
|
|
384
|
+
const decodeActor = decodeJsonString(Actor)
|
|
385
|
+
|
|
386
|
+
return make<Self, Id, D, Socket.WebSocketConstructor>(
|
|
436
387
|
client,
|
|
437
388
|
Effect.gen(function* () {
|
|
438
389
|
const socket = yield* Socket.makeWebSocket(url ?? "/", {
|
|
@@ -444,49 +395,29 @@ export const layerSocket = <
|
|
|
444
395
|
.runRaw((raw) =>
|
|
445
396
|
pipe(
|
|
446
397
|
raw instanceof Uint8Array ? new TextDecoder().decode(raw) : raw,
|
|
447
|
-
|
|
398
|
+
decodeActor,
|
|
448
399
|
Effect.andThen(publish),
|
|
449
400
|
),
|
|
450
401
|
)
|
|
451
402
|
.pipe(
|
|
452
|
-
Effect.
|
|
453
|
-
|
|
403
|
+
Effect.catchIf(
|
|
404
|
+
Socket.isSocketError,
|
|
454
405
|
Effect.fnUntraced(function* (cause) {
|
|
455
406
|
const { reason } = cause
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
case "SocketOpenError": {
|
|
460
|
-
yield* debug(reason._tag, { cause })
|
|
461
|
-
return yield* publish({ _tag: "TransportFailure", cause })
|
|
462
|
-
}
|
|
463
|
-
case "SocketCloseError": {
|
|
464
|
-
const { code, closeReason } = reason
|
|
465
|
-
switch (code) {
|
|
466
|
-
case 1000: {
|
|
467
|
-
return yield* publish({ _tag: "Disconnect" })
|
|
468
|
-
}
|
|
469
|
-
case 4003: {
|
|
470
|
-
return yield* S.decodeUnknownEffect(S.fromJsonString(Protocol.AuditionFailure))(
|
|
471
|
-
closeReason,
|
|
472
|
-
).pipe(Effect.andThen(publish))
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
yield* debug("SocketCloseError", { cause })
|
|
476
|
-
return yield* publish({ _tag: "TransportFailure", cause })
|
|
477
|
-
}
|
|
407
|
+
if (reason._tag === "SocketCloseError" && reason.code === 1000) {
|
|
408
|
+
yield* debug("Socket.Disconnected")
|
|
409
|
+
return yield* publish({ _tag: "Disconnect" })
|
|
478
410
|
}
|
|
411
|
+
yield* debug(`SocketErrored.${reason._tag}`, { cause })
|
|
412
|
+
return yield* new ConnectionError({ cause })
|
|
479
413
|
}),
|
|
480
414
|
),
|
|
481
|
-
Effect.catchTag("SchemaError", Effect.die),
|
|
482
415
|
)
|
|
483
416
|
}, span("listen")),
|
|
484
417
|
send: Effect.fnUntraced(
|
|
485
418
|
function* (v) {
|
|
486
419
|
const write = yield* socket.writer
|
|
487
|
-
const message = yield*
|
|
488
|
-
Effect.mapError((cause) => new ConnectionError({ cause })),
|
|
489
|
-
)
|
|
420
|
+
const message = yield* encodeFPayload(v)
|
|
490
421
|
yield* write(message).pipe(
|
|
491
422
|
Effect.catchTag("SocketError", (cause) => new ConnectionError({ cause }).asEffect()),
|
|
492
423
|
)
|
|
@@ -498,37 +429,28 @@ export const layerSocket = <
|
|
|
498
429
|
}),
|
|
499
430
|
replay,
|
|
500
431
|
)
|
|
432
|
+
}
|
|
501
433
|
|
|
502
|
-
export const layerWorker = <
|
|
503
|
-
ClientSelf,
|
|
504
|
-
ClientId extends string,
|
|
505
|
-
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
506
|
-
EventDefinitions extends Record<string, S.Struct.Fields>,
|
|
507
|
-
>({
|
|
434
|
+
export const layerWorker = <Self, Id extends string, D extends ProtocolDefinition, T extends Protocol<D>>({
|
|
508
435
|
client,
|
|
509
436
|
replay,
|
|
510
437
|
}: {
|
|
511
|
-
readonly client: Client<
|
|
438
|
+
readonly client: Client<Self, Id, D>
|
|
512
439
|
readonly replay?: ReplayConfig | undefined
|
|
513
440
|
}): Layer.Layer<
|
|
514
|
-
|
|
441
|
+
Self,
|
|
515
442
|
never,
|
|
516
|
-
| Worker.
|
|
517
|
-
| Worker.Spawner
|
|
518
|
-
| Protocol.ProtocolSchemas<MethodDefinitions, EventDefinitions>["actor"]["DecodingServices"]
|
|
519
|
-
| Protocol.ProtocolSchemas<MethodDefinitions, EventDefinitions>["f"]["payload"]["EncodingServices"]
|
|
443
|
+
Worker.WorkerPlatform | Worker.Spawner | T["Actor"]["DecodingServices"] | T["F"]["Payload"]["EncodingServices"]
|
|
520
444
|
> =>
|
|
521
|
-
make<
|
|
445
|
+
make<Self, Id, D, Worker.WorkerPlatform | Worker.Spawner>(
|
|
522
446
|
client,
|
|
523
447
|
Effect.gen(function* () {
|
|
524
|
-
type T = typeof client.schema
|
|
525
|
-
|
|
526
448
|
const platform = yield* Worker.WorkerPlatform
|
|
527
449
|
const backing = yield* platform
|
|
528
|
-
.spawn<T["
|
|
450
|
+
.spawn<T["Actor"]["Type"], T["Client"]["Type"]>(0)
|
|
529
451
|
.pipe(Effect.catchTag("WorkerError", (cause) => new ConnectionError({ cause }).asEffect()))
|
|
530
452
|
|
|
531
|
-
const send = (message: T["
|
|
453
|
+
const send = (message: T["Client"]["Type"]) =>
|
|
532
454
|
backing.send(message).pipe(
|
|
533
455
|
Effect.catchTag("WorkerError", (cause) => new ConnectionError({ cause }).asEffect()),
|
|
534
456
|
span("send"),
|
|
@@ -537,18 +459,27 @@ export const layerWorker = <
|
|
|
537
459
|
return {
|
|
538
460
|
listen: Effect.fnUntraced(function* (publish) {
|
|
539
461
|
const stop = yield* Deferred.make<void>()
|
|
540
|
-
yield*
|
|
541
|
-
|
|
462
|
+
yield* backing
|
|
463
|
+
.run(
|
|
542
464
|
Effect.fnUntraced(function* (message) {
|
|
543
465
|
yield* publish(message)
|
|
544
|
-
if (message._tag === "Disconnect" || message._tag === "
|
|
466
|
+
if (message._tag === "Disconnect" || message._tag === "Audition.Failure") {
|
|
545
467
|
yield* Deferred.succeed(stop, void 0)
|
|
546
468
|
}
|
|
547
469
|
}),
|
|
548
|
-
{
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
470
|
+
{
|
|
471
|
+
onSpawn: backing
|
|
472
|
+
.send({
|
|
473
|
+
_tag: "Audition.Payload",
|
|
474
|
+
client: client.key,
|
|
475
|
+
})
|
|
476
|
+
.pipe(Effect.orDie),
|
|
477
|
+
},
|
|
478
|
+
)
|
|
479
|
+
.pipe(
|
|
480
|
+
Effect.raceFirst(Deferred.await(stop)),
|
|
481
|
+
Effect.catchTag("WorkerError", (cause) => new ConnectionError({ cause }).asEffect()),
|
|
482
|
+
)
|
|
552
483
|
}, span("listen")),
|
|
553
484
|
send,
|
|
554
485
|
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Schema as S, Effect, Cause, Ref } from "effect"
|
|
2
|
+
|
|
3
|
+
import type { TopFromString } from "./_util/schema.ts"
|
|
4
|
+
import type { Actor } from "./Actor.ts"
|
|
5
|
+
import type { ActorTransport } from "./ActorTransport.ts"
|
|
6
|
+
import type { ProtocolDefinition, Disconnect, Protocol } from "./Protocol.ts"
|
|
7
|
+
|
|
8
|
+
import * as Diagnostic from "./_util/Diagnostic.ts"
|
|
9
|
+
import { phantom } from "./_util/phantom.ts"
|
|
10
|
+
import * as ClientHandle from "./ClientHandle.ts"
|
|
11
|
+
|
|
12
|
+
const { span } = Diagnostic.module("ClientDirectory")
|
|
13
|
+
|
|
14
|
+
export interface ClientDirectory<
|
|
15
|
+
Raw,
|
|
16
|
+
ActorSelf,
|
|
17
|
+
AttachmentFields extends S.Struct.Fields,
|
|
18
|
+
D extends ProtocolDefinition,
|
|
19
|
+
> {
|
|
20
|
+
readonly "": {
|
|
21
|
+
readonly Handle: ClientHandle.ClientHandle<ActorSelf, AttachmentFields, D>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
readonly handles: ReadonlySet<this[""]["Handle"]>
|
|
25
|
+
|
|
26
|
+
readonly register: (
|
|
27
|
+
raw: Raw,
|
|
28
|
+
attachments: S.Struct<AttachmentFields>["Type"],
|
|
29
|
+
) => Effect.Effect<this[""]["Handle"], S.SchemaError, S.Struct<AttachmentFields>["EncodingServices"]>
|
|
30
|
+
|
|
31
|
+
readonly get: (raw: Raw) => Effect.Effect<this[""]["Handle"], Cause.NoSuchElementError>
|
|
32
|
+
|
|
33
|
+
readonly unregister: (raw: Raw) => Effect.Effect<void>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface HandleEncoders<T, AttachmentFields extends S.Struct.Fields, D extends ProtocolDefinition> {
|
|
37
|
+
attachments: (
|
|
38
|
+
value: S.Struct<AttachmentFields>["Type"],
|
|
39
|
+
) => Effect.Effect<T, S.SchemaError, S.Struct<AttachmentFields>["EncodingServices"]>
|
|
40
|
+
event: (
|
|
41
|
+
value: Protocol<D>["Event"]["Type"],
|
|
42
|
+
) => Effect.Effect<T, S.SchemaError, Protocol<D>["Event"]["EncodingServices"]>
|
|
43
|
+
disconnect: Effect.Effect<T, S.SchemaError, (typeof Disconnect)["EncodingServices"]>
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const make = <
|
|
47
|
+
Raw,
|
|
48
|
+
ActorSelf,
|
|
49
|
+
ActorId extends string,
|
|
50
|
+
Name extends TopFromString,
|
|
51
|
+
AttachmentFields extends S.Struct.Fields,
|
|
52
|
+
ClientSelf,
|
|
53
|
+
ClientId extends string,
|
|
54
|
+
D extends ProtocolDefinition,
|
|
55
|
+
>(
|
|
56
|
+
_actor: Actor<ActorSelf, ActorId, Name, AttachmentFields, ClientSelf, ClientId, D>,
|
|
57
|
+
{ send, close, snapshot }: ActorTransport<Raw, AttachmentFields, D>,
|
|
58
|
+
): ClientDirectory<Raw, ActorSelf, AttachmentFields, D> => {
|
|
59
|
+
type Handle = ClientHandle.ClientHandle<ActorSelf, AttachmentFields, D>
|
|
60
|
+
|
|
61
|
+
const raws = new Map<Raw, Handle>()
|
|
62
|
+
const handles = new Set<Handle>()
|
|
63
|
+
|
|
64
|
+
const get = (raw: Raw) => Effect.fromNullishOr(raws.get(raw))
|
|
65
|
+
|
|
66
|
+
const register = Effect.fnUntraced(function* (raw: Raw, attachments: S.Struct<AttachmentFields>["Type"]) {
|
|
67
|
+
yield* snapshot(raw, attachments)
|
|
68
|
+
const attachmentsRef = yield* Ref.make(attachments)
|
|
69
|
+
const handle: Handle = ClientHandle.make({
|
|
70
|
+
attachments: Ref.get(attachmentsRef),
|
|
71
|
+
save: Effect.fnUntraced(function* (attachments) {
|
|
72
|
+
yield* Ref.set(attachmentsRef, attachments)
|
|
73
|
+
yield* snapshot(raw, attachments)
|
|
74
|
+
}),
|
|
75
|
+
send: (_tag, payload) =>
|
|
76
|
+
send(raw, {
|
|
77
|
+
_tag: "Event",
|
|
78
|
+
event: { _tag, ...payload } as never,
|
|
79
|
+
}),
|
|
80
|
+
disconnect: close(raw).pipe(
|
|
81
|
+
Effect.andThen(() =>
|
|
82
|
+
Effect.sync(() => {
|
|
83
|
+
raws.delete(raw)
|
|
84
|
+
handles.delete(handle)
|
|
85
|
+
}),
|
|
86
|
+
),
|
|
87
|
+
),
|
|
88
|
+
})
|
|
89
|
+
raws.set(raw, handle)
|
|
90
|
+
handles.add(handle)
|
|
91
|
+
return handle
|
|
92
|
+
}, span("register"))
|
|
93
|
+
|
|
94
|
+
const unregister = (raw: Raw) =>
|
|
95
|
+
Effect.sync(() => {
|
|
96
|
+
const handle = raws.get(raw)
|
|
97
|
+
if (handle) {
|
|
98
|
+
raws.delete(raw)
|
|
99
|
+
handles.delete(handle)
|
|
100
|
+
}
|
|
101
|
+
}).pipe(span("unregister"))
|
|
102
|
+
|
|
103
|
+
return { ...phantom, handles, register, get, unregister }
|
|
104
|
+
}
|
package/ClientHandle.ts
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import { Schema as S, Effect } from "effect"
|
|
2
2
|
|
|
3
|
+
import type { ProtocolDefinition } from "./Protocol.ts"
|
|
3
4
|
import type { Send } from "./Send.ts"
|
|
4
5
|
|
|
5
6
|
const TypeId = "~liminal/ClientHandle" as const
|
|
6
7
|
|
|
7
|
-
export interface ClientHandle<
|
|
8
|
-
ActorSelf,
|
|
9
|
-
AttachmentFields extends S.Struct.Fields,
|
|
10
|
-
EventDefinitions extends Record<string, S.Struct.Fields>,
|
|
11
|
-
> {
|
|
8
|
+
export interface ClientHandle<ActorSelf, AttachmentFields extends S.Struct.Fields, D extends ProtocolDefinition> {
|
|
12
9
|
readonly [TypeId]: typeof TypeId
|
|
13
10
|
|
|
14
|
-
readonly send: Send<ActorSelf,
|
|
11
|
+
readonly send: Send<ActorSelf, D>
|
|
15
12
|
|
|
16
13
|
readonly attachments: Effect.Effect<S.Struct<AttachmentFields>["Type"]>
|
|
17
14
|
|
|
@@ -22,17 +19,13 @@ export interface ClientHandle<
|
|
|
22
19
|
readonly disconnect: Effect.Effect<void, never, ActorSelf>
|
|
23
20
|
}
|
|
24
21
|
|
|
25
|
-
export const make = <
|
|
26
|
-
ActorSelf,
|
|
27
|
-
AttachmentFields extends S.Struct.Fields,
|
|
28
|
-
EventDefinitions extends Record<string, S.Struct.Fields>,
|
|
29
|
-
>({
|
|
22
|
+
export const make = <ActorSelf, AttachmentFields extends S.Struct.Fields, D extends ProtocolDefinition>({
|
|
30
23
|
send,
|
|
31
24
|
attachments,
|
|
32
25
|
save,
|
|
33
26
|
disconnect,
|
|
34
27
|
}: {
|
|
35
|
-
readonly send: Send<ActorSelf,
|
|
28
|
+
readonly send: Send<ActorSelf, D>
|
|
36
29
|
|
|
37
30
|
readonly attachments: Effect.Effect<S.Struct<AttachmentFields>["Type"]>
|
|
38
31
|
|
|
@@ -41,7 +34,7 @@ export const make = <
|
|
|
41
34
|
) => Effect.Effect<void, S.SchemaError, S.Struct<AttachmentFields>["EncodingServices"]>
|
|
42
35
|
|
|
43
36
|
readonly disconnect: Effect.Effect<void, never, ActorSelf>
|
|
44
|
-
}): ClientHandle<ActorSelf, AttachmentFields,
|
|
37
|
+
}): ClientHandle<ActorSelf, AttachmentFields, D> => ({
|
|
45
38
|
[TypeId]: TypeId,
|
|
46
39
|
send,
|
|
47
40
|
attachments,
|
package/F.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Effect } from "effect"
|
|
2
2
|
|
|
3
3
|
import type { FError } from "./errors.ts"
|
|
4
|
-
import type {
|
|
4
|
+
import type { ProtocolDefinition } from "./Protocol.ts"
|
|
5
5
|
|
|
6
|
-
export type F<
|
|
7
|
-
Method extends keyof MethodDefinitions,
|
|
8
|
-
>(
|
|
6
|
+
export type F<Self, D extends ProtocolDefinition> = <Method extends keyof D["methods"]>(
|
|
9
7
|
method: Method,
|
|
10
8
|
) => (
|
|
11
|
-
payload:
|
|
12
|
-
) => Effect.Effect<
|
|
9
|
+
payload: D["methods"][Method]["payload"]["Type"],
|
|
10
|
+
) => Effect.Effect<D["methods"][Method]["success"]["Type"], FError<D>, Self>
|