liminal 0.17.14 → 0.17.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Actor.ts +22 -34
- package/ActorHandle.ts +34 -0
- package/ActorNamespace.ts +188 -0
- package/ActorRuntime.ts +449 -0
- package/ActorTransport.ts +8 -6
- package/Audition.ts +87 -40
- package/BrowserActorNamespace.ts +257 -0
- package/CHANGELOG.md +17 -0
- package/Client.ts +374 -197
- package/ClientDirectory.ts +71 -49
- package/ClientHandle.ts +9 -7
- package/ClientHandleEncoders.ts +15 -0
- package/Fn.ts +94 -0
- package/Method.ts +11 -21
- package/Protocol.ts +44 -36
- package/Reducer.ts +22 -0
- package/Tracing.ts +45 -0
- package/dist/Actor.d.ts +3 -5
- package/dist/Actor.js +5 -9
- package/dist/Actor.js.map +1 -1
- package/dist/ActorHandle.d.ts +12 -0
- package/dist/ActorHandle.js +4 -0
- package/dist/ActorHandle.js.map +1 -0
- package/dist/ActorNamespace.d.ts +25 -0
- package/dist/ActorNamespace.js +60 -0
- package/dist/ActorNamespace.js.map +1 -0
- package/dist/ActorRuntime.d.ts +20 -0
- package/dist/ActorRuntime.js +210 -0
- package/dist/ActorRuntime.js.map +1 -0
- package/dist/ActorTransport.d.ts +5 -4
- package/dist/Audition.d.ts +16 -9
- package/dist/Audition.js +25 -9
- package/dist/Audition.js.map +1 -1
- package/dist/BrowserActorNamespace.d.ts +39 -0
- package/dist/BrowserActorNamespace.js +134 -0
- package/dist/BrowserActorNamespace.js.map +1 -0
- package/dist/Client.d.ts +26 -16
- package/dist/Client.js +186 -109
- package/dist/Client.js.map +1 -1
- package/dist/ClientDirectory.d.ts +15 -7
- package/dist/ClientDirectory.js +32 -23
- package/dist/ClientDirectory.js.map +1 -1
- package/dist/ClientHandle.d.ts +5 -4
- package/dist/ClientHandleEncoders.d.ts +7 -0
- package/dist/ClientHandleEncoders.js +2 -0
- package/dist/ClientHandleEncoders.js.map +1 -0
- package/dist/Fn.d.ts +24 -0
- package/dist/Fn.js +2 -0
- package/dist/Fn.js.map +1 -0
- package/dist/Method.d.ts +9 -14
- package/dist/Method.js +0 -1
- package/dist/Method.js.map +1 -1
- package/dist/Protocol.d.ts +19 -22
- package/dist/Protocol.js +20 -15
- package/dist/Protocol.js.map +1 -1
- package/dist/Reducer.d.ts +11 -0
- package/dist/Reducer.js +2 -0
- package/dist/Reducer.js.map +1 -0
- package/dist/Tracing.d.ts +37 -0
- package/dist/Tracing.js +33 -0
- package/dist/Tracing.js.map +1 -0
- package/dist/errors.d.ts +0 -4
- package/dist/errors.js.map +1 -1
- package/dist/experimental/L/append.js +1 -1
- package/dist/experimental/L/append.js.map +1 -1
- package/dist/experimental/L/history.js +1 -1
- package/dist/experimental/L/history.js.map +1 -1
- package/dist/experimental/TaggedTemplateFunction.js +1 -1
- package/dist/experimental/TaggedTemplateFunction.js.map +1 -1
- package/dist/index.common.d.ts +12 -0
- package/dist/index.common.js +13 -0
- package/dist/index.common.js.map +1 -0
- package/dist/index.d.ts +4 -11
- package/dist/index.js +4 -11
- package/dist/index.js.map +1 -1
- package/dist/index.non-workerd.d.ts +1 -0
- package/dist/index.non-workerd.js +2 -0
- package/dist/index.non-workerd.js.map +1 -0
- package/dist/package.json +20 -19
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/errors.ts +0 -6
- package/experimental/L/append.ts +1 -1
- package/experimental/L/history.ts +1 -1
- package/experimental/TaggedTemplateFunction.ts +1 -1
- package/index.common.ts +12 -0
- package/index.non-workerd.ts +1 -0
- package/index.ts +4 -11
- package/package.json +11 -23
- package/tsconfig.json +1 -1
- package/vitest.config.ts +7 -0
- package/Accumulator.ts +0 -103
- package/F.ts +0 -10
- package/_diagnostic.ts +0 -3
- package/_util/Mutex.ts +0 -13
- package/_util/schema.ts +0 -7
- package/browser/BrowserActorNamespace.ts +0 -213
- package/browser/index.ts +0 -1
- package/dist/Accumulator.d.ts +0 -22
- package/dist/Accumulator.js +0 -37
- package/dist/Accumulator.js.map +0 -1
- package/dist/F.d.ts +0 -4
- package/dist/F.js +0 -2
- package/dist/F.js.map +0 -1
- package/dist/_diagnostic.d.ts +0 -4
- package/dist/_diagnostic.js +0 -3
- package/dist/_diagnostic.js.map +0 -1
- package/dist/_util/Mutex.d.ts +0 -7
- package/dist/_util/Mutex.js +0 -9
- package/dist/_util/Mutex.js.map +0 -1
- package/dist/_util/schema.d.ts +0 -4
- package/dist/_util/schema.js +0 -5
- package/dist/_util/schema.js.map +0 -1
- package/dist/browser/BrowserActorNamespace.d.ts +0 -16
- package/dist/browser/BrowserActorNamespace.js +0 -112
- package/dist/browser/BrowserActorNamespace.js.map +0 -1
- package/dist/browser/index.d.ts +0 -1
- package/dist/browser/index.js +0 -2
- package/dist/browser/index.js.map +0 -1
- package/dist/workerd/WorkerdActorNamespace.d.ts +0 -25
- package/dist/workerd/WorkerdActorNamespace.js +0 -146
- package/dist/workerd/WorkerdActorNamespace.js.map +0 -1
- package/dist/workerd/index.d.ts +0 -1
- package/dist/workerd/index.js +0 -2
- package/dist/workerd/index.js.map +0 -1
- package/workerd/WorkerdActorNamespace.ts +0 -362
- package/workerd/index.ts +0 -1
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { BrowserWorkerRunner } from "@effect/platform-browser"
|
|
2
|
+
import { Effect, Exit, Layer, Option, Ref, Schema as S, Scope, Semaphore, Stream, Tracer } from "effect"
|
|
3
|
+
import { WorkerRunner } from "effect/unstable/workers"
|
|
4
|
+
import * as Boundary from "liminal-util/Boundary"
|
|
5
|
+
import { encodeJsonString, decodeJsonString, type TopFromString } from "liminal-util/schema"
|
|
6
|
+
|
|
7
|
+
import type { Actor } from "./Actor.ts"
|
|
8
|
+
import type { ActorTransport } from "./ActorTransport.ts"
|
|
9
|
+
import * as ClientDirectory from "./ClientDirectory.ts"
|
|
10
|
+
import type { ClientHandle } from "./ClientHandle.ts"
|
|
11
|
+
import * as Method from "./Method.ts"
|
|
12
|
+
import type { ProtocolDefinition } from "./Protocol.ts"
|
|
13
|
+
import * as Tracing from "./Tracing.ts"
|
|
14
|
+
|
|
15
|
+
export interface Introduction<Name extends TopFromString, AttachmentFields extends S.Struct.Fields> {
|
|
16
|
+
readonly port: MessagePort
|
|
17
|
+
readonly name: Name["Type"]
|
|
18
|
+
readonly attachments: S.Struct<AttachmentFields>["Type"]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const make = Effect.fnUntraced(
|
|
22
|
+
function* <
|
|
23
|
+
ActorSelf,
|
|
24
|
+
ActorId extends string,
|
|
25
|
+
Name extends TopFromString,
|
|
26
|
+
AttachmentFields extends S.Struct.Fields,
|
|
27
|
+
ClientSelf,
|
|
28
|
+
ClientId extends string,
|
|
29
|
+
D extends ProtocolDefinition,
|
|
30
|
+
const Handlers extends Method.Handlers<D["external"], any>,
|
|
31
|
+
E,
|
|
32
|
+
R,
|
|
33
|
+
IntroductionE,
|
|
34
|
+
IntroductionR,
|
|
35
|
+
>({
|
|
36
|
+
actor,
|
|
37
|
+
handlers,
|
|
38
|
+
hydrate,
|
|
39
|
+
introductions,
|
|
40
|
+
}: {
|
|
41
|
+
readonly actor: Actor<ActorSelf, ActorId, Name, AttachmentFields, ClientSelf, ClientId, D>
|
|
42
|
+
readonly handlers: Handlers
|
|
43
|
+
readonly hydrate: Effect.Effect<S.Struct<D["state"]>["Type"], E, R>
|
|
44
|
+
readonly introductions: Stream.Stream<Introduction<Name, AttachmentFields>, IntroductionE, IntroductionR>
|
|
45
|
+
}) {
|
|
46
|
+
const {
|
|
47
|
+
definition: {
|
|
48
|
+
client: { protocol: P, key: expected },
|
|
49
|
+
name: Name,
|
|
50
|
+
},
|
|
51
|
+
} = actor
|
|
52
|
+
const { Client: ClientM } = P
|
|
53
|
+
const decodeClient = decodeJsonString(ClientM)
|
|
54
|
+
const encodeAuditionSuccess = encodeJsonString(P.Audition.Success)
|
|
55
|
+
const encodeAuditionFailure = encodeJsonString(P.Audition.Failure)
|
|
56
|
+
const encodeFSuccess = encodeJsonString(P.F.Success)
|
|
57
|
+
const encodeFFailure = encodeJsonString(P.F.Failure)
|
|
58
|
+
const encodeEvent = encodeJsonString(P.Event)
|
|
59
|
+
|
|
60
|
+
interface BrowserClient {
|
|
61
|
+
readonly port: MessagePort
|
|
62
|
+
|
|
63
|
+
readonly backing: WorkerRunner.WorkerRunner<string, string>
|
|
64
|
+
|
|
65
|
+
readonly close: Effect.Effect<void>
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface Entry {
|
|
69
|
+
readonly directory: ClientDirectory.ClientDirectory<MessagePort, BrowserClient, ActorSelf, AttachmentFields, D>
|
|
70
|
+
readonly mutex: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const entries: Record<string, Entry> = {}
|
|
74
|
+
|
|
75
|
+
const transport: ActorTransport<MessagePort, BrowserClient, AttachmentFields, D> = {
|
|
76
|
+
key: ({ port }) => port,
|
|
77
|
+
send: ({ backing }, event) => {
|
|
78
|
+
const { _tag } = event.event as never
|
|
79
|
+
return Effect.gen(function* () {
|
|
80
|
+
const trace = yield* Tracing.currentTrace
|
|
81
|
+
yield* backing.send(
|
|
82
|
+
0,
|
|
83
|
+
yield* encodeEvent({ ...event, ...(trace && { trace }) }).pipe(
|
|
84
|
+
Effect.catchTags({
|
|
85
|
+
SchemaError: Effect.die,
|
|
86
|
+
}),
|
|
87
|
+
),
|
|
88
|
+
)
|
|
89
|
+
}).pipe(Boundary.span("send", import.meta.url, { attributes: { _tag }, kind: "producer" }))
|
|
90
|
+
},
|
|
91
|
+
close: ({ close }) => close,
|
|
92
|
+
snapshot: () => Effect.void,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const useEntries = yield* Semaphore.make(1).pipe(Effect.map((v) => v.withPermits(1)))
|
|
96
|
+
|
|
97
|
+
const getEntry = Effect.fnUntraced(function* (key: string) {
|
|
98
|
+
const existing = entries[key]
|
|
99
|
+
if (existing) return existing
|
|
100
|
+
const directory = ClientDirectory.make(actor, { transport })
|
|
101
|
+
const semaphore = yield* Semaphore.make(1)
|
|
102
|
+
const fresh = {
|
|
103
|
+
directory,
|
|
104
|
+
mutex: semaphore.withPermits(1),
|
|
105
|
+
}
|
|
106
|
+
entries[key] = fresh
|
|
107
|
+
return fresh
|
|
108
|
+
}, useEntries)
|
|
109
|
+
|
|
110
|
+
const outerScope = yield* Scope.Scope
|
|
111
|
+
|
|
112
|
+
yield* introductions.pipe(
|
|
113
|
+
Stream.runForEach(
|
|
114
|
+
Effect.fnUntraced(function* ({ name, port, attachments }) {
|
|
115
|
+
const stateRef = yield* Ref.make<
|
|
116
|
+
Option.Option<{
|
|
117
|
+
readonly key: string
|
|
118
|
+
readonly entry: Entry
|
|
119
|
+
readonly currentClient: ClientHandle<ActorSelf, AttachmentFields, D>
|
|
120
|
+
readonly ActorLive: Layer.Layer<ActorSelf>
|
|
121
|
+
}>
|
|
122
|
+
>(Option.none())
|
|
123
|
+
|
|
124
|
+
const scope = yield* Scope.fork(outerScope, "sequential")
|
|
125
|
+
const closeScope = Scope.close(scope, Exit.void)
|
|
126
|
+
|
|
127
|
+
const backing = yield* BrowserWorkerRunner.make(port).start<string, string>()
|
|
128
|
+
|
|
129
|
+
yield* Scope.addFinalizer(
|
|
130
|
+
scope,
|
|
131
|
+
Effect.gen(function* () {
|
|
132
|
+
const state = yield* Ref.get(stateRef)
|
|
133
|
+
if (state._tag === "Some") {
|
|
134
|
+
const {
|
|
135
|
+
key,
|
|
136
|
+
entry: { directory },
|
|
137
|
+
} = state.value
|
|
138
|
+
yield* directory.unregister(port)
|
|
139
|
+
if (directory.handles.size === 0) {
|
|
140
|
+
delete entries[key]
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}).pipe(useEntries),
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
yield* backing
|
|
147
|
+
.run(
|
|
148
|
+
Effect.fnUntraced(
|
|
149
|
+
function* (_portId, raw) {
|
|
150
|
+
const state = yield* Ref.get(stateRef)
|
|
151
|
+
const message = yield* decodeClient(raw)
|
|
152
|
+
if (state._tag === "None") {
|
|
153
|
+
if (message._tag !== "Audition.Payload") {
|
|
154
|
+
return yield* Effect.die(undefined)
|
|
155
|
+
}
|
|
156
|
+
const { client: actual } = message
|
|
157
|
+
if (actual !== expected) {
|
|
158
|
+
yield* backing.send(
|
|
159
|
+
0,
|
|
160
|
+
yield* encodeAuditionFailure({
|
|
161
|
+
_tag: "Audition.Failure",
|
|
162
|
+
expected,
|
|
163
|
+
actual,
|
|
164
|
+
}),
|
|
165
|
+
)
|
|
166
|
+
return yield* closeScope
|
|
167
|
+
}
|
|
168
|
+
const key = yield* S.encodeEffect(Name)(name)
|
|
169
|
+
const entry = yield* getEntry(key)
|
|
170
|
+
const currentClient = yield* entry.directory.register(
|
|
171
|
+
{ port, backing, close: closeScope },
|
|
172
|
+
attachments,
|
|
173
|
+
)
|
|
174
|
+
const ActorLive = Layer.succeed(actor, {
|
|
175
|
+
name,
|
|
176
|
+
clients: entry.directory.handles,
|
|
177
|
+
currentClient,
|
|
178
|
+
})
|
|
179
|
+
yield* Ref.set(stateRef, Option.some({ key, entry, currentClient, ActorLive }))
|
|
180
|
+
const initial = yield* hydrate.pipe(
|
|
181
|
+
entry.mutex,
|
|
182
|
+
Effect.scoped,
|
|
183
|
+
Boundary.span("onConnect", import.meta.url),
|
|
184
|
+
Effect.provide(ActorLive),
|
|
185
|
+
)
|
|
186
|
+
return yield* backing.send(0, yield* encodeAuditionSuccess({ _tag: "Audition.Success", initial }))
|
|
187
|
+
}
|
|
188
|
+
const { entry, ActorLive } = state.value
|
|
189
|
+
if (message._tag === "Audition.Payload") {
|
|
190
|
+
return yield* Effect.die(undefined)
|
|
191
|
+
}
|
|
192
|
+
if (message._tag === "Disconnect") {
|
|
193
|
+
return yield* closeScope
|
|
194
|
+
}
|
|
195
|
+
const { id, payload } = message
|
|
196
|
+
const { _tag, value } = payload as never
|
|
197
|
+
const parent = message.trace && Tracer.externalSpan(message.trace)
|
|
198
|
+
const transportSpan = yield* Tracing.parent
|
|
199
|
+
yield* (
|
|
200
|
+
handlers as Method.Handlers<
|
|
201
|
+
D["external"],
|
|
202
|
+
Handlers[keyof Handlers] extends (v: never) => Effect.Effect<any, any, infer R> ? R : never
|
|
203
|
+
>
|
|
204
|
+
)[_tag]!(value).pipe(
|
|
205
|
+
Effect.matchEffect({
|
|
206
|
+
onSuccess: (value) =>
|
|
207
|
+
encodeFSuccess({
|
|
208
|
+
_tag: "F.Success",
|
|
209
|
+
id,
|
|
210
|
+
success: { _tag, value } as never,
|
|
211
|
+
}),
|
|
212
|
+
onFailure: (value) =>
|
|
213
|
+
encodeFFailure({
|
|
214
|
+
_tag: "F.Failure",
|
|
215
|
+
id,
|
|
216
|
+
failure: { _tag, value } as never,
|
|
217
|
+
}),
|
|
218
|
+
}),
|
|
219
|
+
Effect.andThen((v) => backing.send(0, v)),
|
|
220
|
+
Boundary.span("handle", import.meta.url, {
|
|
221
|
+
attributes: { _tag },
|
|
222
|
+
kind: "server",
|
|
223
|
+
parent,
|
|
224
|
+
links:
|
|
225
|
+
parent && transportSpan
|
|
226
|
+
? [
|
|
227
|
+
{
|
|
228
|
+
span: transportSpan,
|
|
229
|
+
attributes: {
|
|
230
|
+
"liminal.link": "transport",
|
|
231
|
+
"liminal.transport": "worker",
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
]
|
|
235
|
+
: undefined,
|
|
236
|
+
}),
|
|
237
|
+
Effect.scoped,
|
|
238
|
+
Effect.provide(ActorLive),
|
|
239
|
+
entry.mutex,
|
|
240
|
+
)
|
|
241
|
+
},
|
|
242
|
+
Boundary.span("message", import.meta.url),
|
|
243
|
+
),
|
|
244
|
+
)
|
|
245
|
+
.pipe(
|
|
246
|
+
Effect.andThen(closeScope),
|
|
247
|
+
Effect.catchCause((cause) => Effect.logError(cause).pipe(Effect.andThen(closeScope))),
|
|
248
|
+
Boundary.span("backing", import.meta.url),
|
|
249
|
+
Effect.forkScoped,
|
|
250
|
+
Scope.provide(scope),
|
|
251
|
+
)
|
|
252
|
+
}),
|
|
253
|
+
),
|
|
254
|
+
)
|
|
255
|
+
},
|
|
256
|
+
Boundary.span("make", import.meta.url),
|
|
257
|
+
)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# liminal
|
|
2
2
|
|
|
3
|
+
## 0.17.16
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- b08b6d9: Begin trusted publishing configuration.
|
|
8
|
+
- Updated dependencies [b08b6d9]
|
|
9
|
+
- effect-workerd@0.0.6
|
|
10
|
+
- liminal-util@0.0.10
|
|
11
|
+
|
|
12
|
+
## 0.17.15
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- ab30d60: Move accumulator api directly into client and implement internal actor method bindings.
|
|
17
|
+
- Updated dependencies [ab30d60]
|
|
18
|
+
- effect-workerd@0.0.5
|
|
19
|
+
|
|
3
20
|
## 0.17.14
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|