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
@@ -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