liminal 0.17.15 → 0.17.17

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 (81) hide show
  1. package/Actor.ts +22 -33
  2. package/{workerd/ActorHandle.ts → ActorHandle.ts} +7 -2
  3. package/{workerd/WorkerdActorNamespace.ts → ActorNamespace.ts} +46 -32
  4. package/{workerd/WorkerdActorRuntime.ts → ActorRuntime.ts} +71 -44
  5. package/ActorTransport.ts +2 -2
  6. package/Audition.ts +1 -1
  7. package/BrowserActorNamespace.ts +257 -0
  8. package/CHANGELOG.md +28 -0
  9. package/Client.ts +127 -76
  10. package/ClientDirectory.ts +40 -32
  11. package/ClientHandle.ts +9 -7
  12. package/Fn.ts +39 -0
  13. package/Tracing.ts +11 -3
  14. package/dist/Actor.d.ts +3 -5
  15. package/dist/Actor.js +5 -9
  16. package/dist/Actor.js.map +1 -1
  17. package/dist/{workerd/ActorHandle.d.ts → ActorHandle.d.ts} +6 -3
  18. package/dist/ActorHandle.js.map +1 -0
  19. package/dist/{workerd/WorkerdActorNamespace.d.ts → ActorNamespace.d.ts} +12 -10
  20. package/dist/{workerd/WorkerdActorNamespace.js → ActorNamespace.js} +16 -10
  21. package/dist/ActorNamespace.js.map +1 -0
  22. package/dist/{workerd/WorkerdActorRuntime.d.ts → ActorRuntime.d.ts} +10 -9
  23. package/dist/{workerd/WorkerdActorRuntime.js → ActorRuntime.js} +40 -34
  24. package/dist/ActorRuntime.js.map +1 -0
  25. package/dist/ActorTransport.d.ts +2 -2
  26. package/dist/Audition.js +1 -1
  27. package/dist/Audition.js.map +1 -1
  28. package/dist/BrowserActorNamespace.d.ts +39 -0
  29. package/dist/BrowserActorNamespace.js +134 -0
  30. package/dist/BrowserActorNamespace.js.map +1 -0
  31. package/dist/Client.d.ts +7 -4
  32. package/dist/Client.js +78 -48
  33. package/dist/Client.js.map +1 -1
  34. package/dist/ClientDirectory.d.ts +1 -1
  35. package/dist/ClientDirectory.js +11 -5
  36. package/dist/ClientDirectory.js.map +1 -1
  37. package/dist/ClientHandle.d.ts +5 -4
  38. package/dist/Fn.d.ts +8 -0
  39. package/dist/Tracing.js +6 -2
  40. package/dist/Tracing.js.map +1 -1
  41. package/dist/experimental/L/append.js +1 -1
  42. package/dist/experimental/L/append.js.map +1 -1
  43. package/dist/experimental/L/history.js +1 -1
  44. package/dist/experimental/L/history.js.map +1 -1
  45. package/dist/index.common.d.ts +12 -0
  46. package/dist/index.common.js +13 -0
  47. package/dist/index.common.js.map +1 -0
  48. package/dist/index.d.ts +4 -11
  49. package/dist/index.js +4 -11
  50. package/dist/index.js.map +1 -1
  51. package/dist/index.non-workerd.d.ts +1 -0
  52. package/dist/index.non-workerd.js +2 -0
  53. package/dist/index.non-workerd.js.map +1 -0
  54. package/dist/package.json +13 -7
  55. package/dist/tsconfig.tsbuildinfo +1 -1
  56. package/experimental/L/append.ts +1 -1
  57. package/experimental/L/history.ts +1 -1
  58. package/index.common.ts +12 -0
  59. package/index.non-workerd.ts +1 -0
  60. package/index.ts +4 -11
  61. package/package.json +12 -9
  62. package/_util/schema.ts +0 -7
  63. package/browser/BrowserActorNamespace.ts +0 -248
  64. package/browser/index.ts +0 -1
  65. package/dist/_util/schema.d.ts +0 -4
  66. package/dist/_util/schema.js +0 -5
  67. package/dist/_util/schema.js.map +0 -1
  68. package/dist/browser/BrowserActorNamespace.d.ts +0 -16
  69. package/dist/browser/BrowserActorNamespace.js +0 -133
  70. package/dist/browser/BrowserActorNamespace.js.map +0 -1
  71. package/dist/browser/index.d.ts +0 -1
  72. package/dist/browser/index.js +0 -2
  73. package/dist/browser/index.js.map +0 -1
  74. package/dist/workerd/ActorHandle.js.map +0 -1
  75. package/dist/workerd/WorkerdActorNamespace.js.map +0 -1
  76. package/dist/workerd/WorkerdActorRuntime.js.map +0 -1
  77. package/dist/workerd/index.d.ts +0 -3
  78. package/dist/workerd/index.js +0 -4
  79. package/dist/workerd/index.js.map +0 -1
  80. package/workerd/index.ts +0 -3
  81. /package/dist/{workerd/ActorHandle.js → ActorHandle.js} +0 -0
