forked from pingdotgg/t3code
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProviderInstanceRegistry.ts
More file actions
87 lines (84 loc) · 4.03 KB
/
Copy pathProviderInstanceRegistry.ts
File metadata and controls
87 lines (84 loc) · 4.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
* ProviderInstanceRegistry — the single Effect service in the new model.
*
* Owns a `Map<ProviderInstanceId, ProviderInstance>` produced by running
* registered driver factories against `ServerSettings.providerInstances`.
* The registry watches settings; when an instance's config changes (or
* the entry disappears), the registry tears down the affected instance's
* scope and rebuilds — that's the entire hot-reload story.
*
* What rest-of-server reads from here:
* - `getInstance(instanceId)` — for routing turn/session calls.
* - `listInstances` — for snapshot aggregation in `ProviderRegistry`.
* - `listUnavailable` — `ServerProvider` shadows for instances whose
* driver is not registered in this build (rollback / fork tolerance).
* - `streamChanges` — coalesced "registry mutated" pings so consumers
* can re-pull lists or re-broadcast.
*
* @module provider/Services/ProviderInstanceRegistry
*/
import type { ProviderInstanceId, ServerProvider } from "@t3tools/contracts";
import * as Context from "effect/Context";
import type * as Effect from "effect/Effect";
import type * as PubSub from "effect/PubSub";
import type * as Scope from "effect/Scope";
import type * as Stream from "effect/Stream";
import type { ProviderInstance } from "../ProviderDriver.ts";
export interface ProviderInstanceRegistryShape {
/**
* Look up one instance by id. Returns `undefined` (not Option) when the
* id is unknown — callers branch on falsy and emit
* `ProviderInstanceNotFoundError`.
*/
readonly getInstance: (
instanceId: ProviderInstanceId,
) => Effect.Effect<ProviderInstance | undefined>;
/**
* Every available (driver-registered, successfully created) instance,
* in stable settings-author order.
*/
readonly listInstances: Effect.Effect<ReadonlyArray<ProviderInstance>>;
/**
* Wire-shape shadow snapshots for instances whose driver is unknown to
* this build (or whose config failed to decode). Suitable for merging
* directly into `ProviderRegistry` output.
*/
readonly listUnavailable: Effect.Effect<ReadonlyArray<ServerProvider>>;
/**
* Push notification stream emitted whenever the registry's contents
* change — instance added, removed, or rebuilt. The payload is `void`
* because consumers always want to re-pull `listInstances` /
* `listUnavailable` together.
*
* NOTE: because `Stream.fromPubSub` defers `PubSub.subscribe` until the
* stream starts running, forking a consumer via
* `Stream.runForEach(...).pipe(Effect.forkScoped)` races the next
* publish — the forked fiber may not have subscribed yet when the
* publish lands. Hot-reload consumers that must not miss a publish
* should use `subscribeChanges` below instead, which acquires the
* subscription synchronously in the caller's fiber before the consumer
* loop is forked.
*/
readonly streamChanges: Stream.Stream<void>;
/**
* Acquire a subscription to the registry's change channel synchronously
* in the caller's fiber. Returns a `PubSub.Subscription<void>` whose
* lifetime is scoped to the provided `Scope` (the subscription is
* released when the scope closes). Consumers typically `yield*` this
* in the same fiber that forks their consumer loop, then drain with
* `PubSub.take(subscription)` inside `Effect.forever`. Because the
* subscription is registered with the PubSub before this `yield*`
* returns, no subsequent publish can land in a gap.
*
* This exists because the `ProviderInstanceRegistry` publishes on a
* PubSub and `Stream.fromPubSub` defers subscription until the stream
* starts executing — a consumer that `forkScoped`s the stream
* consumption can miss a publish that lands in the narrow window
* between "fiber scheduled" and "fiber starts running".
*/
readonly subscribeChanges: Effect.Effect<PubSub.Subscription<void>, never, Scope.Scope>;
}
export class ProviderInstanceRegistry extends Context.Service<
ProviderInstanceRegistry,
ProviderInstanceRegistryShape
>()("t3/provider/Services/ProviderInstanceRegistry") {}