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
package/Client.ts
CHANGED
|
@@ -15,23 +15,27 @@ import {
|
|
|
15
15
|
Take,
|
|
16
16
|
Schema as S,
|
|
17
17
|
Array,
|
|
18
|
-
Struct,
|
|
19
18
|
Fiber,
|
|
20
19
|
Exit,
|
|
21
20
|
Cause,
|
|
22
21
|
Result,
|
|
23
22
|
flow,
|
|
23
|
+
Tracer,
|
|
24
|
+
identity,
|
|
25
|
+
Semaphore,
|
|
24
26
|
} from "effect"
|
|
25
27
|
import { Socket } from "effect/unstable/socket"
|
|
26
28
|
import { Worker } from "effect/unstable/workers"
|
|
29
|
+
import * as Spanner from "liminal-util/Spanner"
|
|
27
30
|
|
|
28
|
-
import { diagnostic } from "./_diagnostic.ts"
|
|
29
31
|
import { decodeJsonString, encodeJsonString } from "./_util/schema.ts"
|
|
30
|
-
import { type ClientError, AuditionError, ConnectionError,
|
|
31
|
-
import {
|
|
32
|
+
import { type ClientError, AuditionError, ConnectionError, UnresolvedError } from "./errors.ts"
|
|
33
|
+
import type { Fn, FnError } from "./Fn.ts"
|
|
32
34
|
import { Protocol, type ProtocolDefinition } from "./Protocol.ts"
|
|
35
|
+
import * as Reducer from "./Reducer.ts"
|
|
36
|
+
import * as Tracing from "./Tracing.ts"
|
|
33
37
|
|
|
34
|
-
const
|
|
38
|
+
const span = Spanner.make(import.meta.url)
|
|
35
39
|
|
|
36
40
|
export const TypeId = "~liminal/Client" as const
|
|
37
41
|
|
|
@@ -47,15 +51,21 @@ interface EventTake<A, E> {
|
|
|
47
51
|
readonly take: Take.Take<A, E>
|
|
48
52
|
}
|
|
49
53
|
|
|
50
|
-
export
|
|
51
|
-
|
|
54
|
+
export type Service<ClientSelf, D extends ProtocolDefinition> = RcRef.RcRef<
|
|
55
|
+
{
|
|
56
|
+
readonly state: Stream.Stream<S.Struct<D["state"]>["Type"], ClientError | S.SchemaError>
|
|
52
57
|
|
|
53
|
-
|
|
58
|
+
readonly events: Stream.Stream<ReturnType<typeof S.TaggedUnion<D["events"]>>["Type"], ClientError | S.SchemaError>
|
|
54
59
|
|
|
55
|
-
|
|
56
|
-
|
|
60
|
+
readonly fnRaw: <K extends keyof D["external"], M extends D["external"][K]>(
|
|
61
|
+
tag: K,
|
|
62
|
+
payload: M["payload"]["Type"],
|
|
63
|
+
) => Effect.Effect<M["success"]["Type"], M["failure"]["Type"], ClientSelf>
|
|
57
64
|
|
|
58
|
-
|
|
65
|
+
readonly end: Effect.Effect<void>
|
|
66
|
+
},
|
|
67
|
+
ClientError
|
|
68
|
+
>
|
|
59
69
|
|
|
60
70
|
export interface Client<Self, ClientId extends string, D extends ProtocolDefinition> extends Context.Service<
|
|
61
71
|
Self,
|
|
@@ -69,15 +79,23 @@ export interface Client<Self, ClientId extends string, D extends ProtocolDefinit
|
|
|
69
79
|
|
|
70
80
|
readonly protocol: Protocol<D>
|
|
71
81
|
|
|
82
|
+
readonly state: Stream.Stream<
|
|
83
|
+
S.Struct<D["state"]>["Type"],
|
|
84
|
+
ClientError | S.SchemaError,
|
|
85
|
+
Self | S.Struct<D["state"]>["DecodingServices"]
|
|
86
|
+
>
|
|
87
|
+
|
|
72
88
|
readonly events: Stream.Stream<
|
|
73
89
|
ReturnType<typeof S.TaggedUnion<D["events"]>>["Type"],
|
|
74
90
|
ClientError | S.SchemaError,
|
|
75
91
|
Self
|
|
76
92
|
>
|
|
77
93
|
|
|
78
|
-
readonly
|
|
94
|
+
readonly fn: Fn<Self, D["external"]>
|
|
79
95
|
|
|
80
96
|
readonly invalidate: Effect.Effect<void, never, Self>
|
|
97
|
+
|
|
98
|
+
readonly reducer: <K extends keyof D["events"], R extends Reducer.Reducer<D, K>>(_tag: K, f: R) => R
|
|
81
99
|
}
|
|
82
100
|
|
|
83
101
|
export const Service =
|
|
@@ -87,13 +105,27 @@ export const Service =
|
|
|
87
105
|
|
|
88
106
|
const protocol = Protocol(definition)
|
|
89
107
|
|
|
90
|
-
const
|
|
108
|
+
const state = tag.asEffect().pipe(
|
|
109
|
+
Effect.flatMap(RcRef.get),
|
|
110
|
+
Effect.map(({ state }) => state),
|
|
111
|
+
Stream.unwrap,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
const events = tag.asEffect().pipe(
|
|
115
|
+
Effect.flatMap(RcRef.get),
|
|
116
|
+
Effect.map(({ events }) => events),
|
|
117
|
+
Stream.unwrap,
|
|
118
|
+
)
|
|
91
119
|
|
|
92
|
-
const
|
|
93
|
-
Effect.fnUntraced(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
120
|
+
const fn = ((_tag: keyof D["external"], ...f: Array<any>) =>
|
|
121
|
+
Effect.fnUntraced(
|
|
122
|
+
function* (payload: any) {
|
|
123
|
+
const { fnRaw: fn } = yield* tag.asEffect().pipe(Effect.flatMap(RcRef.get))
|
|
124
|
+
return yield* fn(_tag, payload)
|
|
125
|
+
},
|
|
126
|
+
Effect.scoped,
|
|
127
|
+
...(f as [any]),
|
|
128
|
+
)) as Fn<Self, D["external"]>
|
|
97
129
|
|
|
98
130
|
const invalidate = tag.asEffect().pipe(
|
|
99
131
|
Effect.flatMap((rc) =>
|
|
@@ -106,47 +138,69 @@ export const Service =
|
|
|
106
138
|
Effect.ignore,
|
|
107
139
|
)
|
|
108
140
|
|
|
141
|
+
const reducer = <K extends keyof D["events"], R extends Reducer.Reducer<D, K>>(_event: K, f: R) => f
|
|
142
|
+
|
|
109
143
|
return Object.assign(tag, {
|
|
110
144
|
[TypeId]: TypeId,
|
|
111
145
|
definition,
|
|
112
146
|
protocol,
|
|
147
|
+
state,
|
|
113
148
|
events,
|
|
114
|
-
|
|
149
|
+
fn,
|
|
115
150
|
invalidate,
|
|
151
|
+
reducer,
|
|
116
152
|
})
|
|
117
153
|
}
|
|
118
154
|
|
|
119
|
-
export interface ClientTransport<D extends ProtocolDefinition> {
|
|
155
|
+
export interface ClientTransport<D extends ProtocolDefinition, R> {
|
|
120
156
|
readonly listen: (
|
|
121
|
-
publish: (message: Protocol<D>["Actor"]["Type"]) => Effect.Effect<void, ClientError>,
|
|
122
|
-
) => Effect.Effect<void, ClientError | S.SchemaError, Scope.Scope | Protocol<D>["Actor"]["DecodingServices"]>
|
|
157
|
+
publish: (message: Protocol<D>["Actor"]["Type"]) => Effect.Effect<void, ClientError, R>,
|
|
158
|
+
) => Effect.Effect<void, ClientError | S.SchemaError, Scope.Scope | Protocol<D>["Actor"]["DecodingServices"] | R>
|
|
123
159
|
|
|
124
160
|
readonly send: (
|
|
125
161
|
message: Protocol<D>["F"]["Payload"]["Type"],
|
|
126
162
|
) => Effect.Effect<void, ClientError | S.SchemaError, Protocol<D>["F"]["Payload"]["EncodingServices"]>
|
|
127
163
|
}
|
|
128
164
|
|
|
129
|
-
const make = <Self, Id extends string, D extends ProtocolDefinition, R>(
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
165
|
+
const make = <Self, Id extends string, D extends ProtocolDefinition, Reducers extends Reducer.Reducers<D>, R, CR>({
|
|
166
|
+
build,
|
|
167
|
+
client,
|
|
168
|
+
reducers,
|
|
169
|
+
onConnect,
|
|
170
|
+
replay,
|
|
171
|
+
}: {
|
|
172
|
+
readonly client: Client<Self, Id, D>
|
|
173
|
+
readonly onConnect?: undefined | ((state: S.Struct<D["state"]>["Type"]) => Effect.Effect<void, never, CR>)
|
|
174
|
+
readonly reducers: Reducers
|
|
175
|
+
readonly build: Effect.Effect<
|
|
176
|
+
ClientTransport<D, Reducer.Reducers.Services<Self, Reducers> | CR>,
|
|
177
|
+
ClientError,
|
|
178
|
+
R | Scope.Scope
|
|
179
|
+
>
|
|
180
|
+
readonly replay?: ReplayConfig | undefined
|
|
181
|
+
}) =>
|
|
134
182
|
Effect.gen(function* () {
|
|
135
|
-
const rcr:
|
|
183
|
+
const rcr: Service<Self, D> = yield* RcRef.make({
|
|
136
184
|
acquire: Effect.gen(function* () {
|
|
137
185
|
type _ = typeof client.protocol
|
|
138
186
|
type Event = ReturnType<typeof S.TaggedUnion<D["events"]>>["Type"]
|
|
139
187
|
|
|
140
|
-
yield* debug("AcquisitionStarted")
|
|
141
|
-
|
|
142
188
|
const { listen, send } = yield* build
|
|
143
189
|
|
|
144
190
|
const audition = yield* Deferred.make<void>()
|
|
191
|
+
const stateDeferred = yield* Deferred.make<Ref.Ref<S.Struct<D["state"]>["Type"]>>()
|
|
145
192
|
|
|
146
|
-
const inflights: Record<
|
|
193
|
+
const inflights: Record<
|
|
194
|
+
string,
|
|
195
|
+
{
|
|
196
|
+
readonly deferred: Deferred.Deferred<_["F"]["Success"]["Type"], FnError<D["external"], keyof D["external"]>>
|
|
197
|
+
readonly span?: Tracer.AnySpan | undefined
|
|
198
|
+
}
|
|
199
|
+
> = {}
|
|
147
200
|
let callId = 0
|
|
148
201
|
let takeCount = 0
|
|
149
|
-
const
|
|
202
|
+
const eventsPubsub = yield* PubSub.unbounded<EventTake<Event, ClientError>>()
|
|
203
|
+
const statePubsub = yield* PubSub.unbounded<S.Struct<D["state"]>["Type"]>({ replay: 1 })
|
|
150
204
|
|
|
151
205
|
const replayState = yield* Ref.make<{
|
|
152
206
|
readonly startupOpen: boolean
|
|
@@ -175,57 +229,85 @@ const make = <Self, Id extends string, D extends ProtocolDefinition, R>(
|
|
|
175
229
|
return { startupOpen, buffer }
|
|
176
230
|
})
|
|
177
231
|
}
|
|
178
|
-
yield* PubSub.publish(
|
|
232
|
+
yield* PubSub.publish(eventsPubsub, eventTake)
|
|
179
233
|
})
|
|
180
234
|
|
|
181
235
|
const outer = yield* Scope.Scope
|
|
182
236
|
const scope = yield* Scope.fork(outer, "sequential")
|
|
183
237
|
const end = Scope.close(scope, Exit.void)
|
|
238
|
+
const reduceMutex = yield* Semaphore.make(1)
|
|
239
|
+
const reduceTask = Semaphore.withPermits(reduceMutex, 1)
|
|
184
240
|
|
|
185
241
|
const fiber = yield* listen(
|
|
186
242
|
Effect.fnUntraced(function* (message) {
|
|
187
243
|
switch (message._tag) {
|
|
188
244
|
case "Audition.Success": {
|
|
189
|
-
|
|
245
|
+
const { initial } = message
|
|
246
|
+
yield* PubSub.publish(statePubsub, initial)
|
|
247
|
+
const state = yield* Ref.make(initial)
|
|
248
|
+
yield* Deferred.succeed(stateDeferred, state)
|
|
190
249
|
yield* Deferred.succeed(audition, void 0)
|
|
250
|
+
yield* onConnect?.(initial) ?? Effect.void
|
|
191
251
|
return
|
|
192
252
|
}
|
|
193
253
|
case "Audition.Failure": {
|
|
194
254
|
const { expected, actual } = message
|
|
195
|
-
yield* debug("Audition.Failed", { expected, actual })
|
|
196
255
|
return yield* new AuditionError({ value: { expected, actual } })
|
|
197
256
|
}
|
|
198
257
|
case "Event": {
|
|
199
258
|
const { event } = message
|
|
200
|
-
|
|
201
|
-
|
|
259
|
+
const { _tag } = event as never
|
|
260
|
+
const reducer = reducers[_tag]!
|
|
261
|
+
const state = yield* Deferred.await(stateDeferred)
|
|
262
|
+
yield* Effect.gen(function* () {
|
|
263
|
+
const current = yield* Ref.get(state)
|
|
264
|
+
const reduced = yield* reducer(event as never)(current).pipe(
|
|
265
|
+
Effect.provideService(client, rcr),
|
|
266
|
+
) as Effect.Effect<
|
|
267
|
+
S.Struct<D["state"]>["Type"] | undefined,
|
|
268
|
+
never,
|
|
269
|
+
Reducer.Reducers.Services<Self, Reducers>
|
|
270
|
+
>
|
|
271
|
+
if (reduced) {
|
|
272
|
+
yield* PubSub.publish(statePubsub, reduced)
|
|
273
|
+
yield* Ref.set(state, reduced)
|
|
274
|
+
}
|
|
275
|
+
}).pipe(reduceTask)
|
|
276
|
+
const parent = message.trace ? Tracer.externalSpan(message.trace) : undefined
|
|
277
|
+
yield* publishTake([event], true).pipe(
|
|
278
|
+
span("enqueue-event", {
|
|
279
|
+
attributes: { _tag },
|
|
280
|
+
kind: "consumer",
|
|
281
|
+
parent,
|
|
282
|
+
}),
|
|
283
|
+
)
|
|
202
284
|
return
|
|
203
285
|
}
|
|
204
286
|
case "F.Success":
|
|
205
287
|
case "F.Failure": {
|
|
206
288
|
const { id } = message
|
|
207
|
-
const
|
|
208
|
-
if (
|
|
289
|
+
const inflight = inflights[id]
|
|
290
|
+
if (inflight) {
|
|
209
291
|
delete inflights[id]
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
292
|
+
return yield* Effect.gen(function* () {
|
|
293
|
+
switch (message._tag) {
|
|
294
|
+
case "F.Success": {
|
|
295
|
+
const { value } = message.success as never
|
|
296
|
+
yield* Deferred.succeed(inflight.deferred, value)
|
|
297
|
+
return
|
|
298
|
+
}
|
|
299
|
+
case "F.Failure": {
|
|
300
|
+
const { _tag, value } = message.failure as never
|
|
301
|
+
yield* Effect.annotateLogs(Effect.logDebug("Call.Failed"), { id, _tag })
|
|
302
|
+
yield* Deferred.fail(inflight.deferred, value)
|
|
303
|
+
return
|
|
304
|
+
}
|
|
216
305
|
}
|
|
217
|
-
|
|
218
|
-
const { _tag, value } = message.failure as never
|
|
219
|
-
yield* debug("Call.Failed", { id, _tag, value })
|
|
220
|
-
yield* Deferred.fail(deferred, value)
|
|
221
|
-
return
|
|
222
|
-
}
|
|
223
|
-
}
|
|
306
|
+
}).pipe(inflight.span ? Effect.withParentSpan(inflight.span, { captureStackTrace: false }) : identity)
|
|
224
307
|
}
|
|
225
308
|
return
|
|
226
309
|
}
|
|
227
310
|
case "Disconnect": {
|
|
228
|
-
yield* debug("Disconnected")
|
|
229
311
|
return
|
|
230
312
|
}
|
|
231
313
|
}
|
|
@@ -234,7 +316,13 @@ const make = <Self, Id extends string, D extends ProtocolDefinition, R>(
|
|
|
234
316
|
Effect.ensuring(
|
|
235
317
|
Effect.all(
|
|
236
318
|
[
|
|
237
|
-
|
|
319
|
+
Effect.sync(() => Record.keys(inflights).length).pipe(
|
|
320
|
+
Effect.flatMap((unresolved) =>
|
|
321
|
+
unresolved === 0
|
|
322
|
+
? Effect.void
|
|
323
|
+
: Effect.annotateLogs(Effect.logDebug("Client.Closed"), { unresolved }),
|
|
324
|
+
),
|
|
325
|
+
),
|
|
238
326
|
Deferred.succeed(audition, void 0),
|
|
239
327
|
RcRef.invalidate(rcr),
|
|
240
328
|
],
|
|
@@ -245,8 +333,25 @@ const make = <Self, Id extends string, D extends ProtocolDefinition, R>(
|
|
|
245
333
|
Effect.provideService(Scope.Scope, scope),
|
|
246
334
|
)
|
|
247
335
|
|
|
336
|
+
const interrupt = Stream.interruptWhen(
|
|
337
|
+
Fiber.await(fiber).pipe(
|
|
338
|
+
Effect.flatMap(
|
|
339
|
+
Exit.match({
|
|
340
|
+
onSuccess: () => Effect.void,
|
|
341
|
+
onFailure: flow(
|
|
342
|
+
Cause.findError,
|
|
343
|
+
Result.match({
|
|
344
|
+
onSuccess: Effect.fail,
|
|
345
|
+
onFailure: () => Effect.void,
|
|
346
|
+
}),
|
|
347
|
+
),
|
|
348
|
+
}),
|
|
349
|
+
),
|
|
350
|
+
),
|
|
351
|
+
)
|
|
352
|
+
|
|
248
353
|
const events = Effect.gen(function* () {
|
|
249
|
-
const queue = yield* PubSub.subscribe(
|
|
354
|
+
const queue = yield* PubSub.subscribe(eventsPubsub)
|
|
250
355
|
const live = (replayCount: number) =>
|
|
251
356
|
Stream.fromSubscription(queue).pipe(
|
|
252
357
|
Stream.filter((entry) => entry.seq > replayCount),
|
|
@@ -283,109 +388,115 @@ const make = <Self, Id extends string, D extends ProtocolDefinition, R>(
|
|
|
283
388
|
),
|
|
284
389
|
live(replayCount),
|
|
285
390
|
)
|
|
286
|
-
}).pipe(
|
|
287
|
-
Stream.unwrap,
|
|
288
|
-
Stream.interruptWhen(
|
|
289
|
-
Fiber.await(fiber).pipe(
|
|
290
|
-
Effect.flatMap(
|
|
291
|
-
Exit.match({
|
|
292
|
-
onSuccess: () => Effect.void,
|
|
293
|
-
onFailure: flow(
|
|
294
|
-
Cause.findError,
|
|
295
|
-
Result.match({
|
|
296
|
-
onSuccess: Effect.fail,
|
|
297
|
-
onFailure: () => Effect.void,
|
|
298
|
-
}),
|
|
299
|
-
),
|
|
300
|
-
}),
|
|
301
|
-
),
|
|
302
|
-
),
|
|
303
|
-
),
|
|
304
|
-
)
|
|
391
|
+
}).pipe(Stream.unwrap, interrupt)
|
|
305
392
|
|
|
306
|
-
|
|
393
|
+
const state = Stream.fromPubSub(statePubsub).pipe(interrupt)
|
|
307
394
|
|
|
308
395
|
const encodingServices = yield* Effect.context<_["F"]["Payload"]["EncodingServices"]>()
|
|
309
396
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
),
|
|
324
|
-
|
|
325
|
-
}
|
|
326
|
-
const id = callId++
|
|
327
|
-
const inflight = yield* Deferred.make<_["F"]["Success"]["Type"], FError<D>>()
|
|
328
|
-
inflights[id] = inflight
|
|
329
|
-
yield* send({
|
|
330
|
-
_tag: "F.Payload",
|
|
331
|
-
id,
|
|
332
|
-
payload: { _tag, value } as never,
|
|
397
|
+
yield* Deferred.await(audition)
|
|
398
|
+
|
|
399
|
+
const fnRaw = <K extends keyof D["external"]>(_tag: K, value: D["external"][K]["payload"]["Type"]) =>
|
|
400
|
+
Effect.gen(function* () {
|
|
401
|
+
const exit = fiber.pollUnsafe()
|
|
402
|
+
if (exit) {
|
|
403
|
+
return yield* Exit.match(exit, {
|
|
404
|
+
onSuccess: () => new UnresolvedError(),
|
|
405
|
+
onFailure: flow(
|
|
406
|
+
Cause.findError,
|
|
407
|
+
Result.match({
|
|
408
|
+
onSuccess: Effect.fail,
|
|
409
|
+
onFailure: () => new UnresolvedError(),
|
|
410
|
+
}),
|
|
411
|
+
),
|
|
333
412
|
})
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
413
|
+
}
|
|
414
|
+
const id = callId++
|
|
415
|
+
const deferred = yield* Deferred.make<
|
|
416
|
+
_["F"]["Success"]["Type"],
|
|
417
|
+
FnError<D["external"], keyof D["external"]>
|
|
418
|
+
>()
|
|
419
|
+
const span = yield* Tracing.current
|
|
420
|
+
const trace = span ? Tracing.toTraceEnvelope(span) : undefined
|
|
421
|
+
inflights[id] = { deferred, span }
|
|
422
|
+
yield* send({
|
|
423
|
+
_tag: "F.Payload",
|
|
424
|
+
id,
|
|
425
|
+
payload: { _tag, value } as never,
|
|
426
|
+
...(trace && { trace }),
|
|
427
|
+
})
|
|
428
|
+
return yield* Effect.raceFirst(
|
|
429
|
+
Deferred.await(deferred),
|
|
430
|
+
Fiber.await(fiber).pipe(
|
|
431
|
+
Effect.flatMap(
|
|
432
|
+
(exit): Effect.Effect<never, ClientError | UnresolvedError | S.SchemaError> =>
|
|
433
|
+
Exit.match(exit, {
|
|
434
|
+
onSuccess: () => new UnresolvedError().asEffect(),
|
|
435
|
+
onFailure: flow(
|
|
436
|
+
Cause.findError,
|
|
437
|
+
Result.match({
|
|
438
|
+
onSuccess: Effect.fail,
|
|
439
|
+
onFailure: () => new UnresolvedError().asEffect(),
|
|
440
|
+
}),
|
|
441
|
+
),
|
|
442
|
+
}),
|
|
350
443
|
),
|
|
351
|
-
)
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
444
|
+
),
|
|
445
|
+
)
|
|
446
|
+
}).pipe(
|
|
447
|
+
span("fn", {
|
|
448
|
+
kind: "client",
|
|
449
|
+
attributes: { _tag },
|
|
450
|
+
}),
|
|
355
451
|
Effect.provide(encodingServices),
|
|
356
452
|
)
|
|
357
453
|
|
|
358
|
-
return { events,
|
|
454
|
+
return { state, events, fnRaw, end }
|
|
359
455
|
}).pipe(span("acquire", { attributes: { client: client.key } }), Effect.annotateLogs("client", client.key)),
|
|
360
456
|
})
|
|
361
457
|
|
|
362
458
|
return rcr
|
|
363
459
|
}).pipe(Layer.effect(client))
|
|
364
460
|
|
|
365
|
-
export const layerSocket = <
|
|
461
|
+
export const layerSocket = <
|
|
462
|
+
Self,
|
|
463
|
+
Id extends string,
|
|
464
|
+
D extends ProtocolDefinition,
|
|
465
|
+
Reducers extends Reducer.Reducers<D>,
|
|
466
|
+
CR = never,
|
|
467
|
+
>({
|
|
366
468
|
client,
|
|
469
|
+
reducers,
|
|
367
470
|
url,
|
|
368
471
|
protocols,
|
|
369
472
|
replay,
|
|
473
|
+
onConnect,
|
|
370
474
|
}: {
|
|
371
475
|
readonly client: Client<Self, Id, D>
|
|
372
|
-
readonly
|
|
373
|
-
readonly protocols?: string | Array<string> | undefined
|
|
476
|
+
readonly reducers: Reducers
|
|
374
477
|
readonly replay?: ReplayConfig | undefined
|
|
478
|
+
readonly onConnect?: undefined | ((state: S.Struct<D["state"]>["Type"]) => Effect.Effect<void, never, CR>)
|
|
479
|
+
readonly protocols?: string | Array<string> | undefined
|
|
480
|
+
readonly url?: string | undefined
|
|
375
481
|
}): Layer.Layer<
|
|
376
482
|
Self,
|
|
377
483
|
never,
|
|
378
484
|
| Socket.WebSocketConstructor
|
|
379
485
|
| Protocol<D>["Actor"]["DecodingServices"]
|
|
380
486
|
| Protocol<D>["F"]["Payload"]["EncodingServices"]
|
|
487
|
+
| Reducer.Reducers.Services<Self, Reducers>
|
|
488
|
+
| CR
|
|
381
489
|
> => {
|
|
382
490
|
const { F, Actor } = client.protocol
|
|
383
491
|
const encodeFPayload = encodeJsonString(F.Payload)
|
|
384
492
|
const decodeActor = decodeJsonString(Actor)
|
|
385
493
|
|
|
386
|
-
return make<Self, Id, D, Socket.WebSocketConstructor>(
|
|
494
|
+
return make<Self, Id, D, Reducers, Socket.WebSocketConstructor, CR>({
|
|
387
495
|
client,
|
|
388
|
-
|
|
496
|
+
reducers,
|
|
497
|
+
onConnect,
|
|
498
|
+
replay,
|
|
499
|
+
build: Effect.gen(function* () {
|
|
389
500
|
const socket = yield* Socket.makeWebSocket(url ?? "/", {
|
|
390
501
|
protocols: ["liminal", Encoding.encodeBase64Url(client.key), ...(protocols ? Array.ensure(protocols) : [])],
|
|
391
502
|
})
|
|
@@ -405,10 +516,9 @@ export const layerSocket = <Self, Id extends string, D extends ProtocolDefinitio
|
|
|
405
516
|
Effect.fnUntraced(function* (cause) {
|
|
406
517
|
const { reason } = cause
|
|
407
518
|
if (reason._tag === "SocketCloseError" && reason.code === 1000) {
|
|
408
|
-
yield* debug("Socket.Disconnected")
|
|
409
519
|
return yield* publish({ _tag: "Disconnect" })
|
|
410
520
|
}
|
|
411
|
-
yield*
|
|
521
|
+
yield* Effect.annotateLogs(Effect.logDebug(`SocketErrored.${reason._tag}`), { cause })
|
|
412
522
|
return yield* new ConnectionError({ cause })
|
|
413
523
|
}),
|
|
414
524
|
),
|
|
@@ -427,24 +537,41 @@ export const layerSocket = <Self, Id extends string, D extends ProtocolDefinitio
|
|
|
427
537
|
),
|
|
428
538
|
}
|
|
429
539
|
}),
|
|
430
|
-
|
|
431
|
-
)
|
|
540
|
+
})
|
|
432
541
|
}
|
|
433
542
|
|
|
434
|
-
export const layerWorker = <
|
|
543
|
+
export const layerWorker = <
|
|
544
|
+
Self,
|
|
545
|
+
Id extends string,
|
|
546
|
+
D extends ProtocolDefinition,
|
|
547
|
+
Reducers extends Reducer.Reducers<D>,
|
|
548
|
+
T extends Protocol<D>,
|
|
549
|
+
CR = never,
|
|
550
|
+
>({
|
|
435
551
|
client,
|
|
552
|
+
reducers,
|
|
436
553
|
replay,
|
|
554
|
+
onConnect,
|
|
437
555
|
}: {
|
|
438
556
|
readonly client: Client<Self, Id, D>
|
|
557
|
+
readonly reducers: Reducers
|
|
439
558
|
readonly replay?: ReplayConfig | undefined
|
|
559
|
+
readonly onConnect?: undefined | ((state: S.Struct<D["state"]>["Type"]) => Effect.Effect<void, never, CR>)
|
|
440
560
|
}): Layer.Layer<
|
|
441
561
|
Self,
|
|
442
562
|
never,
|
|
443
|
-
|
|
563
|
+
| Worker.WorkerPlatform
|
|
564
|
+
| Worker.Spawner
|
|
565
|
+
| T["Actor"]["DecodingServices"]
|
|
566
|
+
| T["F"]["Payload"]["EncodingServices"]
|
|
567
|
+
| Reducer.Reducers.Services<Self, Reducers>
|
|
444
568
|
> =>
|
|
445
|
-
make<Self, Id, D, Worker.WorkerPlatform | Worker.Spawner>(
|
|
569
|
+
make<Self, Id, D, Reducers, Worker.WorkerPlatform | Worker.Spawner, CR>({
|
|
446
570
|
client,
|
|
447
|
-
|
|
571
|
+
reducers,
|
|
572
|
+
onConnect,
|
|
573
|
+
replay,
|
|
574
|
+
build: Effect.gen(function* () {
|
|
448
575
|
const platform = yield* Worker.WorkerPlatform
|
|
449
576
|
const backing = yield* platform
|
|
450
577
|
.spawn<T["Actor"]["Type"], T["Client"]["Type"]>(0)
|
|
@@ -484,5 +611,4 @@ export const layerWorker = <Self, Id extends string, D extends ProtocolDefinitio
|
|
|
484
611
|
send,
|
|
485
612
|
}
|
|
486
613
|
}),
|
|
487
|
-
|
|
488
|
-
)
|
|
614
|
+
})
|