56 lines · 2.0 KB · TypeScript
RawDownload
import type { ServerLifecycleStreamEvent } from "@t3tools/contracts";import * as Context from "effect/Context";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as PubSub from "effect/PubSub";import * as Ref from "effect/Ref";import * as Stream from "effect/Stream"; type LifecycleEventInput =  | Omit<Extract<ServerLifecycleStreamEvent, { type: "welcome" }>, "sequence">  | Omit<Extract<ServerLifecycleStreamEvent, { type: "ready" }>, "sequence">  | Omit<Extract<ServerLifecycleStreamEvent, { type: "legacyThreadMigration" }>, "sequence">; interface SnapshotState {  readonly sequence: number;  readonly events: ReadonlyArray<ServerLifecycleStreamEvent>;} export class ServerLifecycleEvents extends Context.Service<  ServerLifecycleEvents,  {    readonly publish: (event: LifecycleEventInput) => Effect.Effect<ServerLifecycleStreamEvent>;    readonly snapshot: Effect.Effect<SnapshotState>;    readonly stream: Stream.Stream<ServerLifecycleStreamEvent>;  }>()("t3/serverLifecycleEvents") {} const make = Effect.gen(function* () {  const pubsub = yield* PubSub.unbounded<ServerLifecycleStreamEvent>();  const state = yield* Ref.make<SnapshotState>({    sequence: 0,    events: [],  });   return {    publish: (event) =>      Ref.modify(state, (current) => {        const nextSequence = current.sequence + 1;        const nextEvent = {          ...event,          sequence: nextSequence,        } satisfies ServerLifecycleStreamEvent;        const nextEvents = [          nextEvent,          ...current.events.filter((entry) => entry.type !== nextEvent.type),        ];        return [nextEvent, { sequence: nextSequence, events: nextEvents }] as const;      }).pipe(Effect.tap((event) => PubSub.publish(pubsub, event))),    snapshot: Ref.get(state),    get stream() {      return Stream.fromPubSub(pubsub);    },  } satisfies ServerLifecycleEvents["Service"];}); export const layer = Layer.effect(ServerLifecycleEvents, make);