1/**
2 * The `ClusterSchema` module collects the annotations that add cluster behavior
3 * to RPC protocols and entity definitions. These annotations describe how
4 * requests are persisted, handled in transactions, interrupted, traced, and
5 * routed to shard groups without changing the request or response schema.
6 *
7 * @stability unstable
8 * @since 4.0.0
9 */
10import * as Cause from "../Cause.ts"
11import * as Context from "../Context.ts"
12import { constFalse, constTrue, constUndefined, identity } from "../Function.ts"
13import type * as Rpc from "../rpc/Rpc.ts"
14import type { EntityId } from "./EntityId.ts"
15import type { Request } from "./Envelope.ts"
16
17/**
18 * Annotation carried by an interruption when a persisted cluster request is
19 * abandoned by its current runner and must continue under another owner.
20 *
21 * @stability unstable
22 * @category services
23 * @since 4.0.0
24 */
25export class Abandon extends Context.Service<Abandon, true>()("effect/cluster/ClusterSchema/Abandon") {
26 static annotation = this.context(true).pipe(
27 Context.add(Cause.StackTrace, {
28 name: "ClusterAbandon",
29 stack: constUndefined,
30 parent: undefined
31 })
32 )
33}
34
35/**
36 * Annotation that marks whether a cluster request should be persisted in mailbox
37 * storage.
38 *
39 * **Details**
40 *
41 * The default value is `false`.
42 *
43 * @stability unstable
44 * @category services
45 * @since 4.0.0
46 */
47export const Persisted = Context.Reference<boolean>("effect/cluster/ClusterSchema/Persisted", {
48 defaultValue: constFalse
49})
50
51/**
52 * Annotation that marks whether request handling should be wrapped in the
53 * configured message storage transaction.
54 *
55 * **When to use**
56 *
57 * Use when you need server-side request handling or storage work wrapped in the
58 * storage transaction.
59 *
60 * **Details**
61 *
62 * The default value is `false`. When `true`, entity handling wraps server
63 * writes with the configured storage transaction.
64 *
65 * **Gotchas**
66 *
67 * This annotation has transactional behavior only when the configured
68 * `MessageStorage` implements it.
69 *
70 * @stability unstable
71 * @category services
72 * @since 4.0.0
73 */
74export const WithTransaction = Context.Reference<boolean>(
75 "effect/cluster/ClusterSchema/WithTransaction",
76 { defaultValue: constFalse }
77)
78
79/**
80 * Annotation that controls whether a cluster request is treated as
81 * uninterruptible.
82 *
83 * **Details**
84 *
85 * Use `true` for both client and server handling, `"client"` for client-side
86 * handling only, `"server"` for server-side handling only, or `false` to allow
87 * interruption.
88 *
89 * @stability unstable
90 * @category services
91 * @since 4.0.0
92 */
93export const Uninterruptible = Context.Reference<boolean | "client" | "server">(
94 "effect/cluster/ClusterSchema/Uninterruptible",
95 { defaultValue: constFalse }
96)
97
98/**
99 * Returns whether the `Uninterruptible` annotation applies to server-side
100 * request handling for the provided context.
101 *
102 * **Details**
103 *
104 * Returns `true` only when `Uninterruptible` is `true` or `"server"`.
105 *
106 * @see {@link Uninterruptible} for the annotation values interpreted by this helper
107 * @see {@link isUninterruptibleForClient} for the client-side counterpart
108 *
109 * @stability unstable
110 * @category predicates
111 * @since 4.0.0
112 */
113export const isUninterruptibleForServer = (context: Context.Context<never>): boolean => {
114 const value = Context.get(context, Uninterruptible)
115 return value === true || value === "server"
116}
117
118/**
119 * Returns whether the `Uninterruptible` annotation applies to client-side
120 * request handling for the provided context.
121 *
122 * **When to use**
123 *
124 * Use when you need client-side cluster request handling to decide whether to
125 * ignore an interrupt.
126 *
127 * **Details**
128 *
129 * Returns `true` when `Uninterruptible` is `true` or `"client"`, and `false`
130 * for `"server"` or the default `false`.
131 *
132 * @see {@link Uninterruptible} for the annotation values interpreted by this helper
133 * @see {@link isUninterruptibleForServer} for the server-side counterpart
134 *
135 * @stability unstable
136 * @category predicates
137 * @since 4.0.0
138 */
139export const isUninterruptibleForClient = (context: Context.Context<never>): boolean => {
140 const value = Context.get(context, Uninterruptible)
141 return value === true || value === "client"
142}
143
144/**
145 * Annotation that selects the shard group for an entity id.
146 *
147 * **Details**
148 *
149 * By default, every entity id is assigned to the `"default"` shard group.
150 *
151 * @stability unstable
152 * @category services
153 * @since 4.0.0
154 */
155export const ShardGroup = Context.Reference<(entityId: EntityId) => string>(
156 "effect/cluster/ClusterSchema/ShardGroup",
157 { defaultValue: () => (_) => "default" }
158)
159
160/**
161 * Annotation that controls whether client-side cluster request tracing is
162 * enabled.
163 *
164 * **Details**
165 *
166 * The default value is `true`.
167 *
168 * @stability unstable
169 * @category services
170 * @since 4.0.0
171 */
172export const ClientTracingEnabled = Context.Reference<boolean>("effect/cluster/ClusterSchema/ClientTracingEnabled", {
173 defaultValue: constTrue
174})
175
176/**
177 * Context reference for deriving request annotations from a cluster request.
178 *
179 * **When to use**
180 *
181 * Use to customize server-side request annotations based on the decoded
182 * request value.
183 *
184 * **Gotchas**
185 *
186 * This only applies to requests handled by the entity, not to the generated
187 * client.
188 *
189 * @stability unstable
190 * @category services
191 * @since 4.0.0
192 */
193export const Dynamic = Context.Reference<
194 (annotations: Context.Context<never>, request: Request<Rpc.AnyWithProps>) => Context.Context<never>
195>(
196 "effect/cluster/ClusterSchema/Dynamic",
197 { defaultValue: () => identity }
198)