package/Actor.ts CHANGED
@@ -1,14 +1,11 @@
1
1
  import { Context, Schema as S, Effect } from "effect"
2
- import * as Spanner from "liminal-util/Spanner"
2
+ import * as Boundary from "liminal-util/Boundary"
3
+ import type { TopFromString } from "liminal-util/schema"
3
4
 
4
- import type { TopFromString } from "./_util/schema.ts"
5
5
  import type * as ActorClient from "./Client.ts"
6
6
  import type { ClientHandle, Sender } from "./ClientHandle.ts"
7
- import * as Method from "./Method.ts"
8
7
  import { type ProtocolDefinition } from "./Protocol.ts"
9
8
 
10
- const span = Spanner.make(import.meta.url)
11
-
12
9
  export const TypeId = "~liminal/Actor" as const
13
10
 
14
11
  export interface Service<
@@ -53,14 +50,9 @@ export interface Actor<
53
50
 
54
51
  readonly definition: ActorDefinition<Name, AttachmentFields, ActorClientSelf, ActorClientId, D>
55
52
 
56
- readonly all: Sender<ActorSelf, D>
57
-
58
- readonly others: Sender<ActorSelf, D>
53
+ readonly all: Sender<D, ActorSelf>
59
54
 
60
- readonly handler: <K extends keyof D["external"], R>(
61
- tag: K,
62
- f: Method.Handler<D["external"][K], R>,
63
- ) => Method.Handler<D["external"][K], R>
55
+ readonly others: Sender<D, ActorSelf>
64
56
  }
65
57
 
66
58
  export const Service =
