liminal 0.17.1 → 0.17.3
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/Accumulator.ts +16 -10
- package/Actor.ts +35 -29
- package/Audition.ts +17 -12
- package/CHANGELOG.md +12 -0
- package/Client.ts +260 -295
- package/ClientHandle.ts +17 -6
- package/F.ts +3 -9
- package/LICENSE +202 -0
- package/Method.ts +11 -13
- package/Protocol.ts +104 -126
- package/Send.ts +9 -5
- package/_util/Diagnostic.ts +16 -0
- package/_util/Mutex.ts +5 -5
- package/dist/Accumulator.d.ts +2 -2
- package/dist/Accumulator.js +12 -8
- package/dist/Accumulator.js.map +1 -1
- package/dist/Actor.d.ts +9 -10
- package/dist/Actor.js +6 -15
- package/dist/Actor.js.map +1 -1
- package/dist/Audition.d.ts +3 -4
- package/dist/Audition.js +7 -5
- package/dist/Audition.js.map +1 -1
- package/dist/Client.d.ts +20 -39
- package/dist/Client.js +119 -139
- package/dist/Client.js.map +1 -1
- package/dist/ClientHandle.d.ts +5 -6
- package/dist/ClientHandle.js +1 -1
- package/dist/ClientHandle.js.map +1 -1
- package/dist/F.d.ts +3 -12
- package/dist/F.js +1 -3
- package/dist/F.js.map +1 -1
- package/dist/Method.d.ts +11 -12
- package/dist/Method.js.map +1 -1
- package/dist/Protocol.d.ts +46 -89
- package/dist/Protocol.js +35 -11
- package/dist/Protocol.js.map +1 -1
- package/dist/Send.d.ts +2 -3
- package/dist/Send.js +1 -1
- package/dist/Send.js.map +1 -1
- package/dist/_util/Diagnostic.d.ts +5 -0
- package/dist/_util/Diagnostic.js +10 -0
- package/dist/_util/Diagnostic.js.map +1 -0
- package/dist/_util/Mutex.d.ts +2 -2
- package/dist/_util/Mutex.js +4 -4
- package/dist/_util/Mutex.js.map +1 -1
- package/dist/errors.d.ts +15 -12
- package/dist/errors.js +6 -4
- package/dist/errors.js.map +1 -1
- package/dist/experimental/BranchLive.d.ts +1 -1
- package/dist/experimental/BranchLive.js +1 -1
- package/dist/experimental/BranchLive.js.map +1 -1
- package/dist/experimental/L/append.d.ts +1 -1
- package/dist/experimental/L/append.js +2 -2
- package/dist/experimental/L/append.js.map +1 -1
- package/dist/experimental/L/assistant.d.ts +2 -2
- package/dist/experimental/L/assistant.js +1 -1
- package/dist/experimental/L/assistant.js.map +1 -1
- package/dist/experimental/L/assistantSchema.d.ts +2 -2
- package/dist/experimental/L/assistantSchema.js +1 -1
- package/dist/experimental/L/assistantSchema.js.map +1 -1
- package/dist/experimental/L/assistantStream.d.ts +2 -2
- package/dist/experimental/L/assistantStream.js +1 -1
- package/dist/experimental/L/assistantStream.js.map +1 -1
- package/dist/experimental/L/branch.d.ts +1 -1
- package/dist/experimental/L/clear.d.ts +1 -1
- package/dist/experimental/L/clear.js +1 -1
- package/dist/experimental/L/clear.js.map +1 -1
- package/dist/experimental/L/history.d.ts +2 -2
- package/dist/experimental/L/history.js +2 -2
- package/dist/experimental/L/history.js.map +1 -1
- package/dist/experimental/L/init.d.ts +1 -1
- package/dist/experimental/L/init.js +1 -1
- package/dist/experimental/L/init.js.map +1 -1
- package/dist/experimental/L/matrix.d.ts +2 -2
- package/dist/experimental/L/system.d.ts +1 -1
- package/dist/experimental/L/system.js +1 -1
- package/dist/experimental/L/system.js.map +1 -1
- package/dist/experimental/L/user.d.ts +1 -1
- package/dist/experimental/L/user.js +1 -1
- package/dist/experimental/L/user.js.map +1 -1
- package/dist/experimental/Loader.d.ts +3 -3
- package/dist/experimental/Loader.js +2 -2
- package/dist/experimental/Loader.js.map +1 -1
- package/dist/experimental/TaggedTemplateFunction.d.ts +2 -2
- package/dist/experimental/Template.d.ts +8 -12
- package/dist/experimental/Template.js +53 -51
- package/dist/experimental/Template.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/errors.ts +12 -4
- package/experimental/BranchLive.ts +1 -1
- package/experimental/L/append.ts +2 -2
- package/experimental/L/assistant.ts +1 -1
- package/experimental/L/assistantSchema.ts +3 -3
- package/experimental/L/assistantStream.ts +1 -1
- package/experimental/L/clear.ts +1 -1
- package/experimental/L/history.ts +2 -2
- package/experimental/L/init.ts +1 -1
- package/experimental/L/matrix.ts +3 -3
- package/experimental/L/system.ts +1 -1
- package/experimental/L/user.ts +1 -1
- package/experimental/Loader.ts +3 -3
- package/experimental/TaggedTemplateFunction.ts +2 -2
- package/experimental/Template.ts +62 -70
- package/package.json +2 -6
- package/tsconfig.json +1 -1
- package/_types.ts +0 -27
- package/dist/_types.d.ts +0 -22
- package/dist/_types.js +0 -2
- package/dist/_types.js.map +0 -1
package/Client.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Socket, Worker } from "@effect/platform"
|
|
2
1
|
import {
|
|
3
2
|
Context,
|
|
4
3
|
Encoding,
|
|
@@ -9,6 +8,7 @@ import {
|
|
|
9
8
|
PubSub,
|
|
10
9
|
RcRef,
|
|
11
10
|
Record,
|
|
11
|
+
pipe,
|
|
12
12
|
Ref,
|
|
13
13
|
Scope,
|
|
14
14
|
Stream,
|
|
@@ -16,19 +16,30 @@ import {
|
|
|
16
16
|
Schema as S,
|
|
17
17
|
Array,
|
|
18
18
|
Struct,
|
|
19
|
+
Fiber,
|
|
20
|
+
Exit,
|
|
21
|
+
Cause,
|
|
22
|
+
Result,
|
|
23
|
+
flow,
|
|
24
|
+
identity,
|
|
19
25
|
} from "effect"
|
|
26
|
+
import { Socket } from "effect/unstable/socket"
|
|
27
|
+
import { Worker } from "effect/unstable/workers"
|
|
20
28
|
|
|
21
|
-
import type { FieldsRecord } from "./_types.ts"
|
|
22
|
-
import { type ClientError, AuditionError, ConnectionError } from "./errors.ts"
|
|
23
|
-
import { type F, type FError, UnresolvedError } from "./F.ts"
|
|
24
29
|
import type { MethodDefinition } from "./Method.ts"
|
|
30
|
+
|
|
31
|
+
import * as Diagnostic from "./_util/Diagnostic.ts"
|
|
32
|
+
import { type ClientError, AuditionError, ConnectionError, type FError, UnresolvedError } from "./errors.ts"
|
|
33
|
+
import { type F } from "./F.ts"
|
|
25
34
|
import * as Protocol from "./Protocol.ts"
|
|
26
35
|
|
|
36
|
+
const { debug, span } = Diagnostic.module("Client")
|
|
37
|
+
|
|
27
38
|
export const TypeId = "~liminal/Client" as const
|
|
28
39
|
|
|
29
40
|
export interface ClientDefinition<
|
|
30
41
|
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
31
|
-
EventDefinitions extends
|
|
42
|
+
EventDefinitions extends Record<string, S.Struct.Fields>,
|
|
32
43
|
> {
|
|
33
44
|
readonly methods: MethodDefinitions
|
|
34
45
|
|
|
@@ -47,83 +58,39 @@ interface EventTake<A, E> {
|
|
|
47
58
|
readonly take: Take.Take<A, E>
|
|
48
59
|
}
|
|
49
60
|
|
|
50
|
-
export interface
|
|
61
|
+
export interface Session<
|
|
51
62
|
ClientSelf,
|
|
52
63
|
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
53
|
-
EventDefinitions extends
|
|
64
|
+
EventDefinitions extends Record<string, S.Struct.Fields>,
|
|
54
65
|
> {
|
|
55
|
-
readonly events: Stream.Stream<
|
|
66
|
+
readonly events: Stream.Stream<ReturnType<typeof S.TaggedUnion<EventDefinitions>>["Type"], ClientError>
|
|
56
67
|
|
|
57
68
|
readonly f: F<ClientSelf, MethodDefinitions>
|
|
58
69
|
|
|
59
|
-
readonly
|
|
70
|
+
readonly end: Effect.Effect<void>
|
|
60
71
|
}
|
|
61
72
|
|
|
62
73
|
export type Service<
|
|
63
74
|
ClientSelf,
|
|
64
75
|
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
65
|
-
EventDefinitions extends
|
|
66
|
-
> = RcRef.RcRef<
|
|
67
|
-
|
|
68
|
-
export interface Spec<
|
|
69
|
-
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
70
|
-
EventDefinitions extends FieldsRecord,
|
|
71
|
-
> {
|
|
72
|
-
Call: {
|
|
73
|
-
Payload: Protocol.Call.Payload.Type<MethodDefinitions>
|
|
74
|
-
Success: Protocol.Call.Success.Type<MethodDefinitions>
|
|
75
|
-
Failure: Protocol.Call.Failure.Type<MethodDefinitions>
|
|
76
|
-
}
|
|
77
|
-
Event: FieldsRecord.TaggedMember.Type<EventDefinitions>
|
|
78
|
-
Actor: Protocol.Actor.Type<MethodDefinitions, EventDefinitions>
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export interface ClientSchema<
|
|
82
|
-
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
83
|
-
EventDefinitions extends FieldsRecord,
|
|
84
|
-
> {
|
|
85
|
-
readonly call: {
|
|
86
|
-
readonly payload: S.Schema<
|
|
87
|
-
Protocol.Call.Payload.Type<MethodDefinitions>,
|
|
88
|
-
Protocol.Call.Payload.Encoded<MethodDefinitions>
|
|
89
|
-
>
|
|
90
|
-
|
|
91
|
-
readonly success: S.Schema<
|
|
92
|
-
Protocol.Call.Success.Type<MethodDefinitions>,
|
|
93
|
-
Protocol.Call.Success.Encoded<MethodDefinitions>
|
|
94
|
-
>
|
|
95
|
-
|
|
96
|
-
readonly failure: S.Schema<
|
|
97
|
-
Protocol.Call.Failure.Type<MethodDefinitions>,
|
|
98
|
-
Protocol.Call.Failure.Encoded<MethodDefinitions>
|
|
99
|
-
>
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
readonly event: S.Schema<Protocol.Event.Type<EventDefinitions>, Protocol.Event.Encoded<EventDefinitions>>
|
|
103
|
-
|
|
104
|
-
readonly actor: S.Schema<
|
|
105
|
-
Protocol.Actor.Type<MethodDefinitions, EventDefinitions>,
|
|
106
|
-
Protocol.Actor.Encoded<MethodDefinitions, EventDefinitions>
|
|
107
|
-
>
|
|
108
|
-
}
|
|
76
|
+
EventDefinitions extends Record<string, S.Struct.Fields>,
|
|
77
|
+
> = RcRef.RcRef<Session<ClientSelf, MethodDefinitions, EventDefinitions>, ClientError>
|
|
109
78
|
|
|
110
79
|
export interface Client<
|
|
111
80
|
ClientSelf,
|
|
112
81
|
ClientId extends string,
|
|
113
82
|
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
114
|
-
EventDefinitions extends
|
|
115
|
-
> extends Context.
|
|
116
|
-
new (_: never): Context.
|
|
83
|
+
EventDefinitions extends Record<string, S.Struct.Fields>,
|
|
84
|
+
> extends Context.Service<ClientSelf, Service<ClientSelf, MethodDefinitions, EventDefinitions>> {
|
|
85
|
+
new (_: never): Context.ServiceClass.Shape<ClientId, Service<ClientSelf, MethodDefinitions, EventDefinitions>>
|
|
117
86
|
|
|
118
87
|
readonly [TypeId]: typeof TypeId
|
|
119
88
|
|
|
120
89
|
readonly definition: ClientDefinition<MethodDefinitions, EventDefinitions>
|
|
121
90
|
|
|
122
|
-
readonly schema:
|
|
123
|
-
|
|
124
|
-
readonly events: Stream.Stream<FieldsRecord.TaggedMember.Type<EventDefinitions>, ClientError, ClientSelf>
|
|
91
|
+
readonly schema: Protocol.ProtocolSchemas<MethodDefinitions, EventDefinitions>
|
|
125
92
|
|
|
126
|
-
readonly
|
|
93
|
+
readonly events: Stream.Stream<ReturnType<typeof S.TaggedUnion<EventDefinitions>>["Type"], ClientError, ClientSelf>
|
|
127
94
|
|
|
128
95
|
readonly f: F<ClientSelf, MethodDefinitions>
|
|
129
96
|
|
|
@@ -135,72 +102,28 @@ export const Service =
|
|
|
135
102
|
<
|
|
136
103
|
ClientId extends string,
|
|
137
104
|
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
138
|
-
EventDefinitions extends
|
|
105
|
+
EventDefinitions extends Record<string, S.Struct.Fields>,
|
|
139
106
|
>(
|
|
140
107
|
id: ClientId,
|
|
141
108
|
definition: ClientDefinition<MethodDefinitions, EventDefinitions>,
|
|
142
109
|
): Client<ClientSelf, ClientId, MethodDefinitions, EventDefinitions> => {
|
|
143
|
-
const tag = Context.
|
|
144
|
-
|
|
145
|
-
const call: ClientSchema<MethodDefinitions, EventDefinitions>["call"] = {
|
|
146
|
-
payload: S.TaggedStruct("Call.Payload", {
|
|
147
|
-
id: S.Int,
|
|
148
|
-
payload: S.Union(
|
|
149
|
-
...Record.toEntries(definition.methods).map(([_tag, { payload }]) =>
|
|
150
|
-
S.TaggedStruct(_tag, { value: S.Struct(payload) }),
|
|
151
|
-
),
|
|
152
|
-
),
|
|
153
|
-
}) as never,
|
|
154
|
-
success: S.TaggedStruct("Call.Success", {
|
|
155
|
-
id: S.Int,
|
|
156
|
-
value: S.Union(
|
|
157
|
-
...Record.toEntries(definition.methods).map(([_tag, { success: value }]) => S.TaggedStruct(_tag, { value })),
|
|
158
|
-
),
|
|
159
|
-
}) as never,
|
|
160
|
-
failure: S.TaggedStruct("Call.Failure", {
|
|
161
|
-
id: S.Int,
|
|
162
|
-
cause: S.Union(
|
|
163
|
-
...Record.toEntries(definition.methods).map(([_tag, { failure: value }]) => S.TaggedStruct(_tag, { value })),
|
|
164
|
-
),
|
|
165
|
-
}) as never,
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const event: S.Schema<
|
|
169
|
-
Protocol.Event.Type<EventDefinitions>,
|
|
170
|
-
Protocol.Event.Encoded<EventDefinitions>
|
|
171
|
-
> = S.TaggedStruct("Event", {
|
|
172
|
-
event: S.Union(...Object.entries(definition.events).map(([_tag, fields]) => S.TaggedStruct(_tag, fields))),
|
|
173
|
-
}) as never
|
|
174
|
-
|
|
175
|
-
const actor: S.Schema<
|
|
176
|
-
Protocol.Actor.Type<MethodDefinitions, EventDefinitions>,
|
|
177
|
-
Protocol.Actor.Encoded<MethodDefinitions, EventDefinitions>
|
|
178
|
-
> = S.Union(
|
|
179
|
-
call.success,
|
|
180
|
-
call.failure,
|
|
181
|
-
event,
|
|
182
|
-
Protocol.Audition.Success,
|
|
183
|
-
Protocol.Audition.Failure,
|
|
184
|
-
Protocol.Disconnect,
|
|
185
|
-
)
|
|
110
|
+
const tag = Context.Service<ClientSelf, Service<ClientSelf, MethodDefinitions, EventDefinitions>>()(id)
|
|
186
111
|
|
|
187
|
-
const events
|
|
188
|
-
Effect.flatMap(RcRef.get),
|
|
189
|
-
Effect.map(Struct.get("events")),
|
|
190
|
-
Stream.unwrapScoped,
|
|
191
|
-
)
|
|
112
|
+
const events = tag.asEffect().pipe(Effect.flatMap(RcRef.get), Effect.map(Struct.get("events")), Stream.unwrap)
|
|
192
113
|
|
|
193
114
|
const f: F<ClientSelf, MethodDefinitions> = (_tag) =>
|
|
194
115
|
Effect.fnUntraced(function* (value) {
|
|
195
|
-
const { f } = yield* tag.pipe(Effect.flatMap(RcRef.get))
|
|
116
|
+
const { f } = yield* tag.asEffect().pipe(Effect.flatMap(RcRef.get))
|
|
196
117
|
return yield* f(_tag)(value)
|
|
197
118
|
}, Effect.scoped)
|
|
198
119
|
|
|
199
|
-
const invalidate = tag.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
120
|
+
const invalidate = tag.asEffect().pipe(
|
|
121
|
+
Effect.flatMap((rc) =>
|
|
122
|
+
RcRef.get(rc).pipe(
|
|
123
|
+
Effect.flatMap(({ end }) => end),
|
|
124
|
+
Effect.andThen(RcRef.invalidate(rc)),
|
|
125
|
+
),
|
|
126
|
+
),
|
|
204
127
|
Effect.scoped,
|
|
205
128
|
Effect.ignore,
|
|
206
129
|
)
|
|
@@ -208,9 +131,8 @@ export const Service =
|
|
|
208
131
|
return Object.assign(tag, {
|
|
209
132
|
[TypeId]: TypeId,
|
|
210
133
|
definition,
|
|
211
|
-
schema:
|
|
134
|
+
schema: Protocol.ProtocolSchemas(definition.methods, definition.events),
|
|
212
135
|
events,
|
|
213
|
-
endEvents,
|
|
214
136
|
f,
|
|
215
137
|
invalidate,
|
|
216
138
|
})
|
|
@@ -218,22 +140,34 @@ export const Service =
|
|
|
218
140
|
|
|
219
141
|
export interface Transport<
|
|
220
142
|
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
221
|
-
EventDefinitions extends
|
|
143
|
+
EventDefinitions extends Record<string, S.Struct.Fields>,
|
|
222
144
|
> {
|
|
223
145
|
readonly listen: (
|
|
224
146
|
publish: (
|
|
225
|
-
message:
|
|
226
|
-
|
|
227
|
-
|
|
147
|
+
message:
|
|
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
|
+
>
|
|
228
156
|
|
|
229
|
-
readonly send: (
|
|
157
|
+
readonly send: (
|
|
158
|
+
message: Protocol.ProtocolSchemas<MethodDefinitions, EventDefinitions>["f"]["payload"]["Type"],
|
|
159
|
+
) => Effect.Effect<
|
|
160
|
+
void,
|
|
161
|
+
ConnectionError,
|
|
162
|
+
Protocol.ProtocolSchemas<MethodDefinitions, EventDefinitions>["f"]["payload"]["EncodingServices"]
|
|
163
|
+
>
|
|
230
164
|
}
|
|
231
165
|
|
|
232
166
|
const make = <
|
|
233
167
|
ClientSelf,
|
|
234
168
|
ClientId extends string,
|
|
235
169
|
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
236
|
-
EventDefinitions extends
|
|
170
|
+
EventDefinitions extends Record<string, S.Struct.Fields>,
|
|
237
171
|
R,
|
|
238
172
|
>(
|
|
239
173
|
client: Client<ClientSelf, ClientId, MethodDefinitions, EventDefinitions>,
|
|
@@ -241,33 +175,33 @@ const make = <
|
|
|
241
175
|
replay?: ReplayConfig | undefined,
|
|
242
176
|
) =>
|
|
243
177
|
Effect.gen(function* () {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const rcr: RcRef.RcRef<
|
|
247
|
-
TransportSession<ClientSelf, MethodDefinitions, EventDefinitions>,
|
|
248
|
-
ClientError
|
|
249
|
-
> = yield* RcRef.make({
|
|
178
|
+
const rcr: RcRef.RcRef<Session<ClientSelf, MethodDefinitions, EventDefinitions>, ClientError> = yield* RcRef.make({
|
|
250
179
|
acquire: Effect.gen(function* () {
|
|
180
|
+
type _ = typeof client.schema
|
|
181
|
+
type Event = ReturnType<typeof S.TaggedUnion<EventDefinitions>>["Type"]
|
|
182
|
+
|
|
183
|
+
yield* debug("AcquisitionStarted")
|
|
184
|
+
|
|
251
185
|
const { listen, send } = yield* build
|
|
252
186
|
|
|
253
|
-
const audition = yield* Deferred.make<void
|
|
254
|
-
|
|
187
|
+
const audition = yield* Deferred.make<void>()
|
|
188
|
+
|
|
189
|
+
const inflights: Record<string, Deferred.Deferred<_["f"]["success"]["Type"], FError<MethodDefinitions>>> = {}
|
|
255
190
|
let callId = 0
|
|
256
191
|
let takeCount = 0
|
|
257
|
-
|
|
258
|
-
const pubsub = yield* PubSub.unbounded<EventTake<_["Event"], ClientError>>()
|
|
192
|
+
const pubsub = yield* PubSub.unbounded<EventTake<Event, ClientError>>()
|
|
259
193
|
|
|
260
194
|
const replayState = yield* Ref.make<{
|
|
261
195
|
readonly startupOpen: boolean
|
|
262
|
-
readonly buffer: ReadonlyArray<EventTake<
|
|
196
|
+
readonly buffer: ReadonlyArray<EventTake<Event, ClientError>>
|
|
263
197
|
}>({
|
|
264
198
|
startupOpen: true,
|
|
265
199
|
buffer: [],
|
|
266
200
|
})
|
|
267
201
|
|
|
268
|
-
const publishTake = (take: Take.Take<
|
|
202
|
+
const publishTake = (take: Take.Take<Event, ClientError>, replayable?: boolean | undefined) =>
|
|
269
203
|
Effect.gen(function* () {
|
|
270
|
-
const eventTake: EventTake<
|
|
204
|
+
const eventTake: EventTake<Event, ClientError> = {
|
|
271
205
|
seq: takeCount++,
|
|
272
206
|
take,
|
|
273
207
|
}
|
|
@@ -287,10 +221,82 @@ const make = <
|
|
|
287
221
|
yield* PubSub.publish(pubsub, eventTake)
|
|
288
222
|
})
|
|
289
223
|
|
|
290
|
-
const
|
|
224
|
+
const outer = yield* Scope.Scope
|
|
225
|
+
const scope = yield* Scope.fork(outer, "sequential")
|
|
226
|
+
const end = Scope.close(scope, Exit.void)
|
|
227
|
+
|
|
228
|
+
const fiber = yield* listen(
|
|
229
|
+
Effect.fnUntraced(function* (message) {
|
|
230
|
+
switch (message._tag) {
|
|
231
|
+
case "AuditionSuccess": {
|
|
232
|
+
yield* debug("AuditionSucceeded")
|
|
233
|
+
yield* Deferred.succeed(audition, void 0)
|
|
234
|
+
return
|
|
235
|
+
}
|
|
236
|
+
case "Event": {
|
|
237
|
+
const { event } = message
|
|
238
|
+
yield* debug("EventEmitted", { event })
|
|
239
|
+
yield* publishTake([event as never], true)
|
|
240
|
+
return
|
|
241
|
+
}
|
|
242
|
+
case "FSuccess":
|
|
243
|
+
case "FFailure": {
|
|
244
|
+
const { id } = message
|
|
245
|
+
const deferred = inflights[id]
|
|
246
|
+
if (deferred) {
|
|
247
|
+
delete inflights[id]
|
|
248
|
+
switch (message._tag) {
|
|
249
|
+
case "FSuccess": {
|
|
250
|
+
const { _tag, value } = message.success as never
|
|
251
|
+
yield* debug("CallSucceeded", { id, _tag, value })
|
|
252
|
+
yield* Deferred.succeed(deferred, value)
|
|
253
|
+
return
|
|
254
|
+
}
|
|
255
|
+
case "FFailure": {
|
|
256
|
+
const { _tag, value } = message.failure as never
|
|
257
|
+
yield* debug("CallFailed", { id, _tag, value })
|
|
258
|
+
yield* Deferred.fail(deferred, value)
|
|
259
|
+
return
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return
|
|
264
|
+
}
|
|
265
|
+
case "AuditionFailure": {
|
|
266
|
+
const { client, routed } = message
|
|
267
|
+
yield* debug("AuditionFailed", { client, routed })
|
|
268
|
+
return yield* new AuditionError({ value: { client, routed } })
|
|
269
|
+
}
|
|
270
|
+
case "Disconnect": {
|
|
271
|
+
yield* debug("Disconnected")
|
|
272
|
+
return
|
|
273
|
+
}
|
|
274
|
+
case "TransportFailure": {
|
|
275
|
+
const { cause } = message
|
|
276
|
+
yield* debug("TransportFailed", { cause })
|
|
277
|
+
return yield* new ConnectionError({ cause })
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}),
|
|
281
|
+
).pipe(
|
|
282
|
+
Effect.ensuring(
|
|
283
|
+
Effect.all(
|
|
284
|
+
[
|
|
285
|
+
debug("ClientClosed", { unresolved: Record.keys(inflights).length }),
|
|
286
|
+
Deferred.succeed(audition, void 0),
|
|
287
|
+
RcRef.invalidate(rcr),
|
|
288
|
+
],
|
|
289
|
+
{ concurrency: "unbounded" },
|
|
290
|
+
),
|
|
291
|
+
),
|
|
292
|
+
Effect.forkScoped,
|
|
293
|
+
Effect.provideService(Scope.Scope, scope),
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
const events = Effect.gen(function* () {
|
|
291
297
|
const queue = yield* PubSub.subscribe(pubsub)
|
|
292
|
-
const live = (replayCount: number)
|
|
293
|
-
Stream.
|
|
298
|
+
const live = (replayCount: number) =>
|
|
299
|
+
Stream.fromSubscription(queue).pipe(
|
|
294
300
|
Stream.filter((entry) => entry.seq > replayCount),
|
|
295
301
|
Stream.map((entry) => entry.take),
|
|
296
302
|
Stream.flattenTake,
|
|
@@ -325,114 +331,58 @@ const make = <
|
|
|
325
331
|
),
|
|
326
332
|
live(replayCount),
|
|
327
333
|
)
|
|
328
|
-
}).pipe(Stream.
|
|
329
|
-
|
|
330
|
-
yield* listen(
|
|
331
|
-
Effect.fnUntraced(function* (message) {
|
|
332
|
-
switch (message._tag) {
|
|
333
|
-
case "Audition.Success": {
|
|
334
|
-
yield* Deferred.succeed(audition, void 0)
|
|
335
|
-
break
|
|
336
|
-
}
|
|
337
|
-
case "Event": {
|
|
338
|
-
const { event } = message
|
|
339
|
-
yield* publishTake(Take.of(event), true)
|
|
340
|
-
break
|
|
341
|
-
}
|
|
342
|
-
case "Call.Success":
|
|
343
|
-
case "Call.Failure": {
|
|
344
|
-
const { id } = message
|
|
345
|
-
const deferred = inflights[id]
|
|
346
|
-
if (deferred) {
|
|
347
|
-
delete inflights[id]
|
|
348
|
-
switch (message._tag) {
|
|
349
|
-
case "Call.Success": {
|
|
350
|
-
yield* Deferred.succeed(deferred, message.value.value)
|
|
351
|
-
break
|
|
352
|
-
}
|
|
353
|
-
case "Call.Failure": {
|
|
354
|
-
yield* Deferred.fail(deferred, message.cause.value)
|
|
355
|
-
break
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
break
|
|
360
|
-
}
|
|
361
|
-
case "Audition.Failure": {
|
|
362
|
-
const { actual, expected } = message
|
|
363
|
-
finalError = AuditionError.make({ value: { actual, expected } })
|
|
364
|
-
yield* Deferred.fail(audition, finalError)
|
|
365
|
-
break
|
|
366
|
-
}
|
|
367
|
-
case "Disconnect":
|
|
368
|
-
case "TransportFailure": {
|
|
369
|
-
yield* Deferred.succeed(audition, void 0)
|
|
370
|
-
switch (message._tag) {
|
|
371
|
-
case "Disconnect": {
|
|
372
|
-
finalError = UnresolvedError.make()
|
|
373
|
-
yield* publishTake(Take.end)
|
|
374
|
-
break
|
|
375
|
-
}
|
|
376
|
-
case "TransportFailure": {
|
|
377
|
-
const { cause } = message
|
|
378
|
-
yield* publishTake(Take.fail((finalError = ConnectionError.make({ cause }))))
|
|
379
|
-
break
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
break
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
}),
|
|
386
|
-
).pipe(
|
|
387
|
-
Effect.ensuring(
|
|
388
|
-
Effect.all(
|
|
389
|
-
[
|
|
390
|
-
PubSub.shutdown(pubsub),
|
|
391
|
-
Effect.forEach(
|
|
392
|
-
Record.values(inflights),
|
|
393
|
-
(deferred) => Deferred.fail(deferred, finalError ?? UnresolvedError.make()),
|
|
394
|
-
{
|
|
395
|
-
concurrency: "unbounded",
|
|
396
|
-
},
|
|
397
|
-
),
|
|
398
|
-
RcRef.invalidate(rcr),
|
|
399
|
-
],
|
|
400
|
-
{ concurrency: "unbounded" },
|
|
401
|
-
),
|
|
402
|
-
),
|
|
403
|
-
Effect.forkScoped,
|
|
404
|
-
)
|
|
334
|
+
}).pipe(Stream.unwrap, Stream.interruptWhen(Fiber.join(fiber)))
|
|
405
335
|
|
|
406
336
|
yield* Deferred.await(audition)
|
|
407
337
|
|
|
338
|
+
const encodingServices = yield* Effect.context<_["f"]["payload"]["EncodingServices"]>()
|
|
339
|
+
|
|
408
340
|
const f: F<ClientSelf, MethodDefinitions> = (_tag) =>
|
|
409
|
-
Effect.fnUntraced(
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
341
|
+
Effect.fnUntraced(
|
|
342
|
+
function* (value) {
|
|
343
|
+
const exit = fiber.pollUnsafe()
|
|
344
|
+
if (exit) {
|
|
345
|
+
return yield* Exit.match(exit, {
|
|
346
|
+
onSuccess: () => new UnresolvedError(),
|
|
347
|
+
onFailure: flow(
|
|
348
|
+
Cause.findError,
|
|
349
|
+
Result.match({
|
|
350
|
+
onSuccess: identity,
|
|
351
|
+
onFailure: () => new UnresolvedError(),
|
|
352
|
+
}),
|
|
353
|
+
),
|
|
354
|
+
})
|
|
355
|
+
}
|
|
356
|
+
const id = callId++
|
|
357
|
+
const inflight = yield* Deferred.make<_["f"]["success"]["Type"], FError<MethodDefinitions>>()
|
|
358
|
+
inflights[id] = inflight
|
|
359
|
+
yield* send({
|
|
360
|
+
_tag: "FPayload",
|
|
361
|
+
id,
|
|
362
|
+
payload: { _tag, value } as never,
|
|
363
|
+
})
|
|
364
|
+
return yield* Effect.raceFirst(
|
|
365
|
+
Deferred.await(inflight),
|
|
366
|
+
Fiber.join(fiber).pipe(Effect.andThen(() => new UnresolvedError().asEffect())),
|
|
367
|
+
)
|
|
368
|
+
},
|
|
369
|
+
span("f"),
|
|
370
|
+
Effect.scoped,
|
|
371
|
+
Effect.provide(encodingServices),
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
return { events, f, end }
|
|
375
|
+
}).pipe(span("acquire", { attributes: { client: client.key } }), Effect.annotateLogs("client", client.key)),
|
|
426
376
|
})
|
|
427
377
|
|
|
428
378
|
return rcr
|
|
429
|
-
}).pipe(Layer.
|
|
379
|
+
}).pipe(Layer.effect(client))
|
|
430
380
|
|
|
431
381
|
export const layerSocket = <
|
|
432
382
|
ClientSelf,
|
|
433
383
|
ClientId extends string,
|
|
434
384
|
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
435
|
-
EventDefinitions extends
|
|
385
|
+
EventDefinitions extends Record<string, S.Struct.Fields>,
|
|
436
386
|
>({
|
|
437
387
|
client,
|
|
438
388
|
url,
|
|
@@ -443,7 +393,13 @@ export const layerSocket = <
|
|
|
443
393
|
readonly url?: string | undefined
|
|
444
394
|
readonly protocols?: string | Array<string> | undefined
|
|
445
395
|
readonly replay?: ReplayConfig | undefined
|
|
446
|
-
}): Layer.Layer<
|
|
396
|
+
}): Layer.Layer<
|
|
397
|
+
ClientSelf,
|
|
398
|
+
never,
|
|
399
|
+
| Socket.WebSocketConstructor
|
|
400
|
+
| Protocol.ProtocolSchemas<MethodDefinitions, EventDefinitions>["actor"]["DecodingServices"]
|
|
401
|
+
| Protocol.ProtocolSchemas<MethodDefinitions, EventDefinitions>["f"]["payload"]["EncodingServices"]
|
|
402
|
+
> =>
|
|
447
403
|
make<ClientSelf, ClientId, MethodDefinitions, EventDefinitions, Socket.WebSocketConstructor>(
|
|
448
404
|
client,
|
|
449
405
|
Effect.gen(function* () {
|
|
@@ -453,61 +409,59 @@ export const layerSocket = <
|
|
|
453
409
|
return {
|
|
454
410
|
listen: Effect.fnUntraced(function* (publish) {
|
|
455
411
|
yield* socket
|
|
456
|
-
.runRaw(
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
)
|
|
461
|
-
|
|
462
|
-
}),
|
|
412
|
+
.runRaw((raw) =>
|
|
413
|
+
pipe(
|
|
414
|
+
raw instanceof Uint8Array ? new TextDecoder().decode(raw) : raw,
|
|
415
|
+
S.decodeUnknownEffect(S.fromJsonString(client.schema.actor)),
|
|
416
|
+
Effect.andThen(publish),
|
|
417
|
+
),
|
|
463
418
|
)
|
|
464
419
|
.pipe(
|
|
465
|
-
Effect.catchTag("ParseError", (cause) => publish({ _tag: "TransportFailure", cause })),
|
|
466
420
|
Effect.catchTag(
|
|
467
421
|
"SocketError",
|
|
468
422
|
Effect.fnUntraced(function* (cause) {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
case "
|
|
472
|
-
case "
|
|
473
|
-
case "
|
|
474
|
-
|
|
475
|
-
console.log(cause)
|
|
423
|
+
const { reason } = cause
|
|
424
|
+
switch (reason._tag) {
|
|
425
|
+
case "SocketReadError":
|
|
426
|
+
case "SocketWriteError":
|
|
427
|
+
case "SocketOpenError": {
|
|
428
|
+
yield* debug(reason._tag, { cause })
|
|
476
429
|
return yield* publish({ _tag: "TransportFailure", cause })
|
|
477
430
|
}
|
|
478
|
-
case "
|
|
479
|
-
const { code, closeReason } =
|
|
431
|
+
case "SocketCloseError": {
|
|
432
|
+
const { code, closeReason } = reason
|
|
480
433
|
switch (code) {
|
|
481
434
|
case 1000: {
|
|
482
435
|
return yield* publish({ _tag: "Disconnect" })
|
|
483
436
|
}
|
|
484
437
|
case 4003: {
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
}
|
|
489
|
-
const { actual, expected } = parsed.value
|
|
490
|
-
return yield* publish({
|
|
491
|
-
_tag: "Audition.Failure",
|
|
492
|
-
actual,
|
|
493
|
-
expected,
|
|
494
|
-
})
|
|
438
|
+
return yield* S.decodeUnknownEffect(S.fromJsonString(Protocol.AuditionFailure))(
|
|
439
|
+
closeReason,
|
|
440
|
+
).pipe(Effect.andThen(publish))
|
|
495
441
|
}
|
|
496
442
|
}
|
|
443
|
+
yield* debug("SocketCloseError", { cause })
|
|
497
444
|
return yield* publish({ _tag: "TransportFailure", cause })
|
|
498
445
|
}
|
|
499
446
|
}
|
|
500
447
|
}),
|
|
501
448
|
),
|
|
449
|
+
Effect.catchTag("SchemaError", Effect.die),
|
|
502
450
|
)
|
|
503
|
-
}),
|
|
504
|
-
send: Effect.fnUntraced(
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
451
|
+
}, span("listen")),
|
|
452
|
+
send: Effect.fnUntraced(
|
|
453
|
+
function* (v) {
|
|
454
|
+
const write = yield* socket.writer
|
|
455
|
+
const message = yield* S.encodeEffect(S.fromJsonString(client.schema.f.payload))(v).pipe(
|
|
456
|
+
Effect.mapError((cause) => new ConnectionError({ cause })),
|
|
457
|
+
)
|
|
458
|
+
yield* write(message).pipe(
|
|
459
|
+
Effect.catchTag("SocketError", (cause) => new ConnectionError({ cause }).asEffect()),
|
|
460
|
+
)
|
|
461
|
+
},
|
|
462
|
+
span("send"),
|
|
463
|
+
Effect.scoped,
|
|
464
|
+
),
|
|
511
465
|
}
|
|
512
466
|
}),
|
|
513
467
|
replay,
|
|
@@ -517,42 +471,53 @@ export const layerWorker = <
|
|
|
517
471
|
ClientSelf,
|
|
518
472
|
ClientId extends string,
|
|
519
473
|
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
520
|
-
EventDefinitions extends
|
|
474
|
+
EventDefinitions extends Record<string, S.Struct.Fields>,
|
|
521
475
|
>({
|
|
522
476
|
client,
|
|
523
477
|
replay,
|
|
524
478
|
}: {
|
|
525
479
|
readonly client: Client<ClientSelf, ClientId, MethodDefinitions, EventDefinitions>
|
|
526
480
|
readonly replay?: ReplayConfig | undefined
|
|
527
|
-
}): Layer.Layer<
|
|
528
|
-
|
|
481
|
+
}): Layer.Layer<
|
|
482
|
+
ClientSelf,
|
|
483
|
+
never,
|
|
484
|
+
| Worker.WorkerPlatform
|
|
485
|
+
| Worker.Spawner
|
|
486
|
+
| Protocol.ProtocolSchemas<MethodDefinitions, EventDefinitions>["actor"]["DecodingServices"]
|
|
487
|
+
| Protocol.ProtocolSchemas<MethodDefinitions, EventDefinitions>["f"]["payload"]["EncodingServices"]
|
|
488
|
+
> =>
|
|
489
|
+
make<ClientSelf, ClientId, MethodDefinitions, EventDefinitions, Worker.WorkerPlatform | Worker.Spawner>(
|
|
529
490
|
client,
|
|
530
491
|
Effect.gen(function* () {
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
492
|
+
type T = typeof client.schema
|
|
493
|
+
|
|
494
|
+
const platform = yield* Worker.WorkerPlatform
|
|
495
|
+
const backing = yield* platform
|
|
496
|
+
.spawn<T["actor"]["Type"], T["f"]["payload"]["Type"] | string>(0)
|
|
497
|
+
.pipe(Effect.catchTag("WorkerError", (cause) => new ConnectionError({ cause }).asEffect()))
|
|
498
|
+
|
|
499
|
+
const send = (message: T["f"]["payload"]["Type"]) =>
|
|
500
|
+
backing.send(message).pipe(
|
|
501
|
+
Effect.catchTag("WorkerError", (cause) => new ConnectionError({ cause }).asEffect()),
|
|
502
|
+
span("send"),
|
|
503
|
+
)
|
|
542
504
|
|
|
543
505
|
return {
|
|
544
506
|
listen: Effect.fnUntraced(function* (publish) {
|
|
545
|
-
yield*
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
507
|
+
const stop = yield* Deferred.make<void>()
|
|
508
|
+
yield* Effect.raceFirst(
|
|
509
|
+
backing.run(
|
|
510
|
+
Effect.fnUntraced(function* (message) {
|
|
511
|
+
yield* publish(message)
|
|
512
|
+
if (message._tag === "Disconnect" || message._tag === "AuditionFailure") {
|
|
513
|
+
yield* Deferred.succeed(stop, void 0)
|
|
514
|
+
}
|
|
515
|
+
}),
|
|
516
|
+
{ onSpawn: backing.send(client.key).pipe(Effect.orDie) },
|
|
551
517
|
),
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
}),
|
|
518
|
+
Deferred.await(stop),
|
|
519
|
+
).pipe(Effect.catchTag("WorkerError", (cause) => publish({ _tag: "TransportFailure", cause })))
|
|
520
|
+
}, span("listen")),
|
|
556
521
|
send,
|
|
557
522
|
}
|
|
558
523
|
}),
|