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
package/Actor.ts CHANGED
@@ -1,15 +1,11 @@
1
1
  import { Context, Schema as S, Effect } from "effect"
2
+ import * as Boundary from "liminal-util/Boundary"
3
+ import type { TopFromString } from "liminal-util/schema"
2
4
 
3
- import type { TopFromString } from "./_util/schema.ts"
4
5
  import type * as ActorClient from "./Client.ts"
5
6
  import type { ClientHandle, Sender } from "./ClientHandle.ts"
6
-
7
- import { diagnostic } from "./_diagnostic.ts"
8
- import * as Method from "./Method.ts"
9
7
  import { type ProtocolDefinition } from "./Protocol.ts"
10
8
 
11
- const { span } = diagnostic("Actor")
12
-
13
9
  export const TypeId = "~liminal/Actor" as const
14
10
 
15
11
  export interface Service<
@@ -54,14 +50,9 @@ export interface Actor<
54
50
 
55
51
  readonly definition: ActorDefinition<Name, AttachmentFields, ActorClientSelf, ActorClientId, D>
56
52
 
57
- readonly all: Sender<ActorSelf, D>
58
-
59
- readonly others: Sender<ActorSelf, D>
53
+ readonly all: Sender<D, ActorSelf>
60
54
 
61
- readonly handler: <K extends keyof D["methods"], R>(
62
- tag: K,
63
- f: Method.Handler<D["methods"][K], R>,
64
- ) => Method.Handler<D["methods"][K], R>
55
+ readonly others: Sender<D, ActorSelf>
65
56
  }
66
57
 
67
58
  export const Service =
@@ -79,45 +70,42 @@ export const Service =
79
70
  ): Actor<ActorSelf, ActorId, Name, AttachmentFields, ClientSelf, ClientId, D> => {
80
71
  const tag = Context.Service<ActorSelf, Service<ActorSelf, Name, AttachmentFields, D>>()(id)
81
72
 
82
- const all: Sender<ActorSelf, D> = {
73
+ const all: Sender<D, ActorSelf> = {
83
74
  send: (key, payload) =>
84
- tag.asEffect().pipe(
75
+ tag.pipe(
85
76
  Effect.flatMap(({ clients }) =>
86
77
  Effect.forEach(clients, (client) => client.send(key, payload), { concurrency: "unbounded" }),
87
78
  ),
88
- span("all.send"),
79
+ Boundary.span("send-all", import.meta.url),
89
80
  ),
90
- disconnect: tag.asEffect().pipe(
81
+ disconnect: tag.pipe(
91
82
  Effect.flatMap(({ clients }) => Effect.forEach(clients, ({ disconnect }) => disconnect)),
92
- span("all.disconnect"),
83
+ Boundary.span("disconnect-all", import.meta.url),
93
84
  ),
94
85
  }
95
86
 
96
- const others: Sender<ActorSelf, D> = {
97
- send: Effect.fnUntraced(function* (key, payload) {
98
- const { clients, currentClient } = yield* tag
99
- yield* Effect.forEach(
100
- clients,
101
- (client) => (client === currentClient ? Effect.void : client.send(key, payload)),
102
- { concurrency: "unbounded" },
103
- )
104
- }, span("others.send")),
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
+ ),
105
99
  disconnect: Effect.gen(function* () {
106
100
  const { clients, currentClient } = yield* tag
107
101
  yield* Effect.forEach(clients, (client) => (client === currentClient ? Effect.void : client.disconnect))
108
- }).pipe(span("others.disconnect")),
102
+ }).pipe(Boundary.span("disconnect-others", import.meta.url)),
109
103
  }
110
104
 
111
- const handler = <K extends keyof D["methods"], R>(
112
- _tag: K,
113
- f: Method.Handler<D["methods"][K], R>,
114
- ): Method.Handler<D["methods"][K], R> => f
115
-
116
105
  return Object.assign(tag, {
117
106
  [TypeId]: TypeId,
118
107
  definition,
119
108
  all,
120
109
  others,
121
- handler,
122
110
  })
123
111
  }
package/ActorHandle.ts ADDED
@@ -0,0 +1,34 @@
1
+ import { Schema as S, Effect, Cause, Encoding } from "effect"
2
+ import { NativeRequest } from "effect-workerd"
3
+ import { HttpServerResponse } from "effect/unstable/http"
4
+ import type { TopFromString } from "liminal-util/schema"
5
+
6
+ import type { Send } from "./ClientHandle.ts"
7
+ import type { Methods } from "./Method.ts"
8
+ import type { ProtocolDefinition } from "./Protocol.ts"
9
+
10
+ export interface ActorHandle<
11
+ NamespaceSelf,
12
+ Internal extends Methods,
13
+ Name extends TopFromString,
14
+ AttachmentFields extends S.Struct.Fields,
15
+ D extends ProtocolDefinition,
16
+ > {
17
+ readonly upgrade: (
18
+ attachments: S.Struct<AttachmentFields>["Type"],
19
+ ) => Effect.Effect<
20
+ HttpServerResponse.HttpServerResponse,
21
+ S.SchemaError | Encoding.EncodingError | Cause.NoSuchElementError,
22
+ | NamespaceSelf
23
+ | NativeRequest.NativeRequest
24
+ | Name["EncodingServices"]
25
+ | S.Struct<AttachmentFields>["EncodingServices"]
26
+ >
27
+
28
+ readonly call: <K extends keyof Internal, M extends Internal[K]>(
29
+ method: K,
30
+ payload: M["payload"]["Type"],
31
+ ) => Effect.Effect<M["success"]["Type"], M["failure"]["Type"], NamespaceSelf>
32
+
33
+ readonly proxySendAll: Send<D, NamespaceSelf>
34
+ }
@@ -0,0 +1,188 @@
1
+ import { Layer, Effect, Schema as S, Context, flow, String, Array, Encoding, Exit } from "effect"
2
+ import { Binding, Env, NativeRequest } from "effect-workerd"
3
+ import { SecWebSocketProtocol, close } from "effect-workerd/socket_util"
4
+ import { HttpServerResponse, HttpTraceContext } from "effect/unstable/http"
5
+ import * as Boundary from "liminal-util/Boundary"
6
+ import { type TopFromString, encodeJsonString } from "liminal-util/schema"
7
+
8
+ import type { Actor } from "./Actor.ts"
9
+ import type { ActorHandle } from "./ActorHandle.ts"
10
+ import type { Methods } from "./Method.ts"
11
+ import type { ProtocolDefinition } from "./Protocol.ts"
12
+
13
+ export interface ActorNamespaceDefinition<
14
+ Internal extends Methods,
15
+ ActorSelf,
16
+ ActorId extends string,
17
+ Name extends TopFromString,
18
+ AttachmentFields extends S.Struct.Fields,
19
+ ClientSelf,
20
+ ClientId extends string,
21
+ D extends ProtocolDefinition,
22
+ > {
23
+ readonly binding: string
24
+
25
+ readonly internal: Internal
26
+
27
+ readonly actor: Actor<ActorSelf, ActorId, Name, AttachmentFields, ClientSelf, ClientId, D>
28
+ }
29
+
30
+ export interface ActorNamespace<
31
+ NamespaceSelf,
32
+ NamespaceId extends string,
33
+ Internal extends Methods,
34
+ ActorSelf,
35
+ ActorId extends string,
36
+ Name extends TopFromString,
37
+ AttachmentFields extends S.Struct.Fields,
38
+ ClientSelf,
39
+ ClientId extends string,
40
+ D extends ProtocolDefinition,
41
+ > {
42
+ new (
43
+ _: never,
44
+ ): Context.ServiceClass.Shape<
45
+ NamespaceId,
46
+ DurableObjectNamespace<Rpc.DurableObjectBranded & ActorNamespace.MakeRpc<Internal, D>>
47
+ >
48
+
49
+ readonly definition: ActorNamespaceDefinition<
50
+ Methods,
51
+ ActorSelf,
52
+ ActorId,
53
+ Name,
54
+ AttachmentFields,
55
+ ClientSelf,
56
+ ClientId,
57
+ D
58
+ >
59
+
60
+ readonly bind: (name: Name["Type"]) => ActorHandle<NamespaceSelf, Internal, Name, AttachmentFields, D>
61
+
62
+ readonly layer: Layer.Layer<NamespaceSelf, S.SchemaError, Env>
63
+ }
64
+
65
+ export declare namespace ActorNamespace {
66
+ export type MakeRpc<Internal extends Methods, D extends ProtocolDefinition> = {
67
+ rpc: <K extends keyof Internal>(
68
+ method: K,
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
+ }
74
+ }
75
+
76
+ export const Service =
77
+ <NamespaceSelf>() =>
78
+ <
79
+ NamespaceId extends string,
80
+ Internal extends Methods,
81
+ ActorSelf,
82
+ ActorId extends string,
83
+ Name extends TopFromString,
84
+ AttachmentFields extends S.Struct.Fields,
85
+ ClientSelf,
86
+ ClientId extends string,
87
+ D extends ProtocolDefinition,
88
+ >(
89
+ id: NamespaceId,
90
+ definition: ActorNamespaceDefinition<Internal, ActorSelf, ActorId, Name, AttachmentFields, ClientSelf, ClientId, D>,
91
+ ): ActorNamespace<
92
+ NamespaceSelf,
93
+ NamespaceId,
94
+ Internal,
95
+ ActorSelf,
96
+ ActorId,
97
+ Name,
98
+ AttachmentFields,
99
+ ClientSelf,
100
+ ClientId,
101
+ D
102
+ > => {
103
+ const { binding, actor } = definition
104
+ const {
105
+ definition: {
106
+ name: Name,
107
+ client: { key: clientId, protocol: P },
108
+ attachments: AttachmentFields,
109
+ },
110
+ } = actor
111
+
112
+ const tag = Context.Service<
113
+ NamespaceSelf,
114
+ DurableObjectNamespace<Rpc.DurableObjectBranded & ActorNamespace.MakeRpc<Internal, D>>
115
+ >()(id)
116
+
117
+ const encodeName = S.encodeEffect(Name)
118
+ const Attachments = S.Struct(AttachmentFields)
119
+ const encodeAttachmentsString = encodeJsonString(Attachments)
120
+ const encodeAuditionFailure = encodeJsonString(P.Audition.Failure)
121
+
122
+ const bind = (name: Name["Type"]): ActorHandle<NamespaceSelf, Internal, Name, AttachmentFields, D> => {
123
+ const getStub = Effect.gen(function* () {
124
+ const namespace = yield* tag
125
+ const nameEncoded = yield* encodeName(name)
126
+ return namespace.getByName(nameEncoded)
127
+ })
128
+
129
+ const upgrade = (attachments: S.Struct<AttachmentFields>["Type"]) =>
130
+ Effect.gen({ self: this }, function* () {
131
+ const request = yield* NativeRequest.NativeRequest
132
+ const protocols = yield* Effect.fromNullishOr(request.headers.get(SecWebSocketProtocol)).pipe(
133
+ Effect.map(flow(String.split(","), Array.map(String.trim))),
134
+ )
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)),
139
+ )
140
+ if (requestClientId !== clientId) {
141
+ return close(
142
+ yield* encodeAuditionFailure({
143
+ _tag: "Audition.Failure",
144
+ expected: clientId,
145
+ actual: requestClientId,
146
+ }),
147
+ )
148
+ }
149
+ const url = new URL(request.url)
150
+ url.searchParams.set("__liminal_attachments", yield* encodeAttachmentsString(attachments))
151
+ url.searchParams.set("__liminal_client_id", liminalClientId)
152
+ const actorRequest = new Request(url, request)
153
+ const traceHeaders = yield* Effect.currentSpan.pipe(Effect.map(HttpTraceContext.toHeaders))
154
+ for (const [key, value] of Object.entries(traceHeaders)) {
155
+ actorRequest.headers.set(key, value)
156
+ }
157
+ const stub = yield* getStub
158
+ return yield* Effect.promise(() => stub.fetch(actorRequest)).pipe(Effect.map(HttpServerResponse.raw))
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
181
+
182
+ return { upgrade, call, proxySendAll }
183
+ }
184
+
185
+ const layer = Binding.layer(tag, ["idFromName", "idFromString", "newUniqueId", "get", "getByName"])(binding)
186
+
187
+ return Object.assign(tag, { definition, bind, layer })
188
+ }