@@ -78,45 +70,42 @@ export const Service =
78
70
  ): Actor<ActorSelf, ActorId, Name, AttachmentFields, ClientSelf, ClientId, D> => {
79
71
  const tag = Context.Service<ActorSelf, Service<ActorSelf, Name, AttachmentFields, D>>()(id)
80
72
 
81
- const all: Sender<ActorSelf, D> = {
73
+ const all: Sender<D, ActorSelf> = {
82
74
  send: (key, payload) =>
83
- tag.asEffect().pipe(
75
+ tag.pipe(
84
76
  Effect.flatMap(({ clients }) =>
85
77
  Effect.forEach(clients, (client) => client.send(key, payload), { concurrency: "unbounded" }),
86
78
  ),
87
- span("send-all"),
79
+ Boundary.span("send-all", import.meta.url),
88
80
  ),
89
- disconnect: tag.asEffect().pipe(
81
+ disconnect: tag.pipe(
90
82
  Effect.flatMap(({ clients }) => Effect.forEach(clients, ({ disconnect }) => disconnect)),
91
- span("disconnect-all"),
83
+ Boundary.span("disconnect-all", import.meta.url),
92
84
  ),
93
85
  }
94
86
 
95
- const others: Sender<ActorSelf, D> = {
96
- send: Effect.fnUntraced(function* (key, payload) {
97
- const { clients, currentClient } = yield* tag
98
- yield* Effect.forEach(
99
- clients,
100
- (client) => (client === currentClient ? Effect.void : client.send(key, payload)),
101
- { concurrency: "unbounded" },
102
- )
103
- }, span("send-others")),
87
+ const others: Sender<D, ActorSelf> = {
88
+ send: Effect.fnUntraced(
89
+ function* (key, payload) {
90
+ const { clients, currentClient } = yield* tag
91
+ yield* Effect.forEach(
92
+ clients,
93
+ (client) => (client === currentClient ? Effect.void : client.send(key, payload)),
94
+ { concurrency: "unbounded" },
95
+ )
96
+ },
97
+ Boundary.span("send-others", import.meta.url),
98
+ ),
104
99
  disconnect: Effect.gen(function* () {
105
100
  const { clients, currentClient } = yield* tag
106
101
  yield* Effect.forEach(clients, (client) => (client === currentClient ? Effect.void : client.disconnect))
107
- }).pipe(span("disconnect-others")),
102
+ }).pipe(Boundary.span("disconnect-others", import.meta.url)),
108
103
  }
109
104
 
110
- const handler = <K extends keyof D["external"], R>(
111
- _tag: K,
112
- f: Method.Handler<D["external"][K], R>,
113
- ): Method.Handler<D["external"][K], R> => f
114
-
115
105
  return Object.assign(tag, {
116
106
  [TypeId]: TypeId,
117
107
  definition,
118
108
  all,
119
109
  others,
120
- handler,
121
110
  })
122
111
  }
@@ -1,15 +1,18 @@
1
1
  import { Schema as S, Effect, Cause, Encoding } from "effect"
2
2
  import { NativeRequest } from "effect-workerd"
3
3
  import { HttpServerResponse } from "effect/unstable/http"
4
+ import type { TopFromString } from "liminal-util/schema"
4
5
 
5
- import type { TopFromString } from "../_util/schema.ts"
6
- import type { Methods } from "../Method.ts"
6
+ import type { Send } from "./ClientHandle.ts"
7
+ import type { Methods } from "./Method.ts"
8
+ import type { ProtocolDefinition } from "./Protocol.ts"
7
9
 
8
10
  export interface ActorHandle<
9
11
  NamespaceSelf,
10
12
  Internal extends Methods,
11
13
  Name extends TopFromString,
12
14
  AttachmentFields extends S.Struct.Fields,
15
+ D extends ProtocolDefinition,
13
16
  > {
14
17
  readonly upgrade: (
15
18
  attachments: S.Struct<AttachmentFields>["Type"],
@@ -26,4 +29,6 @@ export interface ActorHandle<
26
29
  method: K,
27
30
  payload: M["payload"]["Type"],
28
31
  ) => Effect.Effect<M["success"]["Type"], M["failure"]["Type"], NamespaceSelf>
32
+
33
+ readonly proxySendAll: Send<D, NamespaceSelf>
29
34
  }
@@ -1,16 +1,14 @@
1
1
  import { Layer, Effect, Schema as S, Context, flow, String, Array, Encoding, Exit } from "effect"
2
- import { Binding, NativeRequest } from "effect-workerd"
2
+ import { Binding, Env, NativeRequest } from "effect-workerd"
3
3
  import { SecWebSocketProtocol, close } from "effect-workerd/socket_util"
4
4
  import { HttpServerResponse, HttpTraceContext } from "effect/unstable/http"
5
- import * as Spanner from "liminal-util/Spanner"
5
+ import * as Boundary from "liminal-util/Boundary"
6
+ import { type TopFromString, encodeJsonString } from "liminal-util/schema"
6
7
 
7
- import { type TopFromString, encodeJsonString } from "../_util/schema.ts"
8
- import type { Actor } from "../Actor.ts"
9
- import type { Methods } from "../Method.ts"
10
- import type { ProtocolDefinition } from "../Protocol.ts"
8
+ import type { Actor } from "./Actor.ts"
11
9
  import type { ActorHandle } from "./ActorHandle.ts"
12
-
13
- const span = Spanner.make(import.meta.url)
10
+ import type { Methods } from "./Method.ts"
11
+ import type { ProtocolDefinition } from "./Protocol.ts"
14
12
 
15
13
  export interface ActorNamespaceDefinition<
16
14
  Internal extends Methods,
@@ -45,7 +43,7 @@ export interface ActorNamespace<
45
43
  _: never,
46
44
  ): Context.ServiceClass.Shape<
47
45
  NamespaceId,
48
- DurableObjectNamespace<Rpc.DurableObjectBranded & WorkerdActorNamespace.MakeRpc<Internal>>
46
+ DurableObjectNamespace<Rpc.DurableObjectBranded & ActorNamespace.MakeRpc<Internal, D>>
49
47
  >
50
48
 
51
49
  readonly definition: ActorNamespaceDefinition<
@@ -59,17 +57,19 @@ export interface ActorNamespace<
59
57
  D
60
58
  >
61
59
 
62
- readonly bind: (name: Name["Type"]) => ActorHandle<NamespaceSelf, Internal, Name, AttachmentFields>
60
+ readonly bind: (name: Name["Type"]) => ActorHandle<NamespaceSelf, Internal, Name, AttachmentFields, D>
63
61
 
64
- readonly layer: Layer.Layer<NamespaceSelf, S.SchemaError, never>
62
+ readonly layer: Layer.Layer<NamespaceSelf, S.SchemaError, Env>
65
63
  }
