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,134 @@
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 } from "liminal-util/schema";
6
+ import * as ClientDirectory from "./ClientDirectory.js";
7
+ import * as Method from "./Method.js";
8
+ import * as Tracing from "./Tracing.js";
9
+ export const make = Effect.fnUntraced(function* ({ actor, handlers, hydrate, introductions, }) {
10
+ const { definition: { client: { protocol: P, key: expected }, name: Name, }, } = actor;
11
+ const { Client: ClientM } = P;
12
+ const decodeClient = decodeJsonString(ClientM);
13
+ const encodeAuditionSuccess = encodeJsonString(P.Audition.Success);
14
+ const encodeAuditionFailure = encodeJsonString(P.Audition.Failure);
15
+ const encodeFSuccess = encodeJsonString(P.F.Success);
16
+ const encodeFFailure = encodeJsonString(P.F.Failure);
17
+ const encodeEvent = encodeJsonString(P.Event);
18
+ const entries = {};
19
+ const transport = {
20
+ key: ({ port }) => port,
21
+ send: ({ backing }, event) => {
22
+ const { _tag } = event.event;
23
+ return Effect.gen(function* () {
24
+ const trace = yield* Tracing.currentTrace;
25
+ yield* backing.send(0, yield* encodeEvent({ ...event, ...(trace && { trace }) }).pipe(Effect.catchTags({
26
+ SchemaError: Effect.die,
27
+ })));
28
+ }).pipe(Boundary.span("send", import.meta.url, { attributes: { _tag }, kind: "producer" }));
29
+ },
30
+ close: ({ close }) => close,
31
+ snapshot: () => Effect.void,
32
+ };
33
+ const useEntries = yield* Semaphore.make(1).pipe(Effect.map((v) => v.withPermits(1)));
34
+ const getEntry = Effect.fnUntraced(function* (key) {
35
+ const existing = entries[key];
36
+ if (existing)
37
+ return existing;
38
+ const directory = ClientDirectory.make(actor, { transport });
39
+ const semaphore = yield* Semaphore.make(1);
40
+ const fresh = {
41
+ directory,
42
+ mutex: semaphore.withPermits(1),
43
+ };
44
+ entries[key] = fresh;
45
+ return fresh;
46
+ }, useEntries);
47
+ const outerScope = yield* Scope.Scope;
48
+ yield* introductions.pipe(Stream.runForEach(Effect.fnUntraced(function* ({ name, port, attachments }) {
49
+ const stateRef = yield* Ref.make(Option.none());
50
+ const scope = yield* Scope.fork(outerScope, "sequential");
51
+ const closeScope = Scope.close(scope, Exit.void);
52
+ const backing = yield* BrowserWorkerRunner.make(port).start();
53
+ yield* Scope.addFinalizer(scope, Effect.gen(function* () {
54
+ const state = yield* Ref.get(stateRef);
55
+ if (state._tag === "Some") {
56
+ const { key, entry: { directory }, } = state.value;
57
+ yield* directory.unregister(port);
58
+ if (directory.handles.size === 0) {
59
+ delete entries[key];
60
+ }
61
+ }
62
+ }).pipe(useEntries));
63
+ yield* backing
64
+ .run(Effect.fnUntraced(function* (_portId, raw) {
65
+ const state = yield* Ref.get(stateRef);
66
+ const message = yield* decodeClient(raw);
67
+ if (state._tag === "None") {
68
+ if (message._tag !== "Audition.Payload") {
69
+ return yield* Effect.die(undefined);
70
+ }
71
+ const { client: actual } = message;
72
+ if (actual !== expected) {
73
+ yield* backing.send(0, yield* encodeAuditionFailure({
74
+ _tag: "Audition.Failure",
75
+ expected,
76
+ actual,
77
+ }));
78
+ return yield* closeScope;
79
+ }
80
+ const key = yield* S.encodeEffect(Name)(name);
81
+ const entry = yield* getEntry(key);
82
+ const currentClient = yield* entry.directory.register({ port, backing, close: closeScope }, attachments);
83
+ const ActorLive = Layer.succeed(actor, {
84
+ name,
85
+ clients: entry.directory.handles,
86
+ currentClient,
87
+ });
88
+ yield* Ref.set(stateRef, Option.some({ key, entry, currentClient, ActorLive }));
89
+ const initial = yield* hydrate.pipe(entry.mutex, Effect.scoped, Boundary.span("onConnect", import.meta.url), Effect.provide(ActorLive));
90
+ return yield* backing.send(0, yield* encodeAuditionSuccess({ _tag: "Audition.Success", initial }));
91
+ }
92
+ const { entry, ActorLive } = state.value;
93
+ if (message._tag === "Audition.Payload") {
94
+ return yield* Effect.die(undefined);
95
+ }
96
+ if (message._tag === "Disconnect") {
97
+ return yield* closeScope;
98
+ }
99
+ const { id, payload } = message;
100
+ const { _tag, value } = payload;
101
+ const parent = message.trace && Tracer.externalSpan(message.trace);
102
+ const transportSpan = yield* Tracing.parent;
103
+ yield* handlers[_tag](value).pipe(Effect.matchEffect({
104
+ onSuccess: (value) => encodeFSuccess({
105
+ _tag: "F.Success",
106
+ id,
107
+ success: { _tag, value },
108
+ }),
109
+ onFailure: (value) => encodeFFailure({
110
+ _tag: "F.Failure",
111
+ id,
112
+ failure: { _tag, value },
113
+ }),
114
+ }), Effect.andThen((v) => backing.send(0, v)), Boundary.span("handle", import.meta.url, {
115
+ attributes: { _tag },
116
+ kind: "server",
117
+ parent,
118
+ links: parent && transportSpan
119
+ ? [
120
+ {
121
+ span: transportSpan,
122
+ attributes: {
123
+ "liminal.link": "transport",
124
+ "liminal.transport": "worker",
125
+ },
126
+ },
127
+ ]
128
+ : undefined,
129
+ }), Effect.scoped, Effect.provide(ActorLive), entry.mutex);
130
+ }, Boundary.span("message", import.meta.url)))
131
+ .pipe(Effect.andThen(closeScope), Effect.catchCause((cause) => Effect.logError(cause).pipe(Effect.andThen(closeScope))), Boundary.span("backing", import.meta.url), Effect.forkScoped, Scope.provide(scope));
132
+ })));
133
+ }, Boundary.span("make", import.meta.url));
134
+ //# sourceMappingURL=BrowserActorNamespace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BrowserActorNamespace.js","sourceRoot":"","sources":["../BrowserActorNamespace.ts"],"names":[],"mappings":"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"}
package/dist/Client.d.ts CHANGED
@@ -2,40 +2,50 @@ import { Context, Effect, Layer, RcRef, Scope, Stream, Schema as S } from "effec
2
2
  import { Socket } from "effect/unstable/socket";
