liminal 0.17.1 → 0.17.2

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.
Files changed (109) hide show
  1. package/Accumulator.ts +16 -10
  2. package/Actor.ts +35 -29
  3. package/Audition.ts +17 -12
  4. package/CHANGELOG.md +6 -0
  5. package/Client.ts +260 -295
  6. package/ClientHandle.ts +17 -6
  7. package/F.ts +3 -9
  8. package/LICENSE +202 -0
  9. package/Method.ts +11 -13
  10. package/Protocol.ts +104 -126
  11. package/Send.ts +9 -5
  12. package/_util/Diagnostic.ts +16 -0
  13. package/_util/Mutex.ts +5 -5
  14. package/dist/Accumulator.d.ts +2 -2
  15. package/dist/Accumulator.js +12 -8
  16. package/dist/Accumulator.js.map +1 -1
  17. package/dist/Actor.d.ts +9 -10
  18. package/dist/Actor.js +6 -15
  19. package/dist/Actor.js.map +1 -1
  20. package/dist/Audition.d.ts +3 -4
  21. package/dist/Audition.js +7 -5
  22. package/dist/Audition.js.map +1 -1
  23. package/dist/Client.d.ts +20 -39
  24. package/dist/Client.js +119 -139
  25. package/dist/Client.js.map +1 -1
  26. package/dist/ClientHandle.d.ts +5 -6
  27. package/dist/ClientHandle.js +1 -1
  28. package/dist/ClientHandle.js.map +1 -1
  29. package/dist/F.d.ts +3 -12
  30. package/dist/F.js +1 -3
  31. package/dist/F.js.map +1 -1
  32. package/dist/Method.d.ts +11 -12
  33. package/dist/Method.js.map +1 -1
  34. package/dist/Protocol.d.ts +46 -89
  35. package/dist/Protocol.js +35 -11
  36. package/dist/Protocol.js.map +1 -1
  37. package/dist/Send.d.ts +2 -3
  38. package/dist/Send.js +1 -1
  39. package/dist/Send.js.map +1 -1
  40. package/dist/_util/Diagnostic.d.ts +5 -0
  41. package/dist/_util/Diagnostic.js +10 -0
  42. package/dist/_util/Diagnostic.js.map +1 -0
  43. package/dist/_util/Mutex.d.ts +2 -2
  44. package/dist/_util/Mutex.js +4 -4
  45. package/dist/_util/Mutex.js.map +1 -1
  46. package/dist/errors.d.ts +15 -12
  47. package/dist/errors.js +6 -4
  48. package/dist/errors.js.map +1 -1
  49. package/dist/experimental/BranchLive.d.ts +1 -1
  50. package/dist/experimental/BranchLive.js +1 -1
  51. package/dist/experimental/BranchLive.js.map +1 -1
  52. package/dist/experimental/L/append.d.ts +1 -1
  53. package/dist/experimental/L/append.js +2 -2
  54. package/dist/experimental/L/append.js.map +1 -1
  55. package/dist/experimental/L/assistant.d.ts +2 -2
  56. package/dist/experimental/L/assistant.js +1 -1
  57. package/dist/experimental/L/assistant.js.map +1 -1
  58. package/dist/experimental/L/assistantSchema.d.ts +2 -2
  59. package/dist/experimental/L/assistantSchema.js +1 -1
  60. package/dist/experimental/L/assistantSchema.js.map +1 -1
  61. package/dist/experimental/L/assistantStream.d.ts +2 -2
  62. package/dist/experimental/L/assistantStream.js +1 -1
  63. package/dist/experimental/L/assistantStream.js.map +1 -1
  64. package/dist/experimental/L/branch.d.ts +1 -1
  65. package/dist/experimental/L/clear.d.ts +1 -1
  66. package/dist/experimental/L/clear.js +1 -1
  67. package/dist/experimental/L/clear.js.map +1 -1
  68. package/dist/experimental/L/history.d.ts +2 -2
  69. package/dist/experimental/L/history.js +2 -2
  70. package/dist/experimental/L/history.js.map +1 -1
  71. package/dist/experimental/L/init.d.ts +1 -1
  72. package/dist/experimental/L/init.js +1 -1
  73. package/dist/experimental/L/init.js.map +1 -1
  74. package/dist/experimental/L/matrix.d.ts +2 -2
  75. package/dist/experimental/L/system.d.ts +1 -1
  76. package/dist/experimental/L/system.js +1 -1
  77. package/dist/experimental/L/system.js.map +1 -1
  78. package/dist/experimental/L/user.d.ts +1 -1
  79. package/dist/experimental/L/user.js +1 -1
  80. package/dist/experimental/L/user.js.map +1 -1
  81. package/dist/experimental/Loader.d.ts +3 -3
  82. package/dist/experimental/Loader.js +2 -2
  83. package/dist/experimental/Loader.js.map +1 -1
  84. package/dist/experimental/TaggedTemplateFunction.d.ts +2 -2
  85. package/dist/experimental/Template.d.ts +8 -12
  86. package/dist/experimental/Template.js +53 -51
  87. package/dist/experimental/Template.js.map +1 -1
  88. package/dist/tsconfig.tsbuildinfo +1 -1
  89. package/errors.ts +12 -4
  90. package/experimental/BranchLive.ts +1 -1
  91. package/experimental/L/append.ts +2 -2
  92. package/experimental/L/assistant.ts +1 -1
  93. package/experimental/L/assistantSchema.ts +3 -3
  94. package/experimental/L/assistantStream.ts +1 -1
  95. package/experimental/L/clear.ts +1 -1
  96. package/experimental/L/history.ts +2 -2
  97. package/experimental/L/init.ts +1 -1
  98. package/experimental/L/matrix.ts +3 -3
  99. package/experimental/L/system.ts +1 -1
  100. package/experimental/L/user.ts +1 -1
  101. package/experimental/Loader.ts +3 -3
  102. package/experimental/TaggedTemplateFunction.ts +2 -2
  103. package/experimental/Template.ts +62 -70
  104. package/package.json +2 -6
  105. package/tsconfig.json +1 -1
  106. package/_types.ts +0 -27
  107. package/dist/_types.d.ts +0 -22
  108. package/dist/_types.js +0 -2
  109. 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 FieldsRecord,
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 TransportSession<
61
+ export interface Session<
51
62
  ClientSelf,