66
64
 
67
- export declare namespace WorkerdActorNamespace {
68
- export type MakeRpc<External extends Methods> = {
69
- rpc: <K extends keyof External>(
65
+ export declare namespace ActorNamespace {
66
+ export type MakeRpc<Internal extends Methods, D extends ProtocolDefinition> = {
67
+ rpc: <K extends keyof Internal>(
70
68
  method: K,
71
- payload: External[K]["payload"]["Type"],
72
- ) => Promise<Exit.Exit<External[K]["success"]["Type"], External[K]["failure"]["Type"]>>
69
+ payload: Internal[K]["payload"]["Type"],
70
+ ) => Promise<Exit.Exit<Internal[K]["success"]["Type"], Internal[K]["failure"]["Type"]>>
71
+
72
+ proxySendAll<K extends keyof D["events"]>(event: K, payload: S.Struct<D["events"][K]>["Type"]): void
73
73
  }
74
74
  }
75
75
 
@@ -111,7 +111,7 @@ export const Service =
111
111
 
112
112
  const tag = Context.Service<
113
113
  NamespaceSelf,
114
- DurableObjectNamespace<Rpc.DurableObjectBranded & WorkerdActorNamespace.MakeRpc<Internal>>
114
+ DurableObjectNamespace<Rpc.DurableObjectBranded & ActorNamespace.MakeRpc<Internal, D>>
115
115
  >()(id)
116
116
 
117
117
  const encodeName = S.encodeEffect(Name)
@@ -119,7 +119,7 @@ export const Service =
119
119
  const encodeAttachmentsString = encodeJsonString(Attachments)
120
120
  const encodeAuditionFailure = encodeJsonString(P.Audition.Failure)
121
121
 
122
- const bind = (name: Name["Type"]): ActorHandle<NamespaceSelf, Internal, Name, AttachmentFields> => {
122
+ const bind = (name: Name["Type"]): ActorHandle<NamespaceSelf, Internal, Name, AttachmentFields, D> => {
123
123
  const getStub = Effect.gen(function* () {
124
124
  const namespace = yield* tag
125
125
  const nameEncoded = yield* encodeName(name)
@@ -132,9 +132,10 @@ export const Service =
132
132
  const protocols = yield* Effect.fromNullishOr(request.headers.get(SecWebSocketProtocol)).pipe(
133
133
  Effect.map(flow(String.split(","), Array.map(String.trim))),
134
134
  )
135
- const liminalTokenI = yield* Array.findFirstIndex(protocols, (v) => v === "liminal")
136
- const requestClientId = yield* Effect.fromNullishOr(protocols[liminalTokenI + 1]).pipe(
137
- Effect.flatMap((v) => Encoding.decodeBase64UrlString(v).asEffect()),
135
+ const liminalTokenI = yield* Array.findFirstIndex(protocols, (v) => v === "liminal").pipe(Effect.fromOption)
136
+ const liminalClientId = yield* Effect.fromNullishOr(protocols[liminalTokenI + 1])
137
+ const requestClientId = yield* Effect.fromNullishOr(protocols[liminalTokenI + 2]).pipe(
138
+ Effect.flatMap((v) => Encoding.decodeBase64UrlString(v).pipe(Effect.fromResult)),
138
139
  )
139
140
  if (requestClientId !== clientId) {
140
141
  return close(
@@ -147,6 +148,7 @@ export const Service =
147
148
  }
148
149
  const url = new URL(request.url)
149
150
  url.searchParams.set("__liminal_attachments", yield* encodeAttachmentsString(attachments))
151
+ url.searchParams.set("__liminal_client_id", liminalClientId)
150
152
  const actorRequest = new Request(url, request)
151
153
  const traceHeaders = yield* Effect.currentSpan.pipe(Effect.map(HttpTraceContext.toHeaders))
152
154
  for (const [key, value] of Object.entries(traceHeaders)) {
@@ -154,18 +156,30 @@ export const Service =
154
156
  }
155
157
  const stub = yield* getStub
156
158
  return yield* Effect.promise(() => stub.fetch(actorRequest)).pipe(Effect.map(HttpServerResponse.raw))
157
- }).pipe(span("upgrade", { kind: "client" }))
158
-
159
- const call = Effect.fnUntraced(function* <K extends keyof Internal, M extends Internal[K]>(
160
- method: K,
161
- payload: M["payload"]["Type"],
162
- ): Effect.fn.Return<M["success"]["Type"], M["failure"]["Type"], NamespaceSelf> {
163
- const stub = yield* getStub
164
- const exit = yield* Effect.promise(() => stub.rpc(method as never, payload as never))
165
- return yield* exit as any
166
- })
159
+ }).pipe(Boundary.span("upgrade", import.meta.url, { kind: "client" }))
160
+
161
+ const call = Effect.fnUntraced(
162
+ function* <K extends keyof Internal, M extends Internal[K]>(
163
+ method: K,
164
+ payload: M["payload"]["Type"],
165
+ ): Effect.fn.Return<M["success"]["Type"], M["failure"]["Type"], NamespaceSelf> {
166
+ const stub = yield* getStub
167
+ const exit = yield* Effect.promise(() => stub.rpc(method as never, payload as never))
168
+ return yield* exit as any
169
+ },
170
+ Boundary.span("call", import.meta.url),
171
+ )
172
+
173
+ // TODO:
174
+ const proxySendAll = Effect.fnUntraced(
175
+ function* <K extends keyof D["events"]>(event: K, payload: S.Struct<D["events"][K]>["Type"]) {
176
+ const stub = yield* getStub
177
+ yield* Effect.promise(() => stub.proxySendAll(event as never, payload as never))
178
+ },
179
+ Boundary.span("proxySendAll", import.meta.url),
180
+ ) as never
167
181
 
168
- return { upgrade, call }
182
+ return { upgrade, call, proxySendAll }
169
183
  }
170
184
 
171
185
  const layer = Binding.layer(tag, ["idFromName", "idFromString", "newUniqueId", "get", "getByName"])(binding)
@@ -15,24 +15,21 @@ import {
15
15
  Exit,
16
16
  } from "effect"
17
17
  import { DoState } from "effect-workerd"
18
+ import { Env } from "effect-workerd"
18
19
  import { Clock } from "effect-workerd/platform"
19
20
  import { SecWebSocketProtocol } from "effect-workerd/socket_util"
20
21
  import { Headers, FetchHttpClient, HttpClient, HttpTraceContext } from "effect/unstable/http"
21
- import { boundLayer } from "liminal-util/boundLayer"
22
- import { logCause } from "liminal-util/logCause"
23
- import * as Spanner from "liminal-util/Spanner"
22
+ import * as Boundary from "liminal-util/Boundary"
23
+ import { type TopFromString, encodeJsonString, decodeJsonString } from "liminal-util/schema"
24
24
 
25
- import { type TopFromString, encodeJsonString, decodeJsonString } from "../_util/schema.ts"
26
- import type { ActorTransport } from "../ActorTransport.ts"
27
- import * as ClientDirectory from "../ClientDirectory.ts"
28
- import type { ClientHandle } from "../ClientHandle.ts"
29
- import type { Handlers, Methods } from "../Method.ts"
30
- import type { ProtocolDefinition } from "../Protocol.ts"
31
- import * as Tracing from "../Tracing.ts"
32
- import { sessionAttributes, SessionId, sessionLink } from "../Tracing.ts"
33
- import type { ActorNamespace } from "./WorkerdActorNamespace.ts"
34
-
35
- const span = Spanner.make(import.meta.url)
25
+ import type { ActorNamespace } from "./ActorNamespace.ts"
26
+ import type { ActorTransport } from "./ActorTransport.ts"
27
+ import * as ClientDirectory from "./ClientDirectory.ts"
28
+ import type { ClientHandle } from "./ClientHandle.ts"
29
+ import type { Handlers, Methods } from "./Method.ts"
30
+ import type { ProtocolDefinition } from "./Protocol.ts"
31
+ import * as Tracing from "./Tracing.ts"
32
+ import { sessionAttributes, SessionId, sessionLink } from "./Tracing.ts"
36
33
 
37
34
  export interface ActorRuntimeDefinition<
38
35
  NamespaceSelf,
@@ -76,25 +73,28 @@ export interface ActorRuntimeDefinition<
76
73
  | Name["EncodingServices"]
77
74
  | Name["DecodingServices"],
78
75
  PreludeE,
79
- HttpClient.HttpClient
76
+ HttpClient.HttpClient | Env
80
77
  >
81
78
 
82
- readonly layer: Layer.Layer<RunROut, RunE, ActorSelf | HttpClient.HttpClient | PreludeROut>
79
+ readonly layer: Layer.Layer<RunROut, RunE, ActorSelf | HttpClient.HttpClient | Env | PreludeROut>
83
80
 
84
- readonly external: Handlers<D["external"], ActorSelf | HttpClient.HttpClient | PreludeROut | RunROut | Scope.Scope>
81
+ readonly external: Handlers<
82
+ D["external"],
83
+ ActorSelf | HttpClient.HttpClient | Env | PreludeROut | RunROut | Scope.Scope
84
+ >
85
85
 
86
86
  readonly internal: Handlers<Internal, ActorSelf | HttpClient.HttpClient | PreludeROut | RunROut | Scope.Scope>
87
87
 
88
88
  readonly hydrate: Effect.Effect<
89
89
  S.Struct<D["state"]>["Type"],
90
90
  never,
91
- ActorSelf | HttpClient.HttpClient | PreludeROut | RunROut | Scope.Scope
91
+ ActorSelf | HttpClient.HttpClient | Env | PreludeROut | RunROut | Scope.Scope
92
92
  >
93
93
 
94
94
  readonly onDisconnect: Effect.Effect<
95
95
  void,
96
96
  never,
97
- ActorSelf | HttpClient.HttpClient | PreludeROut | RunROut | Scope.Scope
97
+ ActorSelf | HttpClient.HttpClient | Env | PreludeROut | RunROut | Scope.Scope
98
98
  >
99
99
 
100
100
  readonly hibernation?: Duration.Input | undefined
@@ -181,17 +181,18 @@ export const make = <
181
181
  const { _tag } = event.event as never
182
182
  return Effect.gen(function* () {
183
183
  const trace = yield* Tracing.currentTrace
184
- const encoded = yield* encodeEvent({
185
- ...event,
186
- ...(trace && { trace }),
187
- })
184
+ const encoded = yield* encodeEvent({ ...event, ...(trace && { trace }) }).pipe(
185
+ Effect.catchTags({
186
+ SchemaError: Effect.die,
187
+ }),
188
+ )
188
189
  // @effect-diagnostics-next-line tryCatchInEffectGen:off
189
190
  try {
190
191
  socket.send(encoded)
191
192
  // oxlint-disable-next-line no-unused-vars
192
193
  } catch (_e) {}
193
194
  }).pipe(
194
- span("send", {
195
+ Boundary.span("send", import.meta.url, {
195
196
  attributes: { _tag, ...sessionAttributes(session) },
196
197
  kind: "producer",
197
198
  links: [sessionLink(session)],
@@ -201,11 +202,14 @@ export const make = <
201
202
  close: ({ socket }) => Effect.sync(() => socket.close(1000)),
202
203
  snapshot: ({ socket, session }, attachments) =>
203
204
  encodeSocketAttachment({ attachments, session }).pipe(
205
+ Effect.catchTags({
206
+ SchemaError: Effect.die,
207
+ }),
204
208
  Effect.andThen((v) => Effect.sync(() => socket.serializeAttachment(v))),
205
209
  ),
206
210
  }
207
211
 
208
- class NameDecoded extends Context.Service<NameDecoded, Name["Type"]>()("liminal/WorkerdActorNamespace/NameDecoded") {}
212
+ class NameDecoded extends Context.Service<NameDecoded, Name["Type"]>()("liminal/ActorNamespace/NameDecoded") {}
209
213
 
210
214
  return class extends DurableObject {
211
215
  readonly run
@@ -239,12 +243,17 @@ export const make = <
239
243
  const Live = Layer.mergeAll(
240
244
  FetchHttpClient.layer,
241
245
  Layer.succeed(DoState.DoState, state),
246
+ Layer.succeed(Env, env as never),
242
247
  Layer.effect(NameDecoded, S.decodeUnknownEffect(Name)(state.id.name)),
243
248
  ).pipe(
244
249
  Layer.provideMerge(
245
250
  prelude.pipe(
246
251
  Layer.provideMerge(
247
- Layer.mergeAll(FetchHttpClient.layer, ConfigProvider.layer(ConfigProvider.fromUnknown(env))),
252
+ Layer.mergeAll(
253
+ FetchHttpClient.layer,
254
+ ConfigProvider.layer(ConfigProvider.fromUnknown(env)),
255
+ Layer.succeed(Env, env as never),
256
+ ),
248
257
  ),
249
258
  ),
250
259
  ),
@@ -258,28 +267,32 @@ export const make = <
258
267
  .register({ socket, session }, attachments)
259
268
  .pipe(Effect.linkSpans(Tracer.externalSpan(session.trace), sessionLink(session).attributes))
260
269
  }
261
- }).pipe(span("hydrate"), Layer.effectDiscard)
262
-
263
- const runtime = ManagedRuntime.make(HydrateClientsLive.pipe(Layer.provideMerge(Live), boundLayer("actor")))
270
+ }).pipe(Boundary.span("hydrate", import.meta.url), Layer.effectDiscard)
264
271
 
265
- this.run = flow(Effect.tapCause(logCause), runtime.runPromise)
272
+ const runtime = ManagedRuntime.make(
273
+ HydrateClientsLive.pipe(Layer.provideMerge(Live), Boundary.layer("actor", import.meta.url)),
274
+ )
275
+ this.run = <A, E, R extends ManagedRuntime.ManagedRuntime.Services<typeof runtime>>(
276
+ effect: Effect.Effect<A, E, R>,
277
+ ) => Effect.onError(effect, Effect.logError).pipe(runtime.runPromise)
266
278
  }
267
279
 
268
280
  override fetch(request: Request): Promise<Response> {
269
281
  return Effect.gen({ self: this }, function* () {
270
282
  const url = new URL(request.url)
271
283
  const attachments = yield* decodeAttachmentsString(url.searchParams.get("__liminal_attachments"))
284
+ const clientId = yield* Effect.fromNullishOr(url.searchParams.get("__liminal_client_id"))
272
285
  const { 0: webSocket, 1: server } = new WebSocketPair()
273
286
  const state = yield* DoState.DoState
274
287
  const session = {
275
- id: SessionId.make(crypto.randomUUID()),
288
+ id: SessionId.make(clientId),
276
289
  trace: yield* Effect.currentSpan.pipe(Effect.map(Tracing.toTraceEnvelope)),
277
290
  }
278
291
  const currentClient = yield* this.directory.register({ socket: server, session }, attachments)
279
292
  state.acceptWebSocket(server)
280
293
  const initial = yield* hydrate.pipe(
281
294
  this.provideActor(currentClient),
282
- span("hydrate", {
295
+ Boundary.span("hydrate", import.meta.url, {
283
296
  attributes: sessionAttributes(session),
284
297
  links: [sessionLink(session)],
285
298
  }),
@@ -291,7 +304,7 @@ export const make = <
291
304
  headers: { [SecWebSocketProtocol]: "liminal" },
292
305
  })
293
306
  }).pipe(
294
- span("fetch", {
307
+ Boundary.span("fetch", import.meta.url, {
295
308
  kind: "server",
296
309
  parent: pipe(request.headers, Headers.fromInput, HttpTraceContext.fromHeaders, Option.getOrUndefined),
297
310
  }),
@@ -312,7 +325,7 @@ export const make = <
312
325
  yield* currentClient.disconnect
313
326
  return yield* onDisconnect.pipe(
314
327
  this.provideActor(currentClient),
315
- span("disconnect", {
328
+ Boundary.span("disconnect", import.meta.url, {
316
329
  attributes: sessionAttributes(session),
317
330
  links: [sessionLink(session)],
318
331
  }),
@@ -358,21 +371,23 @@ export const make = <
358
371
  }),
359
372
  ),
360
373
  this.provideActor(currentClient),
361
- span("handler", {
374
+ Boundary.span("handler", import.meta.url, {
362
375
  attributes: { _tag, ...sessionAttributes(session) },
363
376
  kind: "server",
364
377
  parent,
365
378
  links,
366
379
  }),
367
380
  )
368
- }).pipe(span("socket-message"), this.run)
381
+ }).pipe(Boundary.span("socket-message", import.meta.url), this.run)
369
382
  }
370
383
 
371
384
  override webSocketClose(socket: WebSocket, _code: number, _reason: string, _wasClean: boolean) {
372
385
  Effect.gen({ self: this }, function* () {
373
- const entry = yield* this.directory
374
- .entry(socket)
375
- .pipe(Effect.catchTag("NoSuchElementError", () => Effect.undefined))
386
+ const entry = yield* this.directory.entry(socket).pipe(
387
+ Effect.catchTags({
388
+ NoSuchElementError: () => Effect.undefined,
389
+ }),
390
+ )
376
391
  if (!entry) {
377
392
  return
378
393
  }
@@ -384,12 +399,12 @@ export const make = <
384
399
  yield* this.directory.unregister(socket)
385
400
  yield* onDisconnect.pipe(
386
401
  this.provideActor(currentClient),
387
- span("disconnect", {
402
+ Boundary.span("disconnect", import.meta.url, {
388
403
  attributes: sessionAttributes(session),
389
404
  links: [sessionLink(session)],
390
405
  }),
391
406
  )
392
- }).pipe(span("socket-close"), this.run)
407
+ }).pipe(Boundary.span("socket-close", import.meta.url), this.run)
393
408
  }
394
409
 
395
410
  override webSocketError(socket: WebSocket, cause: unknown) {
@@ -402,13 +417,13 @@ export const make = <
402
417
  yield* this.directory.unregister(socket)
403
418
  yield* onDisconnect.pipe(
404
419
  this.provideActor(currentClient),
405
- span("disconnect", {
420
+ Boundary.span("disconnect", import.meta.url, {
406
421
  attributes: sessionAttributes(session),
407
422
  links: [sessionLink(session)],
408
423
  }),
409
424
  )
410
425
  yield* Effect.annotateLogs(Effect.logDebug("SocketErrored"), { cause })
411
- }).pipe(span("socket-error"), this.run)
426
+ }).pipe(Boundary.span("socket-error", import.meta.url), this.run)
412
427
  }
413
428
 
414
429
  async rpc<K extends keyof Internal>(
@@ -416,7 +431,19 @@ export const make = <
416
431
  payload: Internal[K]["payload"]["Type"],
417
432
  ): Promise<Exit.Exit<Internal[K]["success"]["Type"], Internal[K]["failure"]["Type"]>> {
418
433
  const handler = internal[method]
419
- return await handler(payload).pipe(this.provideActor(null!), span("fn-internal"), Effect.exit, this.run)
434
+ return await handler(payload).pipe(
435
+ this.provideActor(null!),
436
+ Boundary.span("fn-internal", import.meta.url),
437
+ Effect.exit,
438
+ this.run,
439
+ )
440
+ }
441
+
442
+ async proxySendAll<K extends keyof D["events"]>(event: K, payload: S.Struct<D["events"][K]>["Type"]) {
443
+ await Effect.gen(function* () {
444
+ const { clients } = yield* actor
445
+ yield* Effect.forEach(clients, ({ send }) => send(event, payload), { concurrency: "unbounded" })
446
+ }).pipe(this.provideActor(null!), Boundary.span("fn-internal", import.meta.url), this.run)
420
447
  }
421
448
  }
422
449
  }
package/ActorTransport.ts CHANGED
@@ -8,12 +8,12 @@ export interface ActorTransport<Key, Client, AttachmentFields extends S.Struct.F
8
8
  readonly send: (
9
9
  client: Client,
10
10
  event: Protocol<D>["Event"]["Type"],
11
- ) => Effect.Effect<void, S.SchemaError, Protocol<D>["Event"]["EncodingServices"]>
11
+ ) => Effect.Effect<void, never, Protocol<D>["Event"]["EncodingServices"]>
12
12
 
13
13
  readonly close: (client: Client) => Effect.Effect<void>
14
14
 
15
15
  readonly snapshot: (
16
16
  client: Client,
17
17
  attachments: S.Struct<AttachmentFields>["Type"],
18
- ) => Effect.Effect<void, S.SchemaError, S.Struct<AttachmentFields>["EncodingServices"]>
18
+ ) => Effect.Effect<void, never, S.Struct<AttachmentFields>["EncodingServices"]>
19
19
  }
package/Audition.ts CHANGED
@@ -35,7 +35,7 @@ export const empty: Audition<never, never, {}, never> = {
35
35
  return Pipeable.pipeArguments(this, arguments)
36
36
  },
37
37
  state: Stream.fail(new AuditionError()),
38
- fn: () => () => new AuditionError().asEffect(),
38
+ fn: () => () => new AuditionError(),
39
39
  events: Stream.fail(new AuditionError()),
40
40
  }
41
41