3
3
  import { Worker } from "effect/unstable/workers";
4
4
  import { type ClientError } from "./errors.ts";
5
- import { type F } from "./F.ts";
5
+ import type { Fn, FnNoSelf } from "./Fn.ts";
6
6
  import { Protocol, type ProtocolDefinition } from "./Protocol.ts";
7
+ import * as Reducer from "./Reducer.ts";
7
8
  export declare const TypeId: "~liminal/Client";
8
9
  export interface ReplayConfig {
9
10
  readonly mode: "startup" | "all-subscribers";
10
11
  readonly limit?: number | undefined;
11
12
  }
12
- export interface Session<Self, D extends ProtocolDefinition> {
13
+ export type Service<ClientSelf, D extends ProtocolDefinition> = RcRef.RcRef<{
14
+ readonly state: Stream.Stream<S.Struct<D["state"]>["Type"], ClientError | S.SchemaError>;
13
15
  readonly events: Stream.Stream<ReturnType<typeof S.TaggedUnion<D["events"]>>["Type"], ClientError | S.SchemaError>;
14
- readonly f: F<Self, D>;
16
+ readonly fnRaw: <K extends keyof D["external"], M extends D["external"][K]>(tag: K, payload: M["payload"]["Type"]) => Effect.Effect<M["success"]["Type"], M["failure"]["Type"], ClientSelf>;
15
17
  readonly end: Effect.Effect<void>;
16
- }
17
- export type Service<ClientSelf, D extends ProtocolDefinition> = RcRef.RcRef<Session<ClientSelf, D>, ClientError>;
18
- export interface Client<Self, ClientId extends string, D extends ProtocolDefinition> extends Context.Service<Self, Service<Self, D>> {
19
- new (_: never): Context.ServiceClass.Shape<ClientId, Service<Self, D>>;
18
+ }, ClientError>;
19
+ export interface Client<Self, Id extends string, D extends ProtocolDefinition> extends Context.Service<Self, Service<Self, D>> {
20
+ new (_: never): Context.ServiceClass.Shape<Id, Service<Self, D>> & {
21
+ readonly State: S.Struct<D["state"]>["Type"];
22
+ };
20
23
  readonly [TypeId]: typeof TypeId;
21
24
  readonly definition: D;
22
25
  readonly protocol: Protocol<D>;
26
+ readonly state: Stream.Stream<S.Struct<D["state"]>["Type"], ClientError | S.SchemaError, Self | S.Struct<D["state"]>["DecodingServices"]>;
23
27
  readonly events: Stream.Stream<ReturnType<typeof S.TaggedUnion<D["events"]>>["Type"], ClientError | S.SchemaError, Self>;
24
- readonly f: F<Self, D>;
28
+ readonly fn: Fn<Self, D["external"]>;
25
29
  readonly invalidate: Effect.Effect<void, never, Self>;
30
+ readonly reducer: <K extends keyof D["events"], R extends Reducer.Reducer<D, K>>(_tag: K, f: R) => R;
26
31
  }
32
+ export declare const fn: <ClientSelf, D extends ProtocolDefinition>(service: Service<ClientSelf, D>) => FnNoSelf<D["external"]>;
27
33
  export declare const Service: <Self>() => <Id extends string, D extends ProtocolDefinition>(id: Id, definition: D) => Client<Self, Id, D>;
28
- export interface ClientTransport<D extends ProtocolDefinition> {
29
- readonly listen: (publish: (message: Protocol<D>["Actor"]["Type"]) => Effect.Effect<void, ClientError>) => Effect.Effect<void, ClientError | S.SchemaError, Scope.Scope | Protocol<D>["Actor"]["DecodingServices"]>;
34
+ export interface ClientTransport<D extends ProtocolDefinition, R> {
35
+ readonly listen: (publish: (message: Protocol<D>["Actor"]["Type"]) => Effect.Effect<void, ClientError, R>) => Effect.Effect<void, ClientError | S.SchemaError, Scope.Scope | Protocol<D>["Actor"]["DecodingServices"] | R>;
30
36
  readonly send: (message: Protocol<D>["F"]["Payload"]["Type"]) => Effect.Effect<void, ClientError | S.SchemaError, Protocol<D>["F"]["Payload"]["EncodingServices"]>;
31
37
  }
32
- export declare const layerSocket: <Self, Id extends string, D extends ProtocolDefinition>({ client, url, protocols, replay, }: {
38
+ export declare const layerSocket: <Self, Id extends string, D extends ProtocolDefinition, Reducers extends Reducer.Reducers<D>, CR = never>({ client, reducers, url, protocols, replay, onConnect, }: {
33
39
  readonly client: Client<Self, Id, D>;
34
- readonly url?: string | undefined;
35
- readonly protocols?: string | Array<string> | undefined;
40
+ readonly reducers: Reducers;
36
41
  readonly replay?: ReplayConfig | undefined;
37
- }) => Layer.Layer<Self, never, Socket.WebSocketConstructor | Protocol<D>["Actor"]["DecodingServices"] | Protocol<D>["F"]["Payload"]["EncodingServices"]>;
38
- export declare const layerWorker: <Self, Id extends string, D extends ProtocolDefinition, T extends Protocol<D>>({ client, replay, }: {
42
+ readonly onConnect?: undefined | ((state: S.Struct<D["state"]>["Type"]) => Effect.Effect<void, never, CR>);
43
+ readonly protocols?: string | Array<string> | undefined;
44
+ readonly url?: string | undefined;
45
+ }) => Layer.Layer<Self, never, Socket.WebSocketConstructor | Protocol<D>["Actor"]["DecodingServices"] | Protocol<D>["F"]["Payload"]["EncodingServices"] | Reducer.Reducers.Services<Self, Reducers> | CR>;
46
+ export declare const layerWorker: <Self, Id extends string, D extends ProtocolDefinition, Reducers extends Reducer.Reducers<D>, T extends Protocol<D>, CR = never>({ client, reducers, replay, onConnect, }: {
39
47
  readonly client: Client<Self, Id, D>;
48
+ readonly reducers: Reducers;
40
49
  readonly replay?: ReplayConfig | undefined;
41
- }) => Layer.Layer<Self, never, Worker.WorkerPlatform | Worker.Spawner | T["Actor"]["DecodingServices"] | T["F"]["Payload"]["EncodingServices"]>;
50
+ readonly onConnect?: undefined | ((state: S.Struct<D["state"]>["Type"]) => Effect.Effect<void, never, CR>);
51
+ }) => Layer.Layer<Self, never, Worker.WorkerPlatform | Worker.Spawner | T["Actor"]["DecodingServices"] | T["Client"]["EncodingServices"] | Reducer.Reducers.Services<Self, Reducers>>;
package/dist/Client.js CHANGED
@@ -1,41 +1,50 @@
1
- import { Context, Encoding, Deferred, Effect, Layer, Option, PubSub, RcRef, Record, pipe, Ref, Scope, Stream, Take, Schema as S, Array, Struct, Fiber, Exit, Cause, Result, flow, } from "effect";
1
+ import { Context, Encoding, Deferred, Effect, Layer, Option, PubSub, RcRef, Record, pipe, Ref, Scope, Stream, Take, Schema as S, Array, Fiber, Exit, Cause, Result, flow, Tracer, identity, Semaphore, } from "effect";
2
2
  import { Socket } from "effect/unstable/socket";
3
3
  import { Worker } from "effect/unstable/workers";
4
- import { diagnostic } from "./_diagnostic.js";
5
- import { decodeJsonString, encodeJsonString } from "./_util/schema.js";
4
+ import * as Boundary from "liminal-util/Boundary";
5
+ import { decodeJsonString, encodeJsonString } from "liminal-util/schema";
6
6
  import { AuditionError, ConnectionError, UnresolvedError } from "./errors.js";
7
- import {} from "./F.js";
8
7
  import { Protocol } from "./Protocol.js";
9
- const { debug, span } = diagnostic("Client");
8
+ import * as Reducer from "./Reducer.js";
9
+ import * as Tracing from "./Tracing.js";
10
10
  export const TypeId = "~liminal/Client";
11
+ export const fn = (service) => ((_tag, ...f) => Effect.fnUntraced(function* (payload) {
12
+ const { fnRaw: fn } = yield* RcRef.get(service);
13
+ return yield* fn(_tag, payload);
14
+ }, Effect.scoped, ...f));
11
15
  export const Service = () => (id, definition) => {
12
16
  const tag = Context.Service()(id);
13
17
  const protocol = Protocol(definition);
14
- const events = tag.asEffect().pipe(Effect.flatMap(RcRef.get), Effect.map(Struct.get("events")), Stream.unwrap);
15
- const f = (_tag) => Effect.fnUntraced(function* (value) {
16
- const { f } = yield* tag.asEffect().pipe(Effect.flatMap(RcRef.get));
17
- return yield* f(_tag)(value);
18
- }, Effect.scoped);
19
- const invalidate = tag.asEffect().pipe(Effect.flatMap((rc) => RcRef.get(rc).pipe(Effect.flatMap(({ end }) => end), Effect.andThen(RcRef.invalidate(rc)))), Effect.scoped, Effect.ignore);
18
+ const state = tag.pipe(Effect.flatMap(RcRef.get), Effect.map(({ state }) => state), Stream.unwrap);
19
+ const events = tag.pipe(Effect.flatMap(RcRef.get), Effect.map(({ events }) => events), Stream.unwrap);
20
+ const fn = ((_tag, ...f) => Effect.fnUntraced(function* (payload) {
21
+ const { fnRaw: fn } = yield* tag.pipe(Effect.flatMap(RcRef.get));
22
+ return yield* fn(_tag, payload);
23
+ }, Effect.scoped, ...f));
24
+ const invalidate = tag.pipe(Effect.flatMap((rc) => RcRef.get(rc).pipe(Effect.flatMap(({ end }) => end), Effect.andThen(RcRef.invalidate(rc)))), Effect.scoped, Effect.ignore);
25
+ const reducer = (_event, f) => f;
20
26
  return Object.assign(tag, {
21
27
  [TypeId]: TypeId,
22
28
  definition,
23
29
  protocol,
30
+ state,
24
31
  events,
25
- f,
32
+ fn,
26
33
  invalidate,
34
+ reducer,
27
35
  });
28
36
  };
29
- const make = (client, build, replay) => Effect.gen(function* () {
37
+ const make = ({ build, client, reducers, onConnect, replay, }) => Effect.gen(function* () {
30
38
  const rcr = yield* RcRef.make({
31
39
  acquire: Effect.gen(function* () {
32
- yield* debug("AcquisitionStarted");
33
40
  const { listen, send } = yield* build;
34
41
  const audition = yield* Deferred.make();
42
+ const stateDeferred = yield* Deferred.make();
35
43
  const inflights = {};
36
44
  let callId = 0;
37
45
  let takeCount = 0;
38
- const pubsub = yield* PubSub.unbounded();
46
+ const eventsPubsub = yield* PubSub.unbounded();
47
+ const statePubsub = yield* PubSub.unbounded({ replay: 1 });
39
48
  const replayState = yield* Ref.make({
40
49
  startupOpen: true,
41
50
  buffer: [],
@@ -57,64 +66,95 @@ const make = (client, build, replay) => Effect.gen(function* () {
57
66
  return { startupOpen, buffer };
58
67
  });
59
68
  }
60
- yield* PubSub.publish(pubsub, eventTake);
69
+ yield* PubSub.publish(eventsPubsub, eventTake);
61
70
  });
62
71
  const outer = yield* Scope.Scope;
63
72
  const scope = yield* Scope.fork(outer, "sequential");
64
73
  const end = Scope.close(scope, Exit.void);
74
+ const reduceMutex = yield* Semaphore.make(1);
75
+ const reduceTask = Semaphore.withPermits(reduceMutex, 1);
65
76
  const fiber = yield* listen(Effect.fnUntraced(function* (message) {
66
77
  switch (message._tag) {
67
78
  case "Audition.Success": {
68
- yield* debug("Audition.Succeeded");
79
+ const { initial } = message;
80
+ yield* PubSub.publish(statePubsub, initial);
81
+ const state = yield* Ref.make(initial);
82
+ yield* Deferred.succeed(stateDeferred, state);
69
83
  yield* Deferred.succeed(audition, void 0);
84
+ yield* onConnect?.(initial) ?? Effect.void;
70
85
  return;
71
86
  }
72
87
  case "Audition.Failure": {
73
88
  const { expected, actual } = message;
74
- yield* debug("Audition.Failed", { expected, actual });
75
89
  return yield* new AuditionError({ value: { expected, actual } });
76
90
  }
77
91
  case "Event": {
78
92
  const { event } = message;
79
- yield* debug("Event.Emitted", { event });
80
- yield* publishTake([event], true);
93
+ const { _tag } = event;
94
+ const reducer = reducers[_tag];
95
+ const state = yield* Deferred.await(stateDeferred);
96
+ yield* Effect.gen(function* () {
97
+ const current = yield* Ref.get(state);
98
+ const reduced = yield* reducer(event)(current).pipe(Effect.provideService(client, rcr),
99
+ // TODO: rework error-handling
100
+ Effect.catchDefect(() => Effect.succeed(undefined)));
101
+ if (reduced) {
102
+ yield* PubSub.publish(statePubsub, reduced);
103
+ yield* Ref.set(state, reduced);
104
+ }
105
+ }).pipe(reduceTask);
106
+ const parent = message.trace ? Tracer.externalSpan(message.trace) : undefined;
107
+ yield* publishTake([event], true).pipe(Boundary.span("enqueue-event", import.meta.url, {
108
+ attributes: { _tag },
109
+ kind: "consumer",
110
+ parent,
111
+ }));
81
112
  return;
82
113
  }
83
114
  case "F.Success":
84
115
  case "F.Failure": {
85
116
  const { id } = message;
86
- const deferred = inflights[id];
87
- if (deferred) {
117
+ const inflight = inflights[id];
118
+ if (inflight) {
88
119
  delete inflights[id];
89
- switch (message._tag) {
90
- case "F.Success": {
91
- const { _tag, value } = message.success;
92
- yield* debug("Call.Succeeded", { id, _tag, value });
93
- yield* Deferred.succeed(deferred, value);
94
- return;
95
- }
96
- case "F.Failure": {
97
- const { _tag, value } = message.failure;
98
- yield* debug("Call.Failed", { id, _tag, value });
99
- yield* Deferred.fail(deferred, value);
100
- return;
120
+ return yield* Effect.gen(function* () {
121
+ switch (message._tag) {
122
+ case "F.Success": {
123
+ const { value } = message.success;
124
+ yield* Deferred.succeed(inflight.deferred, value);
125
+ return;
126
+ }
127
+ case "F.Failure": {
128
+ const { _tag, value } = message.failure;
129
+ yield* Effect.annotateLogs(Effect.logDebug("Call.Failed"), { id, _tag });
130
+ yield* Deferred.fail(inflight.deferred, value);
131
+ return;
132
+ }
101
133
  }
102
- }
134
+ }).pipe(inflight.span ? Effect.withParentSpan(inflight.span, { captureStackTrace: false }) : identity);
103
135
  }
104
136
  return;
105
137
  }
106
138
  case "Disconnect": {
107
- yield* debug("Disconnected");
108
139
  return;
109
140
  }
110
141
  }
111
142
  })).pipe(Effect.ensuring(Effect.all([
112
- debug("Client.Closed", { unresolved: Record.keys(inflights).length }),
143
+ Effect.sync(() => Record.keys(inflights).length).pipe(Effect.flatMap((unresolved) => unresolved === 0
144
+ ? Effect.void
145
+ : Effect.annotateLogs(Effect.logDebug("Client.Closed"), { unresolved }))),
113
146
  Deferred.succeed(audition, void 0),
114
147
  RcRef.invalidate(rcr),
115
148
  ], { concurrency: "unbounded" })), Effect.forkScoped, Effect.provideService(Scope.Scope, scope));
149
+ const interrupt = Stream.interruptWhen(Fiber.await(fiber).pipe(Effect.flatMap(Exit.match({
150
+ onSuccess: () => Effect.void,
151
+ onFailure: flow(Cause.findError, Result.match({
152
+ onSuccess: Effect.fail,
153
+ onFailure: () => Effect.void,
154
+ })),
155
+ }))));
116
156
  const events = Effect.gen(function* () {
117
- const queue = yield* PubSub.subscribe(pubsub);
157
+ const queue = yield* PubSub.subscribe(eventsPubsub);
118
158
  const live = (replayCount) => Stream.fromSubscription(queue).pipe(Stream.filter((entry) => entry.seq > replayCount), Stream.map((entry) => entry.take), Stream.flattenTake);
119
159
  if (!replay) {
120
160
  return live(-1);
@@ -134,16 +174,11 @@ const make = (client, build, replay) => Effect.gen(function* () {
134
174
  return buffer.length === 0
135
175
  ? live(replayCount)
136
176
  : Stream.concat(Stream.fromIterable(buffer).pipe(Stream.map((entry) => entry.take), Stream.flattenTake), live(replayCount));
137
- }).pipe(Stream.unwrap, Stream.interruptWhen(Fiber.await(fiber).pipe(Effect.flatMap(Exit.match({
138
- onSuccess: () => Effect.void,
139
- onFailure: flow(Cause.findError, Result.match({
140
- onSuccess: Effect.fail,
141
- onFailure: () => Effect.void,
142
- })),
143
- })))));
144
- yield* Deferred.await(audition);
177
+ }).pipe(Stream.unwrap, interrupt);
178
+ const state = Stream.fromPubSub(statePubsub).pipe(interrupt);
145
179
  const encodingServices = yield* Effect.context();
146
- const f = (_tag) => Effect.fnUntraced(function* (value) {
180
+ yield* Deferred.await(audition);
181
+ const fnRaw = (_tag, value) => Effect.gen(function* () {
147
182
  const exit = fiber.pollUnsafe();
148
183
  if (exit) {
149
184
  return yield* Exit.match(exit, {
@@ -155,82 +190,124 @@ const make = (client, build, replay) => Effect.gen(function* () {
155
190
  });
156
191
  }
157
192
  const id = callId++;
158
- const inflight = yield* Deferred.make();
159
- inflights[id] = inflight;
193
+ const deferred = yield* Deferred.make();
194
+ const span = yield* Tracing.current;
195
+ const trace = span ? Tracing.toTraceEnvelope(span) : undefined;
196
+ inflights[id] = { deferred, span };
160
197
  yield* send({
161
198
  _tag: "F.Payload",
162
199
  id,
163
200
  payload: { _tag, value },
201
+ ...(trace && { trace }),
164
202
  });
165
- return yield* Effect.raceFirst(Deferred.await(inflight), Fiber.await(fiber).pipe(Effect.flatMap((exit) => Exit.match(exit, {
166
- onSuccess: () => new UnresolvedError().asEffect(),
203
+ return yield* Effect.raceFirst(Deferred.await(deferred), Fiber.await(fiber).pipe(Effect.flatMap((exit) => Exit.match(exit, {
204
+ onSuccess: () => new UnresolvedError(),
167
205
  onFailure: flow(Cause.findError, Result.match({
168
206
  onSuccess: Effect.fail,
169
- onFailure: () => new UnresolvedError().asEffect(),
207
+ onFailure: () => new UnresolvedError(),
170
208
  })),
171
209
  }))));
172
- }, span("f"), Effect.scoped, Effect.provide(encodingServices));
173
- return { events, f, end };
174
- }).pipe(span("acquire", { attributes: { client: client.key } }), Effect.annotateLogs("client", client.key)),
210
+ }).pipe(Boundary.span("fn", import.meta.url, {
211
+ kind: "client",
212
+ attributes: { _tag },
213
+ }), Effect.provide(encodingServices));
214
+ return { state, events, fnRaw, end };
215
+ }).pipe(Boundary.span("acquire", import.meta.url, {
216
+ attributes: { client: client.key },
217
+ }), Effect.annotateLogs("client", client.key)),
175
218
  });
176
219
  return rcr;
177
220
  }).pipe(Layer.effect(client));
178
- export const layerSocket = ({ client, url, protocols, replay, }) => {
221
+ let clientId_;
222
+ const clientId = () => {
223
+ if (!clientId_) {
224
+ clientId_ = crypto.randomUUID();
225
+ }
226
+ return clientId_;
227
+ };
228
+ export const layerSocket = ({ client, reducers, url, protocols, replay, onConnect, }) => {
179
229
  const { F, Actor } = client.protocol;
180
230
  const encodeFPayload = encodeJsonString(F.Payload);
181
231
  const decodeActor = decodeJsonString(Actor);
182
- return make(client, Effect.gen(function* () {
183
- const socket = yield* Socket.makeWebSocket(url ?? "/", {
184
- protocols: ["liminal", Encoding.encodeBase64Url(client.key), ...(protocols ? Array.ensure(protocols) : [])],
185
- });
186
- return {
187
- listen: Effect.fnUntraced(function* (publish) {
188
- yield* socket
189
- .runRaw((raw) => pipe(raw instanceof Uint8Array ? new TextDecoder().decode(raw) : raw, decodeActor, Effect.andThen(publish)))
190
- .pipe(Effect.catchIf(Socket.isSocketError, Effect.fnUntraced(function* (cause) {
191
- const { reason } = cause;
192
- if (reason._tag === "SocketCloseError" && reason.code === 1000) {
193
- yield* debug("Socket.Disconnected");
194
- return yield* publish({ _tag: "Disconnect" });
195
- }
196
- yield* debug(`SocketErrored.${reason._tag}`, { cause });
197
- return yield* new ConnectionError({ cause });
198
- })));
199
- }, span("listen")),
200
- send: Effect.fnUntraced(function* (v) {
201
- const write = yield* socket.writer;
202
- const message = yield* encodeFPayload(v);
203
- yield* write(message).pipe(Effect.catchTag("SocketError", (cause) => new ConnectionError({ cause }).asEffect()));
204
- }, span("send"), Effect.scoped),
205
- };
206
- }), replay);
232
+ return make({
233
+ client,
234
+ reducers,
235
+ onConnect,
236
+ replay,
237
+ build: Effect.gen(function* () {
238
+ const socket = yield* Socket.makeWebSocket(url ?? "/", {
239
+ protocols: [
240
+ "liminal",
241
+ clientId(),
242
+ Encoding.encodeBase64Url(client.key),
243
+ ...(protocols ? Array.ensure(protocols) : []),
244
+ ],
245
+ });
246
+ return {
247
+ listen: Effect.fnUntraced(function* (publish) {
248
+ yield* socket
249
+ .runRaw((raw) => pipe(raw instanceof Uint8Array ? new TextDecoder().decode(raw) : raw, decodeActor, Effect.andThen(publish)))
250
+ .pipe(Effect.catchIf(Socket.isSocketError, Effect.fnUntraced(function* (cause) {
251
+ const { reason } = cause;
252
+ if (reason._tag === "SocketCloseError" && reason.code === 1000) {
253
+ return yield* publish({ _tag: "Disconnect" });
254
+ }
255
+ yield* Effect.annotateLogs(Effect.logDebug(`SocketErrored.${reason._tag}`), { cause });
256
+ return yield* new ConnectionError({ cause });
257
+ })));
258
+ }, Boundary.span("listen", import.meta.url)),
259
+ send: Effect.fnUntraced(function* (v) {
260
+ const write = yield* socket.writer;
261
+ const message = yield* encodeFPayload(v);
262
+ yield* write(message).pipe(Effect.catchTags({
263
+ SocketError: (cause) => new ConnectionError({ cause }),
264
+ }));
265
+ }, Boundary.span("send", import.meta.url), Effect.scoped),
266
+ };
267
+ }),
268
+ });
269
+ };
270
+ export const layerWorker = ({ client, reducers, replay, onConnect, }) => {
271
+ const { Actor, Client: ClientM } = client.protocol;
272
+ const encodeClient = encodeJsonString(ClientM);
273
+ const decodeActor = decodeJsonString(Actor);
274
+ return make({
275
+ client,
276
+ reducers,
277
+ onConnect,
278
+ replay,
279
+ build: Effect.gen(function* () {
280
+ const platform = yield* Worker.WorkerPlatform;
281
+ const backing = yield* platform.spawn(0).pipe(Effect.catchTags({
282
+ WorkerError: (cause) => new ConnectionError({ cause }),
283
+ }));
284
+ const send = (message) => encodeClient(message).pipe(Effect.flatMap((encoded) => backing.send(encoded)), Effect.catchTags({
285
+ WorkerError: (cause) => new ConnectionError({ cause }),
286
+ }), Boundary.span("send", import.meta.url));
287
+ return {
288
+ listen: Effect.fnUntraced(function* (publish) {
289
+ const stop = yield* Deferred.make();
290
+ const audition = yield* encodeClient({
291
+ _tag: "Audition.Payload",
292
+ client: client.key,
293
+ });
294
+ yield* backing
295
+ .run(Effect.fnUntraced(function* (raw) {
296
+ const message = yield* decodeActor(raw);
297
+ yield* publish(message);
298
+ if (message._tag === "Disconnect" || message._tag === "Audition.Failure") {
299
+ yield* Deferred.succeed(stop, void 0);
300
+ }
301
+ }), {
302
+ onSpawn: backing.send(audition).pipe(Effect.orDie),
303
+ })
304
+ .pipe(Effect.raceFirst(Deferred.await(stop)), Effect.catchTags({
305
+ WorkerError: (cause) => new ConnectionError({ cause }),
306
+ }));
307
+ }, Boundary.span("listen", import.meta.url)),
308
+ send,
309
+ };
310
+ }),
311
+ });
207
312
  };
208
- export const layerWorker = ({ client, replay, }) => make(client, Effect.gen(function* () {
209
- const platform = yield* Worker.WorkerPlatform;
210
- const backing = yield* platform
211
- .spawn(0)
212
- .pipe(Effect.catchTag("WorkerError", (cause) => new ConnectionError({ cause }).asEffect()));
213
- const send = (message) => backing.send(message).pipe(Effect.catchTag("WorkerError", (cause) => new ConnectionError({ cause }).asEffect()), span("send"));
214
- return {
215
- listen: Effect.fnUntraced(function* (publish) {
216
- const stop = yield* Deferred.make();
217
- yield* backing
218
- .run(Effect.fnUntraced(function* (message) {
219
- yield* publish(message);
220
- if (message._tag === "Disconnect" || message._tag === "Audition.Failure") {
221
- yield* Deferred.succeed(stop, void 0);
222
- }
223
- }), {
224
- onSpawn: backing
225
- .send({
226
- _tag: "Audition.Payload",
227
- client: client.key,
228
- })
229
- .pipe(Effect.orDie),
230
- })
231
- .pipe(Effect.raceFirst(Deferred.await(stop)), Effect.catchTag("WorkerError", (cause) => new ConnectionError({ cause }).asEffect()));
232
- }, span("listen")),
233
- send,
234
- };
235
- }), replay);
236
313
  //# sourceMappingURL=Client.js.map