52
63
  MethodDefinitions extends Record<string, MethodDefinition.Any>,
53
- EventDefinitions extends FieldsRecord,
64
+ EventDefinitions extends Record<string, S.Struct.Fields>,
54
65
  > {
55
- readonly events: Stream.Stream<FieldsRecord.TaggedMember.Type<EventDefinitions>, ClientError>
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 endEvents: Effect.Effect<void>
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 FieldsRecord,
66
- > = RcRef.RcRef<TransportSession<ClientSelf, MethodDefinitions, EventDefinitions>, ClientError>
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 FieldsRecord,
115
- > extends Context.Tag<ClientSelf, Service<ClientSelf, MethodDefinitions, EventDefinitions>> {
116
- new (_: never): Context.TagClassShape<ClientId, Service<ClientSelf, MethodDefinitions, EventDefinitions>>
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: ClientSchema<MethodDefinitions, EventDefinitions>
123
-
124
- readonly events: Stream.Stream<FieldsRecord.TaggedMember.Type<EventDefinitions>, ClientError, ClientSelf>
91
+ readonly schema: Protocol.ProtocolSchemas<MethodDefinitions, EventDefinitions>
125
92
 
126
- readonly endEvents: Effect.Effect<void, never, ClientSelf>
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 FieldsRecord,
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.Tag(id)<ClientSelf, Service<ClientSelf, MethodDefinitions, EventDefinitions>>()
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: Stream.Stream<FieldsRecord.TaggedMember.Type<EventDefinitions>, ClientError, ClientSelf> = tag.pipe(
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.pipe(Effect.flatMap(RcRef.invalidate))
200
-
201
- const endEvents = tag.pipe(
202
- Effect.flatMap(RcRef.get),
203
- Effect.flatMap(({ endEvents }) => endEvents),
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: { call, event, actor },
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 FieldsRecord,
143
+ EventDefinitions extends Record<string, S.Struct.Fields>,
222
144
  > {
223
145
  readonly listen: (
224
146
  publish: (
225
- message: Protocol.Actor.Type<MethodDefinitions, EventDefinitions> | typeof Protocol.TransportFailure.Type,
226
- ) => Effect.Effect<void, never>,
227
- ) => Effect.Effect<void, never, Scope.Scope>
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: (v: Protocol.Call.Payload.Type<MethodDefinitions>) => Effect.Effect<void, ConnectionError, never>
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 FieldsRecord,
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
- type _ = Spec<MethodDefinitions, EventDefinitions>
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, AuditionError>()
254
- const inflights: Record<string, Deferred.Deferred<_["Call"]["Success"], FError<MethodDefinitions>>> = {}
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
- let finalError: ClientError | UnresolvedError | undefined
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<_["Event"], ClientError>>
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<_["Event"], ClientError>, replayable?: boolean | undefined) =>
202
+ const publishTake = (take: Take.Take<Event, ClientError>, replayable?: boolean | undefined) =>
269
203
  Effect.gen(function* () {
270
- const eventTake: EventTake<_["Event"], ClientError> = {
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 events: Stream.Stream<_["Event"], ClientError> = Effect.gen(function* () {
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): Stream.Stream<_["Event"], ClientError> =>
293
- Stream.fromQueue(queue).pipe(
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.unwrapScoped)
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(function* (value) {
410
- if (finalError) return yield* finalError
411
- const id = callId++
412
- const inflight = yield* Deferred.make<_["Call"]["Success"], FError<MethodDefinitions>>()
413
- inflights[id] = inflight
414
- yield* send({
415
- _tag: "Call.Payload",
416
- id,
417
- payload: { _tag, value },
418
- })
419
- return yield* Deferred.await(inflight)
420
- }, Effect.scoped)
421
-
422
- const endEvents = publishTake(Take.end)
423
-
424
- return { events, f, endEvents }
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.scoped(client))
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 FieldsRecord,
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<ClientSelf, never, Socket.WebSocketConstructor> =>
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
- Effect.fnUntraced(function* (raw) {
458
- const message = yield* S.decodeUnknown(S.parseJson(client.schema.actor))(
459
- raw instanceof Uint8Array ? new TextDecoder().decode(raw) : raw,
460
- )
461
- yield* publish(message)
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
- switch (cause.reason) {
470
- case "Read":
471
- case "Write":
472
- case "Open":
473
- case "OpenTimeout": {
474
- // TODO
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 "Close": {
479
- const { code, closeReason } = cause
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
- const parsed = S.decodeUnknownOption(S.parseJson(Protocol.Audition.Failure))(closeReason)
486
- if (parsed._tag === "None") {
487
- return yield* publish({ _tag: "TransportFailure", cause })
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(function* (v) {
505
- const write = yield* socket.writer
506
- const message = yield* S.encode(S.parseJson(client.schema.call.payload))(v).pipe(
507
- Effect.mapError((cause) => ConnectionError.make({ cause })),
508
- )
509
- yield* write(message).pipe(Effect.catchTag("SocketError", (cause) => ConnectionError.make({ cause })))
510
- }, Effect.scoped),
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 FieldsRecord,
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<ClientSelf, never, Worker.PlatformWorker | Worker.Spawner> =>
528
- make<ClientSelf, ClientId, MethodDefinitions, EventDefinitions, Worker.PlatformWorker | Worker.Spawner>(
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
- const manager = yield* Worker.makeManager
532
- const worker = yield* manager
533
- .spawn<
534
- Protocol.Call.Payload.Type<MethodDefinitions> | string,
535
- Protocol.Actor.Type<MethodDefinitions, EventDefinitions>,
536
- never
537
- >({})
538
- .pipe(Effect.catchTag("WorkerError", (cause) => ConnectionError.make({ cause })))
539
-
540
- const send = (message: Protocol.Call.Payload.Type<MethodDefinitions>) =>
541
- worker.executeEffect(message).pipe(Effect.catchTag("WorkerError", (cause) => ConnectionError.make({ cause })))
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* worker.execute(client.key).pipe(
546
- Stream.catchTag("WorkerError", (cause) =>
547
- Effect.gen(function* () {
548
- yield* publish({ _tag: "TransportFailure", cause })
549
- return Stream.empty
550
- }).pipe(Stream.unwrap),
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
- Stream.takeUntil((message) => message._tag === "Disconnect" || message._tag === "Audition.Failure"),
553
- Stream.runForEach(publish),
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
  }),