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,36 +1,43 @@
1
1
  import { Schema as S, Effect, Cause, Ref } 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"
5
+ import { phantom } from "./_util/phantom.ts"
4
6
  import type { Actor } from "./Actor.ts"
5
7
  import type { ActorTransport } from "./ActorTransport.ts"
8
+ import * as ClientHandle from "./ClientHandle.ts"
6
9
  import type { ProtocolDefinition, Disconnect, Protocol } from "./Protocol.ts"
7
10
 
8
- import { diagnostic } from "./_diagnostic.ts"
9
- import { phantom } from "./_util/phantom.ts"
10
- import * as ClientHandle from "./ClientHandle.ts"
11
+ export interface ClientEntry<Client, Handle> {
12
+ readonly client: Client
11
13
 
12
- const { span } = diagnostic("ClientDirectory")
14
+ readonly handle: Handle
15
+ }
13
16
 
14
17
  export interface ClientDirectory<
15
- Raw,
18
+ Key,
19
+ Client,
16
20
  ActorSelf,
17
21
  AttachmentFields extends S.Struct.Fields,
18
22
  D extends ProtocolDefinition,
19
23
  > {
20
24
  readonly "": {
21
25
  readonly Handle: ClientHandle.ClientHandle<ActorSelf, AttachmentFields, D>
26
+ readonly Entry: ClientEntry<Client, ClientHandle.ClientHandle<ActorSelf, AttachmentFields, D>>
22
27
  }
23
28
 
24
29
  readonly handles: ReadonlySet<this[""]["Handle"]>
25
30
 
26
31
  readonly register: (
27
- raw: Raw,
32
+ client: Client,
28
33
  attachments: S.Struct<AttachmentFields>["Type"],
29
34
  ) => Effect.Effect<this[""]["Handle"], S.SchemaError, S.Struct<AttachmentFields>["EncodingServices"]>
30
35
 
31
- readonly get: (raw: Raw) => Effect.Effect<this[""]["Handle"], Cause.NoSuchElementError>
36
+ readonly get: (key: Key) => Effect.Effect<this[""]["Handle"], Cause.NoSuchElementError>
37
+
38
+ readonly entry: (key: Key) => Effect.Effect<this[""]["Entry"], Cause.NoSuchElementError>
32
39
 
33
- readonly unregister: (raw: Raw) => Effect.Effect<void>
40
+ readonly unregister: (key: Key) => Effect.Effect<void>
34
41
  }
35
42
 
36
43
  export interface HandleEncoders<T, AttachmentFields extends S.Struct.Fields, D extends ProtocolDefinition> {
@@ -44,7 +51,8 @@ export interface HandleEncoders<T, AttachmentFields extends S.Struct.Fields, D e
44
51
  }
45
52
 
46
53
  export const make = <
47
- Raw,
54
+ Key,
55
+ Client,
48
56
  ActorSelf,
49
57
  ActorId extends string,
50
58
  Name extends TopFromString,
@@ -54,51 +62,65 @@ export const make = <
54
62
  D extends ProtocolDefinition,
55
63
  >(
56
64
  _actor: Actor<ActorSelf, ActorId, Name, AttachmentFields, ClientSelf, ClientId, D>,
57
- { send, close, snapshot }: ActorTransport<Raw, AttachmentFields, D>,
58
- ): ClientDirectory<Raw, ActorSelf, AttachmentFields, D> => {
65
+ {
66
+ transport: { key, send, close, snapshot },
67
+ }: {
68
+ readonly transport: ActorTransport<Key, Client, AttachmentFields, D>
69
+ },
70
+ ): ClientDirectory<Key, Client, ActorSelf, AttachmentFields, D> => {
59
71
  type Handle = ClientHandle.ClientHandle<ActorSelf, AttachmentFields, D>
72
+ type Entry = ClientEntry<Client, Handle>
60
73
 
61
- const raws = new Map<Raw, Handle>()
74
+ const entries = new Map<Key, Entry>()
62
75
  const handles = new Set<Handle>()
63
76
 
64
- const get = (raw: Raw) => Effect.fromNullishOr(raws.get(raw))
65
-
66
- const register = Effect.fnUntraced(function* (raw: Raw, attachments: S.Struct<AttachmentFields>["Type"]) {
67
- yield* snapshot(raw, attachments)
68
- const attachmentsRef = yield* Ref.make(attachments)
69
- const handle: Handle = {
70
- attachments: Ref.get(attachmentsRef),
71
- save: Effect.fnUntraced(function* (attachments) {
72
- yield* Ref.set(attachmentsRef, attachments)
73
- yield* snapshot(raw, attachments)
74
- }),
75
- send: (_tag, payload) =>
76
- send(raw, {
77
- _tag: "Event",
78
- event: { _tag, ...payload } as never,
77
+ const entry = (key: Key) => Effect.fromNullishOr(entries.get(key))
78
+ const get = (key: Key) => entry(key).pipe(Effect.map(({ handle }) => handle))
79
+
80
+ const unregister = (key: Key) =>
81
+ Effect.sync(() => {
82
+ const current = entries.get(key)
83
+ if (current) {
84
+ entries.delete(key)
85
+ handles.delete(current.handle)
86
+ }
87
+ }).pipe(Boundary.span("unregister", import.meta.url))
88
+
89
+ const register = Effect.fnUntraced(
90
+ function* (client: Client, attachments: S.Struct<AttachmentFields>["Type"]) {
91
+ const clientKey = key(client)
92
+ yield* snapshot(client, attachments)
93
+ const attachmentsRef = yield* Ref.make(attachments)
94
+ const handle: Handle = {
95
+ attachments: Ref.get(attachmentsRef),
96
+ save: Effect.fnUntraced(function* (attachments) {
97
+ yield* Ref.set(attachmentsRef, attachments)
98
+ yield* snapshot(client, attachments)
79
99
  }),
80
- disconnect: close(raw).pipe(
81
- Effect.andThen(() =>
82
- Effect.sync(() => {
83
- raws.delete(raw)
84
- handles.delete(handle)
100
+ send: (_tag, payload) =>
101
+ send(client, {
102
+ _tag: "Event",
103
+ event: { _tag, ...payload } as never,
85
104
  }),
86
- ),
87
- ),
88
- }
89
- raws.set(raw, handle)
90
- handles.add(handle)
91
- return handle
92
- }, span("register"))
93
-
94
- const unregister = (raw: Raw) =>
95
- Effect.sync(() => {
96
- const handle = raws.get(raw)
97
- if (handle) {
98
- raws.delete(raw)
99
- handles.delete(handle)
105
+ disconnect: close(client).pipe(Effect.andThen(unregister(clientKey)), Effect.asVoid),
106
+ }
107
+ const previous = entries.get(clientKey)
108
+ if (previous) {
109
+ handles.delete(previous.handle)
100
110
  }
101
- }).pipe(span("unregister"))
111
+ entries.set(clientKey, { client, handle })
112
+ handles.add(handle)
113
+ return handle
114
+ },
115
+ Boundary.span("register", import.meta.url),
116
+ )
102
117
 
103
- return { ...phantom, handles, register, get, unregister }
118
+ return {
119
+ ...phantom,
120
+ handles,
121
+ register,
122
+ get,
123
+ entry,
124
+ unregister,
125
+ }
104
126
  }
package/ClientHandle.ts CHANGED
@@ -2,20 +2,22 @@ import { Schema as S, Effect } from "effect"
2
2
 
3
3
  import type { ProtocolDefinition } from "./Protocol.ts"
4
4
 
5
- export interface Sender<ActorSelf, D extends ProtocolDefinition> {
6
- readonly send: <K extends keyof D["events"]>(
7
- tag: K,
8
- payload: S.Struct<D["events"][K]>["Type"],
9
- ) => Effect.Effect<void, S.SchemaError, ActorSelf | ReturnType<typeof S.TaggedUnion<D["events"]>>["EncodingServices"]>
5
+ export type Send<D extends ProtocolDefinition, RIn> = <K extends keyof D["events"]>(
6
+ tag: K,
7
+ payload: S.Struct<D["events"][K]>["Type"],
8
+ ) => Effect.Effect<void, never, RIn | ReturnType<typeof S.TaggedUnion<D["events"]>>["EncodingServices"]>
10
9
 
11
- readonly disconnect: Effect.Effect<void, never, ActorSelf>
10
+ export interface Sender<D extends ProtocolDefinition, R> {
11
+ readonly send: Send<D, R>
12
+
13
+ readonly disconnect: Effect.Effect<void, never, R>
12
14
  }
13
15
 
14
16
  export interface ClientHandle<
15
17
  ActorSelf,
16
18
  AttachmentFields extends S.Struct.Fields,
17
19
  D extends ProtocolDefinition,
18
- > extends Sender<ActorSelf, D> {
20
+ > extends Sender<D, ActorSelf> {
19
21
  readonly attachments: Effect.Effect<S.Struct<AttachmentFields>["Type"]>
20
22
 
21
23
  readonly save: (
@@ -0,0 +1,15 @@
1
+ import { Schema as S, Effect } from "effect"
2
+
3
+ import type { ProtocolDefinition, Disconnect, Protocol } from "./Protocol.ts"
4
+
5
+ export interface HandleEncoders<T, AttachmentFields extends S.Struct.Fields, D extends ProtocolDefinition> {
6
+ readonly attachments: (
7
+ value: S.Struct<AttachmentFields>["Type"],
8
+ ) => Effect.Effect<T, S.SchemaError, S.Struct<AttachmentFields>["EncodingServices"]>
9
+
10
+ readonly event: (
11
+ value: Protocol<D>["Event"]["Type"],
12
+ ) => Effect.Effect<T, S.SchemaError, Protocol<D>["Event"]["EncodingServices"]>
13
+
14
+ readonly disconnect: Effect.Effect<T, S.SchemaError, (typeof Disconnect)["EncodingServices"]>
15
+ }
package/Fn.ts ADDED
@@ -0,0 +1,94 @@
1
+ import { Effect, Schema as S } from "effect"
2
+
3
+ import type { ClientError, UnresolvedError } from "./errors.ts"
4
+ import type { Methods } from "./Method.ts"
5
+
6
+ export type FnPayload<External extends Methods, K extends keyof External> = External[K]["payload"]["Type"]
7
+
8
+ export type FnError<External extends Methods, K extends keyof External> = [
9
+ External[K]["failure"]["Type"] | ClientError | S.SchemaError | UnresolvedError,
10
+ ][0]
11
+
12
+ export type FnEffect<Self, External extends Methods, K extends keyof External> = Effect.Effect<
13
+ External[K]["success"]["Type"],
14
+ FnError<External, K>,
15
+ Self
16
+ >
17
+
18
+ export interface Fn<Self, Internal extends Methods> {
19
+ <K extends keyof Internal>(tag: K): (payload: FnPayload<Internal, K>) => FnEffect<Self, Internal, K>
20
+
21
+ <K extends keyof Internal, A>(
22
+ tag: K,
23
+ a: (effect: FnEffect<Self, Internal, K>, payload: FnPayload<Internal, K>) => A,
24
+ ): (payload: FnPayload<Internal, K>) => A
25
+
26
+ <K extends keyof Internal, A, B>(
27
+ tag: K,
28
+ a: (effect: FnEffect<Self, Internal, K>, payload: FnPayload<Internal, K>) => A,
29
+ b: (value: A, payload: FnPayload<Internal, K>) => B,
30
+ ): (payload: FnPayload<Internal, K>) => B
31
+
32
+ <K extends keyof Internal, A, B, C>(
33
+ tag: K,
34
+ a: (effect: FnEffect<Self, Internal, K>, payload: FnPayload<Internal, K>) => A,
35
+ b: (value: A, payload: FnPayload<Internal, K>) => B,
36
+ c: (value: B, payload: FnPayload<Internal, K>) => C,
37
+ ): (payload: FnPayload<Internal, K>) => C
38
+
39
+ <K extends keyof Internal, A, B, C, D>(
40
+ tag: K,
41
+ a: (effect: FnEffect<Self, Internal, K>, payload: FnPayload<Internal, K>) => A,
42
+ b: (value: A, payload: FnPayload<Internal, K>) => B,
43
+ c: (value: B, payload: FnPayload<Internal, K>) => C,
44
+ d: (value: C, payload: FnPayload<Internal, K>) => D,
45
+ ): (payload: FnPayload<Internal, K>) => D
46
+
47
+ <K extends keyof Internal, A, B, C, D, E>(
48
+ tag: K,
49
+ a: (effect: FnEffect<Self, Internal, K>, payload: FnPayload<Internal, K>) => A,
50
+ b: (value: A, payload: FnPayload<Internal, K>) => B,
51
+ c: (value: B, payload: FnPayload<Internal, K>) => C,
52
+ d: (value: C, payload: FnPayload<Internal, K>) => D,
53
+ e: (value: D, payload: FnPayload<Internal, K>) => E,
54
+ ): (payload: FnPayload<Internal, K>) => E
55
+ }
56
+
57
+ export interface FnNoSelf<Internal extends Methods> {
58
+ <K extends keyof Internal>(tag: K): (payload: FnPayload<Internal, K>) => FnEffect<never, Internal, K>
59
+
60
+ <K extends keyof Internal, A>(
61
+ tag: K,
62
+ a: (effect: FnEffect<never, Internal, K>, payload: FnPayload<Internal, K>) => A,
63
+ ): (payload: FnPayload<Internal, K>) => A
64
+
65
+ <K extends keyof Internal, A, B>(
66
+ tag: K,
67
+ a: (effect: FnEffect<never, Internal, K>, payload: FnPayload<Internal, K>) => A,
68
+ b: (value: A, payload: FnPayload<Internal, K>) => B,
69
+ ): (payload: FnPayload<Internal, K>) => B
70
+
71
+ <K extends keyof Internal, A, B, C>(
72
+ tag: K,
73
+ a: (effect: FnEffect<never, Internal, K>, payload: FnPayload<Internal, K>) => A,
74
+ b: (value: A, payload: FnPayload<Internal, K>) => B,
75
+ c: (value: B, payload: FnPayload<Internal, K>) => C,
76
+ ): (payload: FnPayload<Internal, K>) => C
77
+
78
+ <K extends keyof Internal, A, B, C, D>(
79
+ tag: K,
80
+ a: (effect: FnEffect<never, Internal, K>, payload: FnPayload<Internal, K>) => A,
81
+ b: (value: A, payload: FnPayload<Internal, K>) => B,
82
+ c: (value: B, payload: FnPayload<Internal, K>) => C,
83
+ d: (value: C, payload: FnPayload<Internal, K>) => D,
84
+ ): (payload: FnPayload<Internal, K>) => D
85
+
86
+ <K extends keyof Internal, A, B, C, D, E>(
87
+ tag: K,
88
+ a: (effect: FnEffect<never, Internal, K>, payload: FnPayload<Internal, K>) => A,
89
+ b: (value: A, payload: FnPayload<Internal, K>) => B,
90
+ c: (value: B, payload: FnPayload<Internal, K>) => C,
91
+ d: (value: C, payload: FnPayload<Internal, K>) => D,
92
+ e: (value: D, payload: FnPayload<Internal, K>) => E,
93
+ ): (payload: FnPayload<Internal, K>) => E
94
+ }
package/Method.ts CHANGED
@@ -1,29 +1,19 @@
1
1
  import { Schema as S, Effect } from "effect"
2
2
 
3
- export interface MethodDefinition<Payload extends S.Top, Success extends S.Top, Failure extends S.Top> {
4
- readonly payload: Payload
5
- readonly success: Success
6
- readonly failure: Failure
3
+ export interface Method {
4
+ readonly payload: S.Top
5
+ readonly success: S.Top
6
+ readonly failure: S.Top
7
7
  }
8
8
 
9
- export type Any = MethodDefinition<S.Top, S.Top, S.Top>
9
+ export type Methods = Record<string, Method>
10
10
 
11
- export const make = <Payload extends S.Top, Success extends S.Top, Failure extends S.Top>({
12
- payload,
13
- success,
14
- failure,
15
- }: {
16
- readonly payload: Payload
17
- readonly success: Success
18
- readonly failure: Failure
19
- }): MethodDefinition<Payload, Success, Failure> => ({ payload, success, failure })
11
+ export type Handler<M extends Method, R> = (
12
+ payload: M["payload"]["Type"],
13
+ ) => Effect.Effect<M["success"]["Type"], M["failure"]["Type"], R>
20
14
 
21
- export type Handler<Method extends Any, R> = (
22
- payload: Method["payload"]["Type"],
23
- ) => Effect.Effect<Method["success"]["Type"], Method["failure"]["Type"], R>
15
+ export const handler = <M extends Method, R>(_method: M, f: Handler<M, R>): Handler<M, R> => f
24
16
 
25
- export type Handlers<Methods extends Record<string, Any>, R> = {
26
- [K in keyof Methods]: Handler<Methods[K], R>
17
+ export type Handlers<Internal extends Methods, R> = {
18
+ readonly [K in keyof Internal]: Handler<Internal[K], R>
27
19
  }
28
-
29
- export const handler = <Method extends Any, R>(_method: Method, f: Handler<Method, R>): Handler<Method, R> => f
package/Protocol.ts CHANGED
@@ -1,33 +1,24 @@
1
- import { Schema as S, Record, Types } from "effect"
1
+ import { Schema as S, Record } from "effect"
2
2
 
3
- import type * as Method from "./Method.ts"
3
+ import type { Methods } from "./Method.ts"
4
+ import * as Tracing from "./Tracing.ts"
4
5
 
5
6
  export interface ProtocolDefinition<
6
- Methods extends Record<string, Method.Any> = Record<string, Method.Any>,
7
+ State extends S.Struct.Fields = S.Struct.Fields,
8
+ External extends Methods = Methods,
7
9
  Events extends Record<string, S.Struct.Fields> = Record<string, S.Struct.Fields>,
8
10
  > {
9
- readonly methods: Methods
11
+ readonly state: State
10
12
 
11
- readonly events: Events
12
- }
13
+ readonly external: External
13
14
 
14
- export declare namespace ProtocolDefinition {
15
- export type Merge<T extends ProtocolDefinition, U extends ProtocolDefinition> = ProtocolDefinition<
16
- [T] extends [never]
17
- ? U["methods"]
18
- : {
19
- [K in keyof T["methods"] & keyof U["methods"] as Types.Equals<T["methods"][K], U["methods"][K]> extends true
20
- ? K
21
- : never]: T["methods"][K]
22
- },
23
- [T] extends [never] ? U["events"] : T["events"] & U["events"]
24
- >
15
+ readonly events: Events
25
16
  }
26
17
 
27
18
  export interface Protocol<D extends ProtocolDefinition> {
28
19
  readonly Audition: {
29
- readonly Payload: S.TaggedStruct<"Audition.Payload", { client: S.String }>
30
- readonly Success: S.TaggedStruct<"Audition.Success", {}>
20
+ readonly Payload: S.TaggedStruct<"Audition.Payload", { readonly client: S.String }>
21
+ readonly Success: S.TaggedStruct<"Audition.Success", { readonly initial: S.Struct<D["state"]> }>
31
22
  readonly Failure: S.TaggedStruct<
32
23
  "Audition.Failure",
33
24
  {
@@ -43,6 +34,7 @@ export interface Protocol<D extends ProtocolDefinition> {
43
34
  readonly event: S.TaggedUnion<{
44
35
  readonly [K in keyof D["events"] & string]: S.TaggedStruct<K, D["events"][K]>
45
36
  }>
37
+ readonly trace: S.optional<typeof Tracing.TraceEnvelope>
46
38
  }
47
39
  >
48
40
 
@@ -52,8 +44,12 @@ export interface Protocol<D extends ProtocolDefinition> {
52
44
  {
53
45
  readonly id: S.Int
54
46
  readonly payload: S.TaggedUnion<{
55
- readonly [K in keyof D["methods"] & string]: S.TaggedStruct<K, { readonly value: D["methods"][K]["payload"] }>
47
+ readonly [K in keyof D["external"] & string]: S.TaggedStruct<
48
+ K,
49
+ { readonly value: D["external"][K]["payload"] }
50
+ >
56
51
  }>
52
+ readonly trace: S.optional<typeof Tracing.TraceEnvelope>
57
53
  }
58
54
  >
59
55
 
@@ -62,7 +58,10 @@ export interface Protocol<D extends ProtocolDefinition> {
62
58
  {
63
59
  readonly id: S.Int
64
60
  readonly success: S.TaggedUnion<{
65
- readonly [K in keyof D["methods"] & string]: S.TaggedStruct<K, { readonly value: D["methods"][K]["success"] }>
61
+ readonly [K in keyof D["external"] & string]: S.TaggedStruct<
62
+ K,
63
+ { readonly value: D["external"][K]["success"] }
64
+ >
66
65
  }>
67
66
  }
68
67
  >
@@ -72,7 +71,10 @@ export interface Protocol<D extends ProtocolDefinition> {
72
71
  {
73
72
  readonly id: S.Int
74
73
  readonly failure: S.TaggedUnion<{
75
- readonly [K in keyof D["methods"] & string]: S.TaggedStruct<K, { readonly value: D["methods"][K]["failure"] }>
74
+ readonly [K in keyof D["external"] & string]: S.TaggedStruct<
75
+ K,
76
+ { readonly value: D["external"][K]["failure"] }
77
+ >
76
78
  }>
77
79
  }
78
80
  >
@@ -96,37 +98,43 @@ export interface Protocol<D extends ProtocolDefinition> {
96
98
 
97
99
  export const Disconnect = S.TaggedStruct("Disconnect", {})
98
100
 
99
- const Audition = {
100
- Payload: S.TaggedStruct("Audition.Payload", {
101
- client: S.String,
102
- }),
103
- Success: S.TaggedStruct("Audition.Success", {}),
104
- Failure: S.TaggedStruct("Audition.Failure", {
105
- expected: S.String,
106
- actual: S.String,
107
- }),
108
- }
101
+ const AuditionPayload = S.TaggedStruct("Audition.Payload", {
102
+ client: S.String,
103
+ })
104
+
105
+ const AuditionFailure = S.TaggedStruct("Audition.Failure", {
106
+ expected: S.String,
107
+ actual: S.String,
108
+ })
109
109
 
110
- export const Protocol = <D extends ProtocolDefinition>({ events, methods }: D): Protocol<D> => {
110
+ export const Protocol = <D extends ProtocolDefinition>({ state, events, external }: D): Protocol<D> => {
111
111
  type T = Protocol<D>
112
112
 
113
+ const Audition = {
114
+ Payload: AuditionPayload,
115
+ Success: S.TaggedStruct("Audition.Success", { initial: S.Struct(state) }),
116
+ Failure: AuditionFailure,
117
+ }
118
+
113
119
  const F: T["F"] = {
114
120
  Payload: S.TaggedStruct("F.Payload", {
115
121
  id: S.Int,
116
- payload: S.TaggedUnion(Record.map(methods, ({ payload: value }) => ({ value }))),
122
+ payload: S.TaggedUnion(Record.map(external, ({ payload: value }) => ({ value }))),
123
+ trace: S.optional(Tracing.TraceEnvelope),
117
124
  }) as never,
118
125
  Success: S.TaggedStruct("F.Success", {
119
126
  id: S.Int,
120
- success: S.TaggedUnion(Record.map(methods, ({ success: value }) => ({ value }))),
127
+ success: S.TaggedUnion(Record.map(external, ({ success: value }) => ({ value }))),
121
128
  }) as never,
122
129
  Failure: S.TaggedStruct("F.Failure", {
123
130
  id: S.Int,
124
- failure: S.TaggedUnion(Record.map(methods, ({ failure: value }) => ({ value }))),
131
+ failure: S.TaggedUnion(Record.map(external, ({ failure: value }) => ({ value }))),
125
132
  }) as never,
126
133
  }
127
134
 
128
135
  const Event: T["Event"] = S.TaggedStruct("Event", {
129
136
  event: S.TaggedUnion(events),
137
+ trace: S.optional(Tracing.TraceEnvelope),
130
138
  }) as never
131
139
 
132
140
  const Client: T["Client"] = S.Union([Audition.Payload, F.Payload, Disconnect])
package/Reducer.ts ADDED
@@ -0,0 +1,22 @@
1
+ import { Schema as S, Types, Effect } from "effect"
2
+
3
+ import type { ProtocolDefinition } from "./Protocol.ts"
4
+
5
+ export type Reducer<D extends ProtocolDefinition, K extends keyof D["events"] = keyof D["events"]> = (
6
+ event: Types.ExtractTag<ReturnType<typeof S.TaggedUnion<D["events"]>>["Type"], Extract<K, string>>,
7
+ ) => (state: S.Struct<D["state"]>["Type"]) => Effect.Effect<S.Struct<D["state"]>["Type"] | void, never, unknown>
8
+
9
+ export type Reducers<D extends ProtocolDefinition> = {
10
+ readonly [K in keyof D["events"]]: Reducer<D, K>
11
+ }
12
+
13
+ export declare namespace Reducers {
14
+ export type Services<Self, D extends Record<string, Reducer<any, any>>> = Exclude<
15
+ {
16
+ readonly [K in keyof D]: D[K] extends (...args: any) => (...args: any) => Effect.Effect<any, any, infer R>
17
+ ? R
18
+ : never
19
+ }[keyof D],
20
+ Self
21
+ >
22
+ }
package/Tracing.ts ADDED
@@ -0,0 +1,45 @@
1
+ import { Effect, Schema as S, Tracer } from "effect"
2
+
3
+ export const TraceEnvelope = S.Struct({
4
+ traceId: S.String,
5
+ spanId: S.String,
6
+ sampled: S.Boolean,
7
+ })
8
+
9
+ export const toTraceEnvelope = ({ traceId, spanId, sampled }: typeof TraceEnvelope.Type) => ({
10
+ traceId,
11
+ spanId,
12
+ sampled,
13
+ })
14
+
15
+ export const SessionId = S.String.check(S.isUUID()).pipe(S.brand("SessionId"))
16
+
17
+ export const Session = S.Struct({
18
+ id: SessionId,
19
+ trace: TraceEnvelope,
20
+ })
21
+
22
+ export const parent = Effect.currentParentSpan.pipe(
23
+ Effect.catchTags({
24
+ NoSuchElementError: () => Effect.undefined,
25
+ }),
26
+ )
27
+
28
+ export const current = Effect.currentSpan.pipe(
29
+ Effect.catchTags({
30
+ NoSuchElementError: () => Effect.undefined,
31
+ }),
32
+ )
33
+
34
+ export const currentTrace = current.pipe(Effect.map((span) => (span ? toTraceEnvelope(span) : undefined)))
35
+
36
+ export const sessionAttributes = ({ id }: typeof Session.Type) => ({ "liminal.session.id": id })
37
+
38
+ export const sessionLink = (session: typeof Session.Type, attributes: Record<string, unknown> = {}) => ({
39
+ span: Tracer.externalSpan(session.trace),
40
+ attributes: {
41
+ "liminal.link": "session",
42
+ ...sessionAttributes(session),
43
+ ...attributes,
44
+ },
45
+ })
package/dist/Actor.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import { Context, Schema as S } from "effect";
2
- import type { TopFromString } from "./_util/schema.ts";
2
+ import type { TopFromString } from "liminal-util/schema";
3
3
  import type * as ActorClient from "./Client.ts";
4
4
  import type { ClientHandle, Sender } from "./ClientHandle.ts";
5
- import * as Method from "./Method.ts";
6
5
  import { type ProtocolDefinition } from "./Protocol.ts";
7
6
  export declare const TypeId: "~liminal/Actor";
8
7
  export interface Service<ActorSelf, Name extends TopFromString, AttachmentFields extends S.Struct.Fields, D extends ProtocolDefinition> {
@@ -19,8 +18,7 @@ export interface Actor<ActorSelf, ActorId extends string, Name extends TopFromSt
19
18
  new (_: never): Context.ServiceClass.Shape<ActorId, Service<ActorSelf, Name, AttachmentFields, D>>;
20
19
  readonly [TypeId]: typeof TypeId;
21
20
  readonly definition: ActorDefinition<Name, AttachmentFields, ActorClientSelf, ActorClientId, D>;
22
- readonly all: Sender<ActorSelf, D>;
23
- readonly others: Sender<ActorSelf, D>;
24
- readonly handler: <K extends keyof D["methods"], R>(tag: K, f: Method.Handler<D["methods"][K], R>) => Method.Handler<D["methods"][K], R>;
21
+ readonly all: Sender<D, ActorSelf>;
22
+ readonly others: Sender<D, ActorSelf>;
25
23
  }
26
24
  export declare const Service: <ActorSelf>() => <ActorId extends string, Name extends TopFromString, D extends ProtocolDefinition, AttachmentFields extends S.Struct.Fields, ClientSelf, ClientId extends string>(id: ActorId, definition: ActorDefinition<Name, AttachmentFields, ClientSelf, ClientId, D>) => Actor<ActorSelf, ActorId, Name, AttachmentFields, ClientSelf, ClientId, D>;
package/dist/Actor.js CHANGED
@@ -1,32 +1,28 @@
1
1
  import { Context, Schema as S, Effect } from "effect";
2
- import { diagnostic } from "./_diagnostic.js";
3
- import * as Method from "./Method.js";
2
+ import * as Boundary from "liminal-util/Boundary";
4
3
  import {} from "./Protocol.js";
5
- const { span } = diagnostic("Actor");
6
4
  export const TypeId = "~liminal/Actor";
7
5
  export const Service = () => (id, definition) => {
8
6
  const tag = Context.Service()(id);
9
7
  const all = {
10
- send: (key, payload) => tag.asEffect().pipe(Effect.flatMap(({ clients }) => Effect.forEach(clients, (client) => client.send(key, payload), { concurrency: "unbounded" })), span("all.send")),
11
- disconnect: tag.asEffect().pipe(Effect.flatMap(({ clients }) => Effect.forEach(clients, ({ disconnect }) => disconnect)), span("all.disconnect")),
8
+ send: (key, payload) => tag.pipe(Effect.flatMap(({ clients }) => Effect.forEach(clients, (client) => client.send(key, payload), { concurrency: "unbounded" })), Boundary.span("send-all", import.meta.url)),
9
+ disconnect: tag.pipe(Effect.flatMap(({ clients }) => Effect.forEach(clients, ({ disconnect }) => disconnect)), Boundary.span("disconnect-all", import.meta.url)),
12
10
  };
13
11
  const others = {
14
12
  send: Effect.fnUntraced(function* (key, payload) {
15
13
  const { clients, currentClient } = yield* tag;
16
14
  yield* Effect.forEach(clients, (client) => (client === currentClient ? Effect.void : client.send(key, payload)), { concurrency: "unbounded" });
17
- }, span("others.send")),
15
+ }, Boundary.span("send-others", import.meta.url)),
18
16
  disconnect: Effect.gen(function* () {
19
17
  const { clients, currentClient } = yield* tag;
20
18
  yield* Effect.forEach(clients, (client) => (client === currentClient ? Effect.void : client.disconnect));
21
- }).pipe(span("others.disconnect")),
19
+ }).pipe(Boundary.span("disconnect-others", import.meta.url)),
22
20
  };
23
- const handler = (_tag, f) => f;
24
21
  return Object.assign(tag, {
25
22
  [TypeId]: TypeId,
26
23
  definition,
27
24
  all,
28
25
  others,
29
- handler,
30
26
  });
31
27
  };
32
28
  //# sourceMappingURL=Actor.js.map
package/dist/Actor.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Actor.js","sourceRoot":"","sources":["../Actor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAMrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,EAA2B,MAAM,eAAe,CAAA;AAEvD,MAAM,EAAE,IAAI,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC,CAAA;AAEpC,MAAM,CAAC,MAAM,MAAM,GAAG,gBAAyB,CAAA;AAsD/C,MAAM,CAAC,MAAM,OAAO,GAClB,GAAc,EAAE,CAChB,CAQE,EAAW,EACX,UAA4E,EACA,EAAE;IAC9E,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,EAA4D,CAAC,EAAE,CAAC,CAAA;IAE3F,MAAM,GAAG,GAAyB;QAChC,IAAI,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,CACrB,GAAG,CAAC,QAAQ,EAAE,CAAC,IAAI,CACjB,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAC7B,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAC7F,EACD,IAAI,CAAC,UAAU,CAAC,CACjB;QACH,UAAU,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,IAAI,CAC7B,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EACxF,IAAI,CAAC,gBAAgB,CAAC,CACvB;KACF,CAAA;IAED,MAAM,MAAM,GAAyB;QACnC,IAAI,EAAE,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,OAAO;YAC7C,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,KAAK,CAAC,CAAC,GAAG,CAAA;YAC7C,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CACnB,OAAO,EACP,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,EAChF,EAAE,WAAW,EAAE,WAAW,EAAE,CAC7B,CAAA;QACH,CAAC,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QACvB,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC9B,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,KAAK,CAAC,CAAC,GAAG,CAAA;YAC7C,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAA;QAC1G,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;KACnC,CAAA;IAED,MAAM,OAAO,GAAG,CACd,IAAO,EACP,CAAqC,EACD,EAAE,CAAC,CAAC,CAAA;IAE1C,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE;QACxB,CAAC,MAAM,CAAC,EAAE,MAAM;QAChB,UAAU;QACV,GAAG;QACH,MAAM;QACN,OAAO;KACR,CAAC,CAAA;AACJ,CAAC,CAAA"}
1
+ {"version":3,"file":"Actor.js","sourceRoot":"","sources":["../Actor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACrD,OAAO,KAAK,QAAQ,MAAM,uBAAuB,CAAA;AAKjD,OAAO,EAA2B,MAAM,eAAe,CAAA;AAEvD,MAAM,CAAC,MAAM,MAAM,GAAG,gBAAyB,CAAA;AAiD/C,MAAM,CAAC,MAAM,OAAO,GAClB,GAAc,EAAE,CAChB,CAQE,EAAW,EACX,UAA4E,EACA,EAAE;IAC9E,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,EAA4D,CAAC,EAAE,CAAC,CAAA;IAE3F,MAAM,GAAG,GAAyB;QAChC,IAAI,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,CACrB,GAAG,CAAC,IAAI,CACN,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAC7B,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAC7F,EACD,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAC3C;QACH,UAAU,EAAE,GAAG,CAAC,IAAI,CAClB,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EACxF,QAAQ,CAAC,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CACjD;KACF,CAAA;IAED,MAAM,MAAM,GAAyB;QACnC,IAAI,EAAE,MAAM,CAAC,UAAU,CACrB,QAAQ,CAAC,EAAE,GAAG,EAAE,OAAO;YACrB,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,KAAK,CAAC,CAAC,GAAG,CAAA;YAC7C,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CACnB,OAAO,EACP,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,EAChF,EAAE,WAAW,EAAE,WAAW,EAAE,CAC7B,CAAA;QACH,CAAC,EACD,QAAQ,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAC9C;QACD,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC9B,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,KAAK,CAAC,CAAC,GAAG,CAAA;YAC7C,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAA;QAC1G,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;KAC7D,CAAA;IAED,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE;QACxB,CAAC,MAAM,CAAC,EAAE,MAAM;QAChB,UAAU;QACV,GAAG;QACH,MAAM;KACP,CAAC,CAAA;AACJ,CAAC,CAAA"}
@@ -0,0 +1,12 @@
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
+ import type { Send } from "./ClientHandle.ts";
6
+ import type { Methods } from "./Method.ts";
7
+ import type { ProtocolDefinition } from "./Protocol.ts";
8
+ export interface ActorHandle<NamespaceSelf, Internal extends Methods, Name extends TopFromString, AttachmentFields extends S.Struct.Fields, D extends ProtocolDefinition> {
9
+ readonly upgrade: (attachments: S.Struct<AttachmentFields>["Type"]) => Effect.Effect<HttpServerResponse.HttpServerResponse, S.SchemaError | Encoding.EncodingError | Cause.NoSuchElementError, NamespaceSelf | NativeRequest.NativeRequest | Name["EncodingServices"] | S.Struct<AttachmentFields>["EncodingServices"]>;
10
+ readonly call: <K extends keyof Internal, M extends Internal[K]>(method: K, payload: M["payload"]["Type"]) => Effect.Effect<M["success"]["Type"], M["failure"]["Type"], NamespaceSelf>;
11
+ readonly proxySendAll: Send<D, NamespaceSelf>;
12
+ }