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.
Files changed (126) hide show
  1. package/Actor.ts +22 -34
  2. package/ActorHandle.ts +34 -0
  3. package/ActorNamespace.ts +188 -0
  4. package/ActorRuntime.ts +449 -0
  5. package/ActorTransport.ts +8 -6
  6. package/Audition.ts +87 -40
  7. package/BrowserActorNamespace.ts +257 -0
  8. package/CHANGELOG.md +17 -0
  9. package/Client.ts +374 -197
  10. package/ClientDirectory.ts +71 -49
  11. package/ClientHandle.ts +9 -7
  12. package/ClientHandleEncoders.ts +15 -0
  13. package/Fn.ts +94 -0
  14. package/Method.ts +11 -21
  15. package/Protocol.ts +44 -36
  16. package/Reducer.ts +22 -0
  17. package/Tracing.ts +45 -0
  18. package/dist/Actor.d.ts +3 -5
  19. package/dist/Actor.js +5 -9
  20. package/dist/Actor.js.map +1 -1
  21. package/dist/ActorHandle.d.ts +12 -0
  22. package/dist/ActorHandle.js +4 -0
  23. package/dist/ActorHandle.js.map +1 -0
  24. package/dist/ActorNamespace.d.ts +25 -0
  25. package/dist/ActorNamespace.js +60 -0
  26. package/dist/ActorNamespace.js.map +1 -0
  27. package/dist/ActorRuntime.d.ts +20 -0
  28. package/dist/ActorRuntime.js +210 -0
  29. package/dist/ActorRuntime.js.map +1 -0
  30. package/dist/ActorTransport.d.ts +5 -4
  31. package/dist/Audition.d.ts +16 -9
  32. package/dist/Audition.js +25 -9
  33. package/dist/Audition.js.map +1 -1
  34. package/dist/BrowserActorNamespace.d.ts +39 -0
  35. package/dist/BrowserActorNamespace.js +134 -0
  36. package/dist/BrowserActorNamespace.js.map +1 -0
  37. package/dist/Client.d.ts +26 -16
  38. package/dist/Client.js +186 -109
  39. package/dist/Client.js.map +1 -1
  40. package/dist/ClientDirectory.d.ts +15 -7
  41. package/dist/ClientDirectory.js +32 -23
  42. package/dist/ClientDirectory.js.map +1 -1
  43. package/dist/ClientHandle.d.ts +5 -4
  44. package/dist/ClientHandleEncoders.d.ts +7 -0
  45. package/dist/ClientHandleEncoders.js +2 -0
  46. package/dist/ClientHandleEncoders.js.map +1 -0
  47. package/dist/Fn.d.ts +24 -0
  48. package/dist/Fn.js +2 -0
  49. package/dist/Fn.js.map +1 -0
  50. package/dist/Method.d.ts +9 -14
  51. package/dist/Method.js +0 -1
  52. package/dist/Method.js.map +1 -1
  53. package/dist/Protocol.d.ts +19 -22
  54. package/dist/Protocol.js +20 -15
  55. package/dist/Protocol.js.map +1 -1
  56. package/dist/Reducer.d.ts +11 -0
  57. package/dist/Reducer.js +2 -0
  58. package/dist/Reducer.js.map +1 -0
  59. package/dist/Tracing.d.ts +37 -0
  60. package/dist/Tracing.js +33 -0
  61. package/dist/Tracing.js.map +1 -0
  62. package/dist/errors.d.ts +0 -4
  63. package/dist/errors.js.map +1 -1
  64. package/dist/experimental/L/append.js +1 -1
  65. package/dist/experimental/L/append.js.map +1 -1
  66. package/dist/experimental/L/history.js +1 -1
  67. package/dist/experimental/L/history.js.map +1 -1
  68. package/dist/experimental/TaggedTemplateFunction.js +1 -1
  69. package/dist/experimental/TaggedTemplateFunction.js.map +1 -1
  70. package/dist/index.common.d.ts +12 -0
  71. package/dist/index.common.js +13 -0
  72. package/dist/index.common.js.map +1 -0
  73. package/dist/index.d.ts +4 -11
  74. package/dist/index.js +4 -11
  75. package/dist/index.js.map +1 -1
  76. package/dist/index.non-workerd.d.ts +1 -0
  77. package/dist/index.non-workerd.js +2 -0
  78. package/dist/index.non-workerd.js.map +1 -0
  79. package/dist/package.json +20 -19
  80. package/dist/tsconfig.tsbuildinfo +1 -1
  81. package/errors.ts +0 -6
  82. package/experimental/L/append.ts +1 -1
  83. package/experimental/L/history.ts +1 -1
  84. package/experimental/TaggedTemplateFunction.ts +1 -1
  85. package/index.common.ts +12 -0
  86. package/index.non-workerd.ts +1 -0
  87. package/index.ts +4 -11
  88. package/package.json +11 -23
  89. package/tsconfig.json +1 -1
  90. package/vitest.config.ts +7 -0
  91. package/Accumulator.ts +0 -103
  92. package/F.ts +0 -10
  93. package/_diagnostic.ts +0 -3
  94. package/_util/Mutex.ts +0 -13
  95. package/_util/schema.ts +0 -7
  96. package/browser/BrowserActorNamespace.ts +0 -213
  97. package/browser/index.ts +0 -1
  98. package/dist/Accumulator.d.ts +0 -22
  99. package/dist/Accumulator.js +0 -37
  100. package/dist/Accumulator.js.map +0 -1
  101. package/dist/F.d.ts +0 -4
  102. package/dist/F.js +0 -2
  103. package/dist/F.js.map +0 -1
  104. package/dist/_diagnostic.d.ts +0 -4
  105. package/dist/_diagnostic.js +0 -3
  106. package/dist/_diagnostic.js.map +0 -1
  107. package/dist/_util/Mutex.d.ts +0 -7
  108. package/dist/_util/Mutex.js +0 -9
  109. package/dist/_util/Mutex.js.map +0 -1
  110. package/dist/_util/schema.d.ts +0 -4
  111. package/dist/_util/schema.js +0 -5
  112. package/dist/_util/schema.js.map +0 -1
  113. package/dist/browser/BrowserActorNamespace.d.ts +0 -16
  114. package/dist/browser/BrowserActorNamespace.js +0 -112
  115. package/dist/browser/BrowserActorNamespace.js.map +0 -1
  116. package/dist/browser/index.d.ts +0 -1
  117. package/dist/browser/index.js +0 -2
  118. package/dist/browser/index.js.map +0 -1
  119. package/dist/workerd/WorkerdActorNamespace.d.ts +0 -25
  120. package/dist/workerd/WorkerdActorNamespace.js +0 -146
  121. package/dist/workerd/WorkerdActorNamespace.js.map +0 -1
  122. package/dist/workerd/index.d.ts +0 -1
  123. package/dist/workerd/index.js +0 -2
  124. package/dist/workerd/index.js.map +0 -1
  125. package/workerd/WorkerdActorNamespace.ts +0 -362
  126. package/workerd/index.ts +0 -1
@@ -1,362 +0,0 @@
1
- import { DurableObject } from "cloudflare:workers"
2
- import {
3
- Layer,
4
- Effect,
5
- Scope,
6
- Schema as S,
7
- Context,
8
- ManagedRuntime,
9
- ConfigProvider,
10
- Duration,
11
- flow,
12
- String,
13
- Array,
14
- Encoding,
15
- Option,
16
- Cause,
17
- } from "effect"
18
- import { Binding, DoState, NativeRequest } from "effect-workerd"
19
- import { SecWebSocketProtocol, close } from "effect-workerd/socket_util"
20
- import { HttpServerResponse, HttpClient, FetchHttpClient } from "effect/unstable/http"
21
- import { boundLayer } from "liminal-util/boundLayer"
22
- import { logCause } from "liminal-util/logCause"
23
-
24
- import type { Actor } from "../Actor.ts"
25
- import type { ActorTransport } from "../ActorTransport.ts"
26
- import type { ProtocolDefinition } from "../Protocol.ts"
27
-
28
- import { diagnostic } from "../_diagnostic.ts"
29
- import * as Mutex from "../_util/Mutex.ts"
30
- import { type TopFromString, encodeJsonString, decodeJsonString } from "../_util/schema.ts"
31
- import * as ClientDirectory from "../ClientDirectory.ts"
32
- import * as Method from "../Method.ts"
33
-
34
- const { debug, span } = diagnostic("workerd.WorkerdActorNamespace")
35
-
36
- export interface ActorNamespaceDefinition<
37
- ActorSelf,
38
- ActorId extends string,
39
- Name extends TopFromString,
40
- AttachmentFields extends S.Struct.Fields,
41
- ClientSelf,
42
- ClientId extends string,
43
- D extends ProtocolDefinition,
44
- PreludeROut,
45
- PreludeE,
46
- RunROut,
47
- RunE,
48
- > {
49
- readonly ""?: this["actor"]["definition"]["client"]["protocol"]
50
-
51
- readonly actor: Actor<ActorSelf, ActorId, Name, AttachmentFields, ClientSelf, ClientId, D>
52
-
53
- readonly prelude: Layer.Layer<
54
- | PreludeROut
55
- | NonNullable<this[""]>["F"]["Payload"]["DecodingServices"]
56
- | NonNullable<this[""]>["F"]["Success"]["EncodingServices"]
57
- | NonNullable<this[""]>["F"]["Failure"]["EncodingServices"]
58
- | NonNullable<this[""]>["Event"]["EncodingServices"]
59
- | S.Struct<AttachmentFields>["DecodingServices"]
60
- | S.Struct<AttachmentFields>["EncodingServices"]
61
- | Name["EncodingServices"]
62
- | Name["DecodingServices"],
63
- PreludeE
64
- >
65
-
66
- readonly layer: Layer.Layer<RunROut, RunE, ActorSelf | HttpClient.HttpClient | PreludeROut>
67
-
68
- readonly handlers: Method.Handlers<
69
- D["methods"],
70
- ActorSelf | HttpClient.HttpClient | PreludeROut | RunROut | Scope.Scope
71
- >
72
-
73
- readonly onConnect: Effect.Effect<
74
- void,
75
- never,
76
- ActorSelf | HttpClient.HttpClient | PreludeROut | RunROut | Scope.Scope
77
- >
78
-
79
- readonly hibernation?: Duration.Input | undefined
80
- }
81
-
82
- export interface ActorNamespace<
83
- NamespaceSelf,
84
- NamespaceId extends string,
85
- ActorSelf,
86
- ActorId extends string,
87
- Name extends TopFromString,
88
- AttachmentFields extends S.Struct.Fields,
89
- ClientSelf,
90
- ClientId extends string,
91
- D extends ProtocolDefinition,
92
- PreludeROut,
93
- PreludeE,
94
- RunROut,
95
- RunE,
96
- > {
97
- new (state: DurableObjectState<{}>, env: Cloudflare.Env): DurableObject
98
-
99
- readonly service: Context.ServiceClass<NamespaceSelf, NamespaceId, DurableObjectNamespace>
100
-
101
- readonly definition: ActorNamespaceDefinition<
102
- ActorSelf,
103
- ActorId,
104
- Name,
105
- AttachmentFields,
106
- ClientSelf,
107
- ClientId,
108
- D,
109
- PreludeROut,
110
- PreludeE,
111
- RunROut,
112
- RunE
113
- >
114
-
115
- readonly upgrade: (
116
- name: Name["Type"],
117
- attachments: S.Struct<AttachmentFields>["Type"],
118
- ) => Effect.Effect<
119
- HttpServerResponse.HttpServerResponse,
120
- S.SchemaError | Encoding.EncodingError | Cause.NoSuchElementError,
121
- | NamespaceSelf
122
- | NativeRequest.NativeRequest
123
- | Name["EncodingServices"]
124
- | S.Struct<AttachmentFields>["EncodingServices"]
125
- >
126
-
127
- readonly layer: (binding: string) => Layer.Layer<NamespaceSelf, S.SchemaError, never>
128
- }
129
-
130
- export const Service =
131
- <NamespaceSelf>() =>
132
- <
133
- NamespaceId extends string,
134
- ActorSelf,
135
- ActorId extends string,
136
- Name extends TopFromString,
137
- AttachmentFields extends S.Struct.Fields,
138
- ClientSelf,
139
- ClientId extends string,
140
- D extends ProtocolDefinition,
141
- PreludeROut,
142
- PreludeE,
143
- RunROut,
144
- RunE,
145
- >(
146
- id: NamespaceId,
147
- definition: ActorNamespaceDefinition<
148
- ActorSelf,
149
- ActorId,
150
- Name,
151
- AttachmentFields,
152
- ClientSelf,
153
- ClientId,
154
- D,
155
- PreludeROut,
156
- PreludeE,
157
- RunROut,
158
- RunE
159
- >,
160
- ): ActorNamespace<
161
- NamespaceSelf,
162
- NamespaceId,
163
- ActorSelf,
164
- ActorId,
165
- Name,
166
- AttachmentFields,
167
- ClientSelf,
168
- ClientId,
169
- D,
170
- PreludeROut,
171
- PreludeE,
172
- RunROut,
173
- RunE
174
- > => {
175
- const { hibernation, actor, prelude, handlers, layer, onConnect } = definition
176
- const {
177
- definition: {
178
- name: Name,
179
- client: { key: clientId, protocol: P },
180
- attachments: AttachmentFields,
181
- },
182
- } = actor
183
-
184
- const encodeName = S.encodeEffect(Name)
185
-
186
- const Attachments = S.Struct(AttachmentFields)
187
- const encodeAttachments = S.encodeEffect(S.toCodecJson(Attachments))
188
- const decodeAttachments = S.decodeUnknownEffect(S.toCodecJson(Attachments))
189
- const encodeAttachmentsString = encodeJsonString(Attachments)
190
- const decodeAttachmentsString = decodeJsonString(Attachments)
191
-
192
- const encodeAuditionSuccess = encodeJsonString(P.Audition.Success)
193
- const encodeAuditionFailure = encodeJsonString(P.Audition.Failure)
194
- const decodeClientM = decodeJsonString(P.Client)
195
- const encodeFSuccess = encodeJsonString(P.F.Success)
196
- const encodeFFailure = encodeJsonString(P.F.Failure)
197
-
198
- const encodeEvent = encodeJsonString(P.Event)
199
-
200
- const transport: ActorTransport<WebSocket, AttachmentFields, D> = {
201
- send: (socket, event) => encodeEvent(event).pipe(Effect.andThen((v) => Effect.sync(() => socket.send(v)))),
202
- close: (socket) => Effect.sync(() => socket.close(1000)),
203
- snapshot: (socket, attachments) =>
204
- encodeAttachments(attachments).pipe(Effect.andThen((v) => Effect.sync(() => socket.serializeAttachment(v)))),
205
- }
206
-
207
- class NameDecoded extends Context.Service<NameDecoded, Name["Type"]>()(
208
- "liminal/WorkerdActorNamespace/NameDecoded",
209
- ) {}
210
-
211
- return class extends DurableObject {
212
- static definition = definition
213
- static service = Context.Service<NamespaceSelf, DurableObjectNamespace>()(id)
214
- static layer = Binding.layer(this.service, ["idFromName", "idFromString", "newUniqueId", "get"])
215
-
216
- readonly runtime
217
- readonly directory = ClientDirectory.make(actor, transport)
218
-
219
- constructor(state: DurableObjectState<{}>, env: Cloudflare.Env) {
220
- super(state, env)
221
- if (hibernation) {
222
- Option.andThen(
223
- Duration.fromInput(hibernation),
224
- flow(Duration.toMillis, state.setHibernatableWebSocketEventTimeout),
225
- )
226
- }
227
-
228
- const Live = Layer.mergeAll(
229
- FetchHttpClient.layer,
230
- Layer.succeed(DoState.DoState, state),
231
- Mutex.layer,
232
- Layer.effect(NameDecoded, S.decodeUnknownEffect(Name)(state.id.name)).pipe(
233
- Layer.provideMerge(prelude.pipe(Layer.provideMerge(ConfigProvider.layer(ConfigProvider.fromUnknown(env))))),
234
- ),
235
- ).pipe(boundLayer("actor"))
236
-
237
- const hydrateAttachments = Effect.gen({ self: this }, function* () {
238
- for (const socket of state.getWebSockets()) {
239
- const attachments = yield* decodeAttachments(socket.deserializeAttachment())
240
- yield* this.directory.register(socket, attachments)
241
- }
242
- }).pipe(span("hydrateAttachments"), Effect.tapCause(logCause))
243
-
244
- this.runtime = hydrateAttachments.pipe(Layer.effectDiscard, Layer.provideMerge(Live), ManagedRuntime.make)
245
- }
246
-
247
- override fetch(request: Request): Promise<Response> {
248
- return Effect.gen({ self: this }, function* () {
249
- const url = new URL(request.url)
250
- const attachments = yield* decodeAttachmentsString(url.searchParams.get("__liminal_attachments"))
251
- const { 0: webSocket, 1: server } = new WebSocketPair()
252
- const state = yield* DoState.DoState
253
- state.acceptWebSocket(server)
254
- server.send(yield* encodeAuditionSuccess({ _tag: "Audition.Success" }))
255
- const currentClient = yield* this.directory.register(server, attachments)
256
- const name = yield* NameDecoded
257
- const ActorLive = Layer.succeed(actor, {
258
- name,
259
- clients: this.directory.handles,
260
- currentClient,
261
- })
262
- yield* onConnect.pipe(
263
- Effect.scoped,
264
- span("onConnect"),
265
- Effect.scoped,
266
- Effect.provide(Layer.provideMerge(layer, ActorLive)),
267
- )
268
- yield* debug("ClientRegistered")
269
- return new Response(null, {
270
- status: 101,
271
- webSocket,
272
- headers: { [SecWebSocketProtocol]: "liminal" },
273
- })
274
- }).pipe(Effect.tapCause(logCause), span("fetch"), this.runtime.runPromise)
275
- }
276
-
277
- override webSocketMessage(socket: WebSocket, raw: string | ArrayBuffer) {
278
- Effect.gen({ self: this }, function* () {
279
- const currentClient = yield* this.directory.get(socket)
280
- const name = yield* NameDecoded
281
- const ActorLive = Layer.succeed(actor, {
282
- name,
283
- clients: this.directory.handles,
284
- currentClient,
285
- })
286
- const message = yield* decodeClientM(raw instanceof ArrayBuffer ? new TextDecoder().decode(raw) : raw)
287
- yield* debug("MessageReceived", { message })
288
- if (message._tag === "Audition.Payload") {
289
- return yield* Effect.die(undefined)
290
- }
291
- if (message._tag === "Disconnect") {
292
- return yield* currentClient.disconnect
293
- }
294
- const { id, payload } = message
295
- const { _tag, value } = payload as never
296
- yield* handlers[_tag]!(value).pipe(
297
- Effect.matchEffect({
298
- onSuccess: (value) =>
299
- encodeFSuccess({
300
- _tag: "F.Success",
301
- id,
302
- success: { _tag, value } as never,
303
- }),
304
- onFailure: (value) =>
305
- encodeFFailure({
306
- _tag: "F.Failure",
307
- id,
308
- failure: { _tag, value } as never,
309
- }),
310
- }),
311
- span("handler", { attributes: { _tag } }),
312
- Effect.andThen((v) => Effect.sync(() => socket.send(v))),
313
- Effect.scoped,
314
- Effect.provide(Layer.provideMerge(layer, ActorLive)),
315
- )
316
- }).pipe(Effect.scoped, Mutex.task, Effect.tapCause(logCause), span("webSocketMessage"), this.runtime.runFork)
317
- }
318
-
319
- override webSocketClose(socket: WebSocket, _code: number, _reason: string, _wasClean: boolean) {
320
- this.directory
321
- .unregister(socket)
322
- .pipe(Effect.tap(debug("SocketClosed")), Effect.tapCause(logCause), this.runtime.runFork)
323
- }
324
-
325
- override webSocketError(socket: WebSocket, cause: unknown) {
326
- Effect.gen({ self: this }, function* () {
327
- yield* debug("SocketErrored", { cause })
328
- yield* this.directory.unregister(socket)
329
- }).pipe(Effect.tapCause(logCause), span("SocketErrored", { attributes: { cause } }), this.runtime.runFork)
330
- }
331
-
332
- static readonly upgrade = (name: Name["Type"], attachments: (typeof Attachments)["Type"]) =>
333
- Effect.gen({ self: this }, function* () {
334
- yield* debug("UpgradeInitiated", { attachments })
335
- const namespace = yield* this.service
336
- const nameEncoded = yield* encodeName(name)
337
- const stub = namespace.getByName(nameEncoded)
338
- const request = yield* NativeRequest.NativeRequest
339
- const protocols = yield* Effect.fromNullishOr(request.headers.get(SecWebSocketProtocol)).pipe(
340
- Effect.map(flow(String.split(","), Array.map(String.trim))),
341
- )
342
- const liminalTokenI = yield* Array.findFirstIndex(protocols, (v) => v === "liminal")
343
- const requestClientId = yield* Effect.fromNullishOr(protocols[liminalTokenI + 1]).pipe(
344
- Effect.flatMap((v) => Encoding.decodeBase64UrlString(v).asEffect()),
345
- )
346
- if (requestClientId !== clientId) {
347
- return close(
348
- yield* encodeAuditionFailure({
349
- _tag: "Audition.Failure",
350
- expected: clientId,
351
- actual: requestClientId,
352
- }),
353
- )
354
- }
355
- const url = new URL(request.url)
356
- url.searchParams.set("__liminal_attachments", yield* encodeAttachmentsString(attachments))
357
- return yield* Effect.promise(() => stub.fetch(new Request(url, request))).pipe(
358
- Effect.map(HttpServerResponse.raw),
359
- )
360
- }).pipe(span("upgrade"))
361
- }
362
- }
package/workerd/index.ts DELETED
@@ -1 +0,0 @@
1
- export * as WorkerdActorNamespace from "./WorkerdActorNamespace.ts"