liminal 0.17.0 → 0.17.1
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 +97 -0
- package/Actor.ts +118 -0
- package/Audition.ts +105 -0
- package/CHANGELOG.md +2 -212
- package/Client.ts +560 -0
- package/ClientHandle.ts +39 -0
- package/F.ts +18 -0
- package/Method.ts +39 -0
- package/Protocol.ts +142 -0
- package/Send.ts +8 -0
- package/_constants.ts +1 -0
- package/_types.ts +27 -0
- package/_util/Mutex.ts +13 -0
- package/_util/phantom.ts +1 -0
- package/dist/Accumulator.d.ts +22 -0
- package/dist/Accumulator.js +33 -0
- package/dist/Accumulator.js.map +1 -0
- package/dist/Actor.d.ts +30 -0
- package/dist/Actor.js +31 -0
- package/dist/Actor.js.map +1 -0
- package/dist/Audition.d.ts +18 -0
- package/dist/Audition.js +27 -0
- package/dist/Audition.js.map +1 -0
- package/dist/Client.d.ts +65 -0
- package/dist/Client.js +248 -0
- package/dist/Client.js.map +1 -0
- package/dist/ClientHandle.d.ts +18 -0
- package/dist/ClientHandle.js +10 -0
- package/dist/ClientHandle.js.map +1 -0
- package/dist/F.d.ts +13 -0
- package/dist/F.js +4 -0
- package/dist/F.js.map +1 -0
- package/dist/Method.d.ts +23 -0
- package/dist/Method.js +4 -0
- package/dist/Method.js.map +1 -0
- package/dist/Protocol.d.ts +96 -0
- package/dist/Protocol.js +13 -0
- package/dist/Protocol.js.map +1 -0
- package/dist/Send.d.ts +3 -0
- package/dist/Send.js +2 -0
- package/dist/Send.js.map +1 -0
- package/dist/_constants.d.ts +1 -0
- package/dist/_constants.js +2 -0
- package/dist/_constants.js.map +1 -0
- package/dist/_types.d.ts +22 -0
- package/dist/_types.js +2 -0
- package/dist/_types.js.map +1 -0
- package/dist/_util/Mutex.d.ts +7 -0
- package/dist/_util/Mutex.js +9 -0
- package/dist/_util/Mutex.js.map +1 -0
- package/dist/_util/phantom.d.ts +3 -0
- package/dist/_util/phantom.js +2 -0
- package/dist/_util/phantom.js.map +1 -0
- package/dist/errors.d.ts +20 -0
- package/dist/errors.js +13 -0
- package/dist/errors.js.map +1 -0
- package/dist/experimental/BranchLive.d.ts +3 -0
- package/dist/experimental/BranchLive.js +5 -0
- package/dist/experimental/BranchLive.js.map +1 -0
- package/dist/experimental/L/L.d.ts +11 -0
- package/dist/experimental/L/L.js +12 -0
- package/dist/experimental/L/L.js.map +1 -0
- package/dist/experimental/L/append.d.ts +3 -0
- package/dist/experimental/L/append.js +4 -0
- package/dist/experimental/L/append.js.map +1 -0
- package/dist/experimental/L/assistant.d.ts +3 -0
- package/dist/experimental/L/assistant.js +13 -0
- package/dist/experimental/L/assistant.js.map +1 -0
- package/dist/experimental/L/assistantSchema.d.ts +3 -0
- package/dist/experimental/L/assistantSchema.js +13 -0
- package/dist/experimental/L/assistantSchema.js.map +1 -0
- package/dist/experimental/L/assistantStream.d.ts +3 -0
- package/dist/experimental/L/assistantStream.js +5 -0
- package/dist/experimental/L/assistantStream.js.map +1 -0
- package/dist/experimental/L/branch.d.ts +2 -0
- package/dist/experimental/L/branch.js +4 -0
- package/dist/experimental/L/branch.js.map +1 -0
- package/dist/experimental/L/clear.d.ts +3 -0
- package/dist/experimental/L/clear.js +7 -0
- package/dist/experimental/L/clear.js.map +1 -0
- package/dist/experimental/L/history.d.ts +3 -0
- package/dist/experimental/L/history.js +4 -0
- package/dist/experimental/L/history.js.map +1 -0
- package/dist/experimental/L/init.d.ts +3 -0
- package/dist/experimental/L/init.js +4 -0
- package/dist/experimental/L/init.js.map +1 -0
- package/dist/experimental/L/matrix.d.ts +10 -0
- package/dist/experimental/L/matrix.js +6 -0
- package/dist/experimental/L/matrix.js.map +1 -0
- package/dist/experimental/L/system.d.ts +1 -0
- package/dist/experimental/L/system.js +4 -0
- package/dist/experimental/L/system.js.map +1 -0
- package/dist/experimental/L/user.d.ts +1 -0
- package/dist/experimental/L/user.js +6 -0
- package/dist/experimental/L/user.js.map +1 -0
- package/dist/experimental/Loader.d.ts +16 -0
- package/dist/experimental/Loader.js +20 -0
- package/dist/experimental/Loader.js.map +1 -0
- package/dist/experimental/TaggedTemplateFunction.d.ts +11 -0
- package/dist/experimental/TaggedTemplateFunction.js +14 -0
- package/dist/experimental/TaggedTemplateFunction.js.map +1 -0
- package/dist/experimental/Template.d.ts +32 -0
- package/dist/experimental/Template.js +58 -0
- package/dist/experimental/Template.js.map +1 -0
- package/dist/experimental/index.d.ts +4 -0
- package/dist/experimental/index.js +5 -0
- package/dist/experimental/index.js.map +1 -0
- package/dist/index.d.ts +9 -7
- package/dist/index.js +9 -7
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/errors.ts +14 -0
- package/experimental/BranchLive.ts +6 -0
- package/experimental/L/L.ts +11 -0
- package/experimental/L/append.ts +12 -0
- package/experimental/L/assistant.ts +16 -0
- package/experimental/L/assistantSchema.ts +18 -0
- package/experimental/L/assistantStream.ts +9 -0
- package/experimental/L/branch.ts +5 -0
- package/experimental/L/clear.ts +7 -0
- package/experimental/L/history.ts +4 -0
- package/experimental/L/init.ts +4 -0
- package/experimental/L/matrix.ts +26 -0
- package/experimental/L/system.ts +5 -0
- package/experimental/L/user.ts +10 -0
- package/experimental/Loader.ts +35 -0
- package/experimental/TaggedTemplateFunction.ts +44 -0
- package/experimental/Template.ts +93 -0
- package/experimental/index.ts +4 -0
- package/index.ts +9 -7
- package/package.json +21 -30
- package/tsconfig.json +9 -0
- package/Digest.ts +0 -30
- package/Envelope.ts +0 -16
- package/F/F.ts +0 -1
- package/L/L.ts +0 -20
- package/L/append.ts +0 -14
- package/L/assistant.ts +0 -30
- package/L/assistantSchema.ts +0 -61
- package/L/assistantStream.ts +0 -22
- package/L/branch.ts +0 -20
- package/L/clear.ts +0 -14
- package/L/disable.ts +0 -13
- package/L/enable.ts +0 -24
- package/L/events.ts +0 -11
- package/L/json.ts +0 -12
- package/L/line.ts +0 -22
- package/L/listen.ts +0 -26
- package/L/messages.ts +0 -10
- package/L/prev.ts +0 -4
- package/L/provide.ts +0 -10
- package/L/self.ts +0 -5
- package/L/send.ts +0 -56
- package/L/system.ts +0 -25
- package/L/thread.ts +0 -16
- package/L/toolkit.ts +0 -14
- package/L/user.test.ts +0 -33
- package/L/user.ts +0 -32
- package/LEvent.ts +0 -33
- package/Thread.ts +0 -67
- package/dist/Digest.d.ts +0 -17
- package/dist/Digest.js +0 -7
- package/dist/Digest.js.map +0 -1
- package/dist/Envelope.d.ts +0 -13
- package/dist/Envelope.js +0 -2
- package/dist/Envelope.js.map +0 -1
- package/dist/F/F.d.ts +0 -1
- package/dist/F/F.js +0 -2
- package/dist/F/F.js.map +0 -1
- package/dist/L/L.d.ts +0 -20
- package/dist/L/L.js +0 -21
- package/dist/L/L.js.map +0 -1
- package/dist/L/append.d.ts +0 -5
- package/dist/L/append.js +0 -10
- package/dist/L/append.js.map +0 -1
- package/dist/L/assistant.d.ts +0 -6
- package/dist/L/assistant.js +0 -26
- package/dist/L/assistant.js.map +0 -1
- package/dist/L/assistantSchema.d.ts +0 -13
- package/dist/L/assistantSchema.js +0 -44
- package/dist/L/assistantSchema.js.map +0 -1
- package/dist/L/assistantStream.d.ts +0 -7
- package/dist/L/assistantStream.js +0 -17
- package/dist/L/assistantStream.js.map +0 -1
- package/dist/L/branch.d.ts +0 -3
- package/dist/L/branch.js +0 -19
- package/dist/L/branch.js.map +0 -1
- package/dist/L/clear.d.ts +0 -5
- package/dist/L/clear.js +0 -12
- package/dist/L/clear.js.map +0 -1
- package/dist/L/disable.d.ts +0 -4
- package/dist/L/disable.js +0 -10
- package/dist/L/disable.js.map +0 -1
- package/dist/L/enable.d.ts +0 -5
- package/dist/L/enable.js +0 -13
- package/dist/L/enable.js.map +0 -1
- package/dist/L/events.d.ts +0 -5
- package/dist/L/events.js +0 -6
- package/dist/L/events.js.map +0 -1
- package/dist/L/json.d.ts +0 -5
- package/dist/L/json.js +0 -8
- package/dist/L/json.js.map +0 -1
- package/dist/L/line.d.ts +0 -5
- package/dist/L/line.js +0 -11
- package/dist/L/line.js.map +0 -1
- package/dist/L/listen.d.ts +0 -7
- package/dist/L/listen.js +0 -14
- package/dist/L/listen.js.map +0 -1
- package/dist/L/messages.d.ts +0 -5
- package/dist/L/messages.js +0 -5
- package/dist/L/messages.js.map +0 -1
- package/dist/L/prev.d.ts +0 -5
- package/dist/L/prev.js +0 -5
- package/dist/L/prev.js.map +0 -1
- package/dist/L/provide.d.ts +0 -3
- package/dist/L/provide.js +0 -4
- package/dist/L/provide.js.map +0 -1
- package/dist/L/self.d.ts +0 -3
- package/dist/L/self.js +0 -4
- package/dist/L/self.js.map +0 -1
- package/dist/L/send.d.ts +0 -6
- package/dist/L/send.js +0 -34
- package/dist/L/send.js.map +0 -1
- package/dist/L/system.d.ts +0 -5
- package/dist/L/system.js +0 -14
- package/dist/L/system.js.map +0 -1
- package/dist/L/thread.d.ts +0 -3
- package/dist/L/thread.js +0 -15
- package/dist/L/thread.js.map +0 -1
- package/dist/L/toolkit.d.ts +0 -4
- package/dist/L/toolkit.js +0 -9
- package/dist/L/toolkit.js.map +0 -1
- package/dist/L/user.d.ts +0 -6
- package/dist/L/user.js +0 -13
- package/dist/L/user.js.map +0 -1
- package/dist/L/user.test.d.ts +0 -1
- package/dist/L/user.test.js.map +0 -1
- package/dist/LEvent.d.ts +0 -154
- package/dist/LEvent.js +0 -19
- package/dist/LEvent.js.map +0 -1
- package/dist/Thread.d.ts +0 -52
- package/dist/Thread.js +0 -32
- package/dist/Thread.js.map +0 -1
- package/dist/patterns/Debate.d.ts +0 -1
- package/dist/patterns/Debate.js +0 -3
- package/dist/patterns/Debate.js.map +0 -1
- package/dist/patterns/Model.d.ts +0 -6
- package/dist/patterns/Model.js +0 -14
- package/dist/patterns/Model.js.map +0 -1
- package/dist/patterns/Route.d.ts +0 -3
- package/dist/patterns/Route.js +0 -14
- package/dist/patterns/Route.js.map +0 -1
- package/dist/util/JsonValue.d.ts +0 -8
- package/dist/util/JsonValue.js +0 -109
- package/dist/util/JsonValue.js.map +0 -1
- package/dist/util/NeverTool.d.ts +0 -3
- package/dist/util/NeverTool.js +0 -3
- package/dist/util/NeverTool.js.map +0 -1
- package/dist/util/Taggable.d.ts +0 -6
- package/dist/util/Taggable.js +0 -13
- package/dist/util/Taggable.js.map +0 -1
- package/dist/util/extract.d.ts +0 -3
- package/dist/util/extract.js +0 -2
- package/dist/util/extract.js.map +0 -1
- package/dist/util/messageCodec.d.ts +0 -98
- package/dist/util/messageCodec.js +0 -5
- package/dist/util/messageCodec.js.map +0 -1
- package/dist/util/normalizeRaw.d.ts +0 -4
- package/dist/util/normalizeRaw.js +0 -75
- package/dist/util/normalizeRaw.js.map +0 -1
- package/dist/util/prefix.d.ts +0 -1
- package/dist/util/prefix.js +0 -2
- package/dist/util/prefix.js.map +0 -1
- package/dist/util/raw.d.ts +0 -6
- package/dist/util/raw.js +0 -5
- package/dist/util/raw.js.map +0 -1
- package/patterns/Debate.ts +0 -2
- package/patterns/Model.ts +0 -29
- package/patterns/Route.ts +0 -25
- package/util/JsonValue.ts +0 -144
- package/util/NeverTool.ts +0 -10
- package/util/Taggable.ts +0 -33
- package/util/extract.ts +0 -7
- package/util/messageCodec.ts +0 -5
- package/util/normalizeRaw.ts +0 -93
- package/util/prefix.ts +0 -1
- package/util/raw.ts +0 -27
package/Client.ts
ADDED
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
import { Socket, Worker } from "@effect/platform"
|
|
2
|
+
import {
|
|
3
|
+
Context,
|
|
4
|
+
Encoding,
|
|
5
|
+
Deferred,
|
|
6
|
+
Effect,
|
|
7
|
+
Layer,
|
|
8
|
+
Option,
|
|
9
|
+
PubSub,
|
|
10
|
+
RcRef,
|
|
11
|
+
Record,
|
|
12
|
+
Ref,
|
|
13
|
+
Scope,
|
|
14
|
+
Stream,
|
|
15
|
+
Take,
|
|
16
|
+
Schema as S,
|
|
17
|
+
Array,
|
|
18
|
+
Struct,
|
|
19
|
+
} from "effect"
|
|
20
|
+
|
|
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
|
+
import type { MethodDefinition } from "./Method.ts"
|
|
25
|
+
import * as Protocol from "./Protocol.ts"
|
|
26
|
+
|
|
27
|
+
export const TypeId = "~liminal/Client" as const
|
|
28
|
+
|
|
29
|
+
export interface ClientDefinition<
|
|
30
|
+
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
31
|
+
EventDefinitions extends FieldsRecord,
|
|
32
|
+
> {
|
|
33
|
+
readonly methods: MethodDefinitions
|
|
34
|
+
|
|
35
|
+
readonly events: EventDefinitions
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ReplayConfig {
|
|
39
|
+
readonly mode: "startup" | "all-subscribers"
|
|
40
|
+
|
|
41
|
+
readonly limit?: number | undefined
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface EventTake<A, E> {
|
|
45
|
+
readonly seq: number
|
|
46
|
+
|
|
47
|
+
readonly take: Take.Take<A, E>
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface TransportSession<
|
|
51
|
+
ClientSelf,
|
|
52
|
+
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
53
|
+
EventDefinitions extends FieldsRecord,
|
|
54
|
+
> {
|
|
55
|
+
readonly events: Stream.Stream<FieldsRecord.TaggedMember.Type<EventDefinitions>, ClientError>
|
|
56
|
+
|
|
57
|
+
readonly f: F<ClientSelf, MethodDefinitions>
|
|
58
|
+
|
|
59
|
+
readonly endEvents: Effect.Effect<void>
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type Service<
|
|
63
|
+
ClientSelf,
|
|
64
|
+
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
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface Client<
|
|
111
|
+
ClientSelf,
|
|
112
|
+
ClientId extends string,
|
|
113
|
+
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>>
|
|
117
|
+
|
|
118
|
+
readonly [TypeId]: typeof TypeId
|
|
119
|
+
|
|
120
|
+
readonly definition: ClientDefinition<MethodDefinitions, EventDefinitions>
|
|
121
|
+
|
|
122
|
+
readonly schema: ClientSchema<MethodDefinitions, EventDefinitions>
|
|
123
|
+
|
|
124
|
+
readonly events: Stream.Stream<FieldsRecord.TaggedMember.Type<EventDefinitions>, ClientError, ClientSelf>
|
|
125
|
+
|
|
126
|
+
readonly endEvents: Effect.Effect<void, never, ClientSelf>
|
|
127
|
+
|
|
128
|
+
readonly f: F<ClientSelf, MethodDefinitions>
|
|
129
|
+
|
|
130
|
+
readonly invalidate: Effect.Effect<void, never, ClientSelf>
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export const Service =
|
|
134
|
+
<ClientSelf>() =>
|
|
135
|
+
<
|
|
136
|
+
ClientId extends string,
|
|
137
|
+
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
138
|
+
EventDefinitions extends FieldsRecord,
|
|
139
|
+
>(
|
|
140
|
+
id: ClientId,
|
|
141
|
+
definition: ClientDefinition<MethodDefinitions, EventDefinitions>,
|
|
142
|
+
): 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
|
+
)
|
|
186
|
+
|
|
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
|
+
)
|
|
192
|
+
|
|
193
|
+
const f: F<ClientSelf, MethodDefinitions> = (_tag) =>
|
|
194
|
+
Effect.fnUntraced(function* (value) {
|
|
195
|
+
const { f } = yield* tag.pipe(Effect.flatMap(RcRef.get))
|
|
196
|
+
return yield* f(_tag)(value)
|
|
197
|
+
}, Effect.scoped)
|
|
198
|
+
|
|
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),
|
|
204
|
+
Effect.scoped,
|
|
205
|
+
Effect.ignore,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
return Object.assign(tag, {
|
|
209
|
+
[TypeId]: TypeId,
|
|
210
|
+
definition,
|
|
211
|
+
schema: { call, event, actor },
|
|
212
|
+
events,
|
|
213
|
+
endEvents,
|
|
214
|
+
f,
|
|
215
|
+
invalidate,
|
|
216
|
+
})
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export interface Transport<
|
|
220
|
+
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
221
|
+
EventDefinitions extends FieldsRecord,
|
|
222
|
+
> {
|
|
223
|
+
readonly listen: (
|
|
224
|
+
publish: (
|
|
225
|
+
message: Protocol.Actor.Type<MethodDefinitions, EventDefinitions> | typeof Protocol.TransportFailure.Type,
|
|
226
|
+
) => Effect.Effect<void, never>,
|
|
227
|
+
) => Effect.Effect<void, never, Scope.Scope>
|
|
228
|
+
|
|
229
|
+
readonly send: (v: Protocol.Call.Payload.Type<MethodDefinitions>) => Effect.Effect<void, ConnectionError, never>
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const make = <
|
|
233
|
+
ClientSelf,
|
|
234
|
+
ClientId extends string,
|
|
235
|
+
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
236
|
+
EventDefinitions extends FieldsRecord,
|
|
237
|
+
R,
|
|
238
|
+
>(
|
|
239
|
+
client: Client<ClientSelf, ClientId, MethodDefinitions, EventDefinitions>,
|
|
240
|
+
build: Effect.Effect<Transport<MethodDefinitions, EventDefinitions>, ClientError, R | Scope.Scope>,
|
|
241
|
+
replay?: ReplayConfig | undefined,
|
|
242
|
+
) =>
|
|
243
|
+
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({
|
|
250
|
+
acquire: Effect.gen(function* () {
|
|
251
|
+
const { listen, send } = yield* build
|
|
252
|
+
|
|
253
|
+
const audition = yield* Deferred.make<void, AuditionError>()
|
|
254
|
+
const inflights: Record<string, Deferred.Deferred<_["Call"]["Success"], FError<MethodDefinitions>>> = {}
|
|
255
|
+
let callId = 0
|
|
256
|
+
let takeCount = 0
|
|
257
|
+
let finalError: ClientError | UnresolvedError | undefined
|
|
258
|
+
const pubsub = yield* PubSub.unbounded<EventTake<_["Event"], ClientError>>()
|
|
259
|
+
|
|
260
|
+
const replayState = yield* Ref.make<{
|
|
261
|
+
readonly startupOpen: boolean
|
|
262
|
+
readonly buffer: ReadonlyArray<EventTake<_["Event"], ClientError>>
|
|
263
|
+
}>({
|
|
264
|
+
startupOpen: true,
|
|
265
|
+
buffer: [],
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
const publishTake = (take: Take.Take<_["Event"], ClientError>, replayable?: boolean | undefined) =>
|
|
269
|
+
Effect.gen(function* () {
|
|
270
|
+
const eventTake: EventTake<_["Event"], ClientError> = {
|
|
271
|
+
seq: takeCount++,
|
|
272
|
+
take,
|
|
273
|
+
}
|
|
274
|
+
if (replay && replayable) {
|
|
275
|
+
yield* Ref.update(replayState, (state) => {
|
|
276
|
+
if (replay.mode === "startup" && !state.startupOpen) {
|
|
277
|
+
return state
|
|
278
|
+
}
|
|
279
|
+
const buffer =
|
|
280
|
+
replay.limit === undefined
|
|
281
|
+
? [...state.buffer, eventTake]
|
|
282
|
+
: [...(state.buffer.length >= replay.limit ? state.buffer.slice(1) : state.buffer), eventTake]
|
|
283
|
+
const { startupOpen } = state
|
|
284
|
+
return { startupOpen, buffer }
|
|
285
|
+
})
|
|
286
|
+
}
|
|
287
|
+
yield* PubSub.publish(pubsub, eventTake)
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
const events: Stream.Stream<_["Event"], ClientError> = Effect.gen(function* () {
|
|
291
|
+
const queue = yield* PubSub.subscribe(pubsub)
|
|
292
|
+
const live = (replayCount: number): Stream.Stream<_["Event"], ClientError> =>
|
|
293
|
+
Stream.fromQueue(queue).pipe(
|
|
294
|
+
Stream.filter((entry) => entry.seq > replayCount),
|
|
295
|
+
Stream.map((entry) => entry.take),
|
|
296
|
+
Stream.flattenTake,
|
|
297
|
+
)
|
|
298
|
+
if (!replay) {
|
|
299
|
+
return live(-1)
|
|
300
|
+
}
|
|
301
|
+
const buffer =
|
|
302
|
+
replay.mode === "all-subscribers"
|
|
303
|
+
? (yield* Ref.get(replayState)).buffer
|
|
304
|
+
: yield* Ref.modify(replayState, (state) =>
|
|
305
|
+
state.startupOpen
|
|
306
|
+
? [
|
|
307
|
+
state.buffer,
|
|
308
|
+
{
|
|
309
|
+
startupOpen: false,
|
|
310
|
+
buffer: [],
|
|
311
|
+
},
|
|
312
|
+
]
|
|
313
|
+
: [[], state],
|
|
314
|
+
)
|
|
315
|
+
const replayCount = Array.get(buffer, buffer.length - 1).pipe(
|
|
316
|
+
Option.map(({ seq }) => seq),
|
|
317
|
+
Option.getOrElse(() => -1),
|
|
318
|
+
)
|
|
319
|
+
return buffer.length === 0
|
|
320
|
+
? live(replayCount)
|
|
321
|
+
: Stream.concat(
|
|
322
|
+
Stream.fromIterable(buffer).pipe(
|
|
323
|
+
Stream.map((entry) => entry.take),
|
|
324
|
+
Stream.flattenTake,
|
|
325
|
+
),
|
|
326
|
+
live(replayCount),
|
|
327
|
+
)
|
|
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
|
+
)
|
|
405
|
+
|
|
406
|
+
yield* Deferred.await(audition)
|
|
407
|
+
|
|
408
|
+
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
|
+
}),
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
return rcr
|
|
429
|
+
}).pipe(Layer.scoped(client))
|
|
430
|
+
|
|
431
|
+
export const layerSocket = <
|
|
432
|
+
ClientSelf,
|
|
433
|
+
ClientId extends string,
|
|
434
|
+
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
435
|
+
EventDefinitions extends FieldsRecord,
|
|
436
|
+
>({
|
|
437
|
+
client,
|
|
438
|
+
url,
|
|
439
|
+
protocols,
|
|
440
|
+
replay,
|
|
441
|
+
}: {
|
|
442
|
+
readonly client: Client<ClientSelf, ClientId, MethodDefinitions, EventDefinitions>
|
|
443
|
+
readonly url?: string | undefined
|
|
444
|
+
readonly protocols?: string | Array<string> | undefined
|
|
445
|
+
readonly replay?: ReplayConfig | undefined
|
|
446
|
+
}): Layer.Layer<ClientSelf, never, Socket.WebSocketConstructor> =>
|
|
447
|
+
make<ClientSelf, ClientId, MethodDefinitions, EventDefinitions, Socket.WebSocketConstructor>(
|
|
448
|
+
client,
|
|
449
|
+
Effect.gen(function* () {
|
|
450
|
+
const socket = yield* Socket.makeWebSocket(url ?? "/", {
|
|
451
|
+
protocols: ["liminal", Encoding.encodeBase64Url(client.key), ...(protocols ? Array.ensure(protocols) : [])],
|
|
452
|
+
})
|
|
453
|
+
return {
|
|
454
|
+
listen: Effect.fnUntraced(function* (publish) {
|
|
455
|
+
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
|
+
}),
|
|
463
|
+
)
|
|
464
|
+
.pipe(
|
|
465
|
+
Effect.catchTag("ParseError", (cause) => publish({ _tag: "TransportFailure", cause })),
|
|
466
|
+
Effect.catchTag(
|
|
467
|
+
"SocketError",
|
|
468
|
+
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)
|
|
476
|
+
return yield* publish({ _tag: "TransportFailure", cause })
|
|
477
|
+
}
|
|
478
|
+
case "Close": {
|
|
479
|
+
const { code, closeReason } = cause
|
|
480
|
+
switch (code) {
|
|
481
|
+
case 1000: {
|
|
482
|
+
return yield* publish({ _tag: "Disconnect" })
|
|
483
|
+
}
|
|
484
|
+
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
|
+
})
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
return yield* publish({ _tag: "TransportFailure", cause })
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}),
|
|
501
|
+
),
|
|
502
|
+
)
|
|
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),
|
|
511
|
+
}
|
|
512
|
+
}),
|
|
513
|
+
replay,
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
export const layerWorker = <
|
|
517
|
+
ClientSelf,
|
|
518
|
+
ClientId extends string,
|
|
519
|
+
MethodDefinitions extends Record<string, MethodDefinition.Any>,
|
|
520
|
+
EventDefinitions extends FieldsRecord,
|
|
521
|
+
>({
|
|
522
|
+
client,
|
|
523
|
+
replay,
|
|
524
|
+
}: {
|
|
525
|
+
readonly client: Client<ClientSelf, ClientId, MethodDefinitions, EventDefinitions>
|
|
526
|
+
readonly replay?: ReplayConfig | undefined
|
|
527
|
+
}): Layer.Layer<ClientSelf, never, Worker.PlatformWorker | Worker.Spawner> =>
|
|
528
|
+
make<ClientSelf, ClientId, MethodDefinitions, EventDefinitions, Worker.PlatformWorker | Worker.Spawner>(
|
|
529
|
+
client,
|
|
530
|
+
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 })))
|
|
542
|
+
|
|
543
|
+
return {
|
|
544
|
+
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),
|
|
551
|
+
),
|
|
552
|
+
Stream.takeUntil((message) => message._tag === "Disconnect" || message._tag === "Audition.Failure"),
|
|
553
|
+
Stream.runForEach(publish),
|
|
554
|
+
)
|
|
555
|
+
}),
|
|
556
|
+
send,
|
|
557
|
+
}
|
|
558
|
+
}),
|
|
559
|
+
replay,
|
|
560
|
+
)
|
package/ClientHandle.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Schema as S, Effect, ParseResult } from "effect"
|
|
2
|
+
|
|
3
|
+
import type { Fields, FieldsRecord } from "./_types.ts"
|
|
4
|
+
import type { Send } from "./Send.ts"
|
|
5
|
+
|
|
6
|
+
const TypeId = "~liminal/ClientHandle" as const
|
|
7
|
+
|
|
8
|
+
export interface ClientHandle<ActorSelf, AttachmentFields extends Fields, EventDefinitions extends FieldsRecord> {
|
|
9
|
+
readonly [TypeId]: typeof TypeId
|
|
10
|
+
|
|
11
|
+
readonly send: Send<ActorSelf, EventDefinitions>
|
|
12
|
+
|
|
13
|
+
readonly attachments: Effect.Effect<S.Struct<AttachmentFields>["Type"]>
|
|
14
|
+
|
|
15
|
+
readonly save: (attachments: S.Struct<AttachmentFields>["Type"]) => Effect.Effect<void, ParseResult.ParseError>
|
|
16
|
+
|
|
17
|
+
readonly disconnect: Effect.Effect<void, never, ActorSelf>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const make = <ActorSelf, AttachmentFields extends Fields, EventDefinitions extends FieldsRecord>({
|
|
21
|
+
send,
|
|
22
|
+
attachments,
|
|
23
|
+
save,
|
|
24
|
+
disconnect,
|
|
25
|
+
}: {
|
|
26
|
+
readonly send: Send<ActorSelf, EventDefinitions>
|
|
27
|
+
|
|
28
|
+
readonly attachments: Effect.Effect<S.Struct<AttachmentFields>["Type"]>
|
|
29
|
+
|
|
30
|
+
readonly save: (attachments: S.Struct<AttachmentFields>["Type"]) => Effect.Effect<void, ParseResult.ParseError>
|
|
31
|
+
|
|
32
|
+
readonly disconnect: Effect.Effect<void, never, ActorSelf>
|
|
33
|
+
}): ClientHandle<ActorSelf, AttachmentFields, EventDefinitions> => ({
|
|
34
|
+
[TypeId]: TypeId,
|
|
35
|
+
send,
|
|
36
|
+
attachments,
|
|
37
|
+
save,
|
|
38
|
+
disconnect,
|
|
39
|
+
})
|
package/F.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Record, Effect, Schema as S } from "effect"
|
|
2
|
+
|
|
3
|
+
import type { ClientError } from "./errors.ts"
|
|
4
|
+
import type { MethodDefinition } from "./Method.ts"
|
|
5
|
+
|
|
6
|
+
export class UnresolvedError extends S.TaggedError<UnresolvedError>()("UnresolvedError", {}) {}
|
|
7
|
+
|
|
8
|
+
export type FError<MethodDefinitions extends Record<string, MethodDefinition.Any>> = [
|
|
9
|
+
MethodDefinitions[keyof MethodDefinitions]["failure"]["Type"] | ClientError | UnresolvedError,
|
|
10
|
+
][0]
|
|
11
|
+
|
|
12
|
+
export type F<ClientSelf, MethodDefinitions extends Record<string, MethodDefinition.Any>> = <
|
|
13
|
+
Method extends keyof MethodDefinitions,
|
|
14
|
+
>(
|
|
15
|
+
method: Method,
|
|
16
|
+
) => (
|
|
17
|
+
payload: S.Struct<MethodDefinitions[Method]["payload"]>["Type"],
|
|
18
|
+
) => Effect.Effect<MethodDefinitions[Method]["success"]["Type"], FError<MethodDefinitions>, ClientSelf>
|
package/Method.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Schema as S, Effect } from "effect"
|
|
2
|
+
|
|
3
|
+
import type { Fields } from "./_types.ts"
|
|
4
|
+
|
|
5
|
+
export interface MethodDefinition<P extends Fields, AA, AI, EA, EI> {
|
|
6
|
+
readonly payload: P
|
|
7
|
+
readonly success: S.Schema<AA, AI>
|
|
8
|
+
readonly failure: S.Schema<EA, EI>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export declare namespace MethodDefinition {
|
|
12
|
+
export type Any = MethodDefinition<Fields, any, any, any, any> | MethodDefinition<Fields, any, any, never, never>
|
|
13
|
+
|
|
14
|
+
export type Merge<T, U> = [T] extends [never]
|
|
15
|
+
? U
|
|
16
|
+
: {
|
|
17
|
+
[K in keyof T & keyof U]: T[K] extends U[K] ? (U[K] extends T[K] ? T[K] : never) : never
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const define = <const P extends Fields, AA, AI, EA, EI>({
|
|
22
|
+
payload,
|
|
23
|
+
success,
|
|
24
|
+
failure,
|
|
25
|
+
}: {
|
|
26
|
+
readonly payload: P
|
|
27
|
+
readonly success: S.Schema<AA, AI>
|
|
28
|
+
readonly failure: S.Schema<EA, EI>
|
|
29
|
+
}): MethodDefinition<P, AA, AI, EA, EI> => ({ payload, success, failure })
|
|
30
|
+
|
|
31
|
+
export type Handler<MethodDefinition extends MethodDefinition.Any, R> = (
|
|
32
|
+
payload: S.Struct<MethodDefinition["payload"]>["Type"],
|
|
33
|
+
) => Effect.Effect<MethodDefinition["success"]["Type"], MethodDefinition["failure"]["Type"], R>
|
|
34
|
+
|
|
35
|
+
export type Handlers<MethodDefinitions extends Record<string, MethodDefinition.Any>, R> = {
|
|
36
|
+
[K in keyof MethodDefinitions]: Handler<MethodDefinitions[K], R>
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const handler = <M extends MethodDefinition.Any, R>(_method: M, f: Handler<M, R>): Handler<M, R> => f
|