effect-cf 0.39.1 → 0.40.1
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.
- package/CHANGELOG.md +18 -0
- package/README.md +9 -1
- package/dist/AiGateway.d.mts +86 -0
- package/dist/AiGateway.mjs +178 -0
- package/dist/AnalyticsEngine.d.mts +170 -0
- package/dist/AnalyticsEngine.mjs +275 -0
- package/dist/{Artifacts-B3r3RuTB.d.mts → Artifacts.d.mts} +8 -5
- package/dist/{Artifacts-CBxWGeZB.mjs → Artifacts.mjs} +9 -5
- package/dist/Binding.d.mts +45 -0
- package/dist/{Binding-C82jRhdr.mjs → Binding.mjs} +8 -4
- package/dist/BrowserRendering.d.mts +85 -0
- package/dist/BrowserRendering.mjs +81 -0
- package/dist/Cache.d.mts +40 -0
- package/dist/Cache.mjs +51 -0
- package/dist/CloudflareOtlp.d.mts +95 -0
- package/dist/CloudflareOtlp.mjs +125 -0
- package/dist/CloudflareTracer.d.mts +20 -0
- package/dist/CloudflareTracer.mjs +69 -0
- package/dist/ComputerArtifacts.d.mts +2 -2
- package/dist/ComputerArtifacts.mjs +3 -2
- package/dist/ComputerWorkspace.d.mts +36 -31
- package/dist/ComputerWorkspace.mjs +8 -3
- package/dist/ComputerWorkspaceHost.d.mts +1 -1
- package/dist/ComputerWorkspaceHost.mjs +1 -1
- package/dist/{ContainerNamespace-DbLaeFhN.d.mts → ContainerNamespace.d.mts} +16 -12
- package/dist/ContainerNamespace.mjs +165 -0
- package/dist/D1.d.mts +33 -0
- package/dist/D1.mjs +32 -0
- package/dist/DurableObject.d.mts +103 -0
- package/dist/DurableObject.mjs +77 -0
- package/dist/DurableObjectAlarm.d.mts +187 -0
- package/dist/DurableObjectAlarm.mjs +344 -0
- package/dist/DurableObjectDefinition.d.mts +87 -0
- package/dist/DurableObjectDefinition.mjs +125 -0
- package/dist/DurableObjectNamespace.d.mts +154 -0
- package/dist/DurableObjectNamespace.mjs +144 -0
- package/dist/DurableObjectRpcWebSocket.d.mts +112 -0
- package/dist/DurableObjectRpcWebSocket.mjs +547 -0
- package/dist/DurableObjectSqlite.d.mts +11 -0
- package/dist/DurableObjectSqlite.mjs +16 -0
- package/dist/DurableObjectState.d.mts +65 -0
- package/dist/DurableObjectState.mjs +47 -0
- package/dist/DurableObjectStorage.d.mts +112 -0
- package/dist/{DurableObjectState-DLL0YgBd.mjs → DurableObjectStorage.mjs} +19 -157
- package/dist/DurableObjectWebSocket.d.mts +83 -0
- package/dist/DurableObjectWebSocket.mjs +120 -0
- package/dist/Email.d.mts +106 -0
- package/dist/Email.mjs +247 -0
- package/dist/Environment.d.mts +59 -0
- package/dist/{ErrorMessage-Byx2OgNd.mjs → Environment.mjs} +7 -18
- package/dist/{Hyperdrive-HrwFlZ6x.d.mts → Hyperdrive.d.mts} +8 -5
- package/dist/Hyperdrive.mjs +32 -0
- package/dist/HyperdrivePg.d.mts +4 -3
- package/dist/HyperdrivePg.mjs +3 -1
- package/dist/Images.d.mts +191 -0
- package/dist/Images.mjs +112 -0
- package/dist/Kv.d.mts +84 -0
- package/dist/Kv.mjs +132 -0
- package/dist/Mcp.d.mts +5 -3
- package/dist/Mcp.mjs +13 -4
- package/dist/Queue.d.mts +68 -0
- package/dist/Queue.mjs +54 -0
- package/dist/QueueBinding.d.mts +42 -0
- package/dist/QueueBinding.mjs +69 -0
- package/dist/QueueDefinition.d.mts +48 -0
- package/dist/QueueDefinition.mjs +63 -0
- package/dist/R2.d.mts +89 -0
- package/dist/R2.mjs +117 -0
- package/dist/Rpc.d.mts +29 -0
- package/dist/Rpc.mjs +28 -0
- package/dist/RpcDefinition.d.mts +116 -0
- package/dist/RpcDefinition.mjs +184 -0
- package/dist/RpcTracing.d.mts +52 -0
- package/dist/RpcTracing.mjs +77 -0
- package/dist/Sandbox.d.mts +10 -5
- package/dist/Sandbox.mjs +9 -3
- package/dist/ServiceBinding.d.mts +112 -0
- package/dist/ServiceBinding.mjs +145 -0
- package/dist/Vectorize.d.mts +68 -0
- package/dist/Vectorize.mjs +76 -0
- package/dist/Vitest.d.mts +6 -4
- package/dist/Vitest.mjs +8 -4
- package/dist/WebTransport.d.mts +66 -0
- package/dist/WebTransport.mjs +63 -0
- package/dist/Worker.d.mts +139 -0
- package/dist/Worker.mjs +141 -0
- package/dist/WorkerDefinition.d.mts +100 -0
- package/dist/WorkerDefinition.mjs +103 -0
- package/dist/WorkersAi.d.mts +84 -0
- package/dist/WorkersAi.mjs +84 -0
- package/dist/Workflow.d.mts +91 -0
- package/dist/Workflow.mjs +139 -0
- package/dist/{WorkflowBinding-DtVykUCz.d.mts → WorkflowBinding.d.mts} +14 -10
- package/dist/WorkflowBinding.mjs +100 -0
- package/dist/WorkflowDefinition.d.mts +51 -0
- package/dist/WorkflowDefinition.mjs +69 -0
- package/dist/{rolldown-runtime-D7D4PA-g.mjs → _virtual/_rolldown/runtime.mjs} +1 -1
- package/dist/index.d.mts +40 -1800
- package/dist/index.mjs +40 -3123
- package/dist/{ComputerWorkspaceHostRegistry-CY5Fv4Ng.mjs → internal/ComputerWorkspaceHostRegistry.mjs} +1 -1
- package/dist/internal/Entrypoint.mjs +37 -0
- package/dist/internal/ErrorMessage.mjs +17 -0
- package/dist/internal/NativeCallback.mjs +33 -0
- package/dist/internal/RpcInvocation.d.mts +13 -0
- package/dist/internal/RpcInvocation.mjs +34 -0
- package/dist/internal/Runtime.mjs +31 -0
- package/dist/internal/Telemetry.mjs +20 -0
- package/dist/{WorkerContext-Yobnfw-K.mjs → internal/WorkerContext.mjs} +4 -31
- package/package.json +1 -1
- package/src/AiGateway.ts +5 -1
- package/src/AnalyticsEngine.ts +14 -19
- package/src/Artifacts.ts +6 -1
- package/src/Binding.ts +5 -1
- package/src/BrowserRendering.ts +5 -1
- package/src/Cache.ts +5 -1
- package/src/CloudflareOtlp.ts +12 -10
- package/src/CloudflareTracer.ts +7 -1
- package/src/ComputerArtifacts.ts +2 -1
- package/src/ComputerWorkspace.ts +7 -1
- package/src/ContainerNamespace.ts +6 -1
- package/src/D1.ts +4 -1
- package/src/DurableObject.ts +122 -29
- package/src/DurableObjectAlarm.ts +185 -52
- package/src/DurableObjectDefinition.ts +22 -22
- package/src/DurableObjectNamespace.ts +4 -1
- package/src/DurableObjectRpcWebSocket.ts +7 -1
- package/src/DurableObjectSqlite.ts +2 -1
- package/src/DurableObjectState.ts +4 -1
- package/src/DurableObjectStorage.ts +11 -13
- package/src/DurableObjectWebSocket.ts +5 -1
- package/src/Email.ts +5 -1
- package/src/Environment.ts +6 -1
- package/src/Hyperdrive.ts +4 -1
- package/src/HyperdrivePg.ts +3 -1
- package/src/Images.ts +7 -1
- package/src/Kv.ts +7 -1
- package/src/Mcp.ts +13 -13
- package/src/Queue.ts +3 -1
- package/src/QueueBinding.ts +5 -1
- package/src/QueueDefinition.ts +4 -1
- package/src/R2.ts +7 -1
- package/src/Rpc.ts +4 -1
- package/src/RpcDefinition.ts +4 -1
- package/src/RpcTracing.ts +9 -1
- package/src/Sandbox.ts +7 -1
- package/src/ServiceBinding.ts +6 -1
- package/src/Vectorize.ts +7 -1
- package/src/Vitest.ts +5 -1
- package/src/WebTransport.ts +6 -1
- package/src/Worker.ts +10 -16
- package/src/WorkerDefinition.ts +3 -1
- package/src/WorkersAi.ts +7 -1
- package/src/Workflow.ts +8 -11
- package/src/WorkflowBinding.ts +6 -1
- package/src/WorkflowDefinition.ts +4 -1
- package/src/internal/Entrypoint.ts +3 -1
- package/src/internal/ErrorMessage.ts +1 -1
- package/src/internal/NativeCallback.ts +3 -1
- package/src/internal/RpcInvocation.ts +5 -1
- package/src/internal/Runtime.ts +5 -1
- package/src/internal/Telemetry.ts +4 -3
- package/src/internal/WorkerContext.ts +3 -1
- package/dist/Binding-DKcLrQCM.d.mts +0 -94
- package/dist/DurableObjectState-DJ0K-zyd.d.mts +0 -244
- package/dist/Worker-XsGJsCJX.mjs +0 -885
- package/dist/Worker-gV4mDqIs.d.mts +0 -641
package/src/DurableObject.ts
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { DurableObject as CloudflareDurableObject } from "cloudflare:workers";
|
|
2
|
-
import
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Layer from "effect/Layer";
|
|
4
|
+
import * as Tracer from "effect/Tracer";
|
|
5
|
+
import type { ManagedRuntime, Scope } from "effect";
|
|
3
6
|
|
|
4
7
|
import { NativeRequest } from "./Worker";
|
|
5
8
|
import { WorkerEnvironment, type WorkerEnv } from "./Environment";
|
|
6
9
|
import { DurableObjectState, fromDurableObjectState } from "./DurableObjectState";
|
|
10
|
+
import {
|
|
11
|
+
DurableObjectAlarm,
|
|
12
|
+
type AlarmRegistration,
|
|
13
|
+
type AlarmService,
|
|
14
|
+
} from "./DurableObjectAlarm";
|
|
7
15
|
import { fromWebSocket, type DurableWebSocket } from "./DurableObjectWebSocket";
|
|
8
16
|
import * as RpcDefinition from "./RpcDefinition";
|
|
9
17
|
import * as Entrypoint from "./internal/Entrypoint";
|
|
@@ -13,7 +21,11 @@ import type { ReceiverOptions, RpcInvocationInfo } from "./RpcTracing";
|
|
|
13
21
|
|
|
14
22
|
const reservedMethodNames: ReadonlySet<string> = RpcDefinition.reservedMethodNames;
|
|
15
23
|
|
|
16
|
-
type RuntimeContext<ROut> =
|
|
24
|
+
export type RuntimeContext<ROut> =
|
|
25
|
+
| DurableObjectState
|
|
26
|
+
| DurableObjectAlarm
|
|
27
|
+
| WorkerEnvironment
|
|
28
|
+
| ROut;
|
|
17
29
|
|
|
18
30
|
type HandlerContext<ROut> = RuntimeContext<ROut> | Scope.Scope;
|
|
19
31
|
|
|
@@ -80,11 +92,12 @@ export type RpcHandlers<ROut, Api> = {
|
|
|
80
92
|
: never;
|
|
81
93
|
};
|
|
82
94
|
|
|
83
|
-
|
|
95
|
+
interface DurableObjectOptionsBase<
|
|
84
96
|
RRuntime,
|
|
85
97
|
REvent = never,
|
|
86
98
|
EventLayerError = never,
|
|
87
|
-
Rpc extends DurableObjectRpc<RRuntime | REvent> = Record<never, never>,
|
|
99
|
+
Rpc extends DurableObjectRpc<RRuntime | REvent | RAlarm> = Record<never, never>,
|
|
100
|
+
RAlarm = never,
|
|
88
101
|
> {
|
|
89
102
|
/**
|
|
90
103
|
* Layer provided around each Cloudflare event handled by this Durable Object.
|
|
@@ -96,7 +109,7 @@ export interface DurableObjectOptions<
|
|
|
96
109
|
readonly eventLayer?: Layer.Layer<
|
|
97
110
|
REvent,
|
|
98
111
|
EventLayerError,
|
|
99
|
-
|
|
112
|
+
RuntimeContext<NoInfer<RRuntime | RAlarm>>
|
|
100
113
|
>;
|
|
101
114
|
/**
|
|
102
115
|
* Effect run when Cloudflare loads this Durable Object instance into memory.
|
|
@@ -106,7 +119,7 @@ export interface DurableObjectOptions<
|
|
|
106
119
|
* the same Durable Object id again after eviction or restart; use Durable
|
|
107
120
|
* Object storage if work must happen only once per id.
|
|
108
121
|
*/
|
|
109
|
-
readonly initialize?: Effect.Effect<void, unknown, HandlerContext<RRuntime
|
|
122
|
+
readonly initialize?: Effect.Effect<void, unknown, HandlerContext<NoInfer<RRuntime | RAlarm>>>;
|
|
110
123
|
/**
|
|
111
124
|
* Optional RPC methods exposed as Durable Object instance methods.
|
|
112
125
|
*
|
|
@@ -118,9 +131,20 @@ export interface DurableObjectOptions<
|
|
|
118
131
|
readonly rpc?: Rpc;
|
|
119
132
|
/** Accept live native trace metadata. The client must also enable rpcTracing. */
|
|
120
133
|
readonly rpcTracing?: ReceiverOptions;
|
|
121
|
-
readonly fetch?: Effect.Effect<
|
|
134
|
+
readonly fetch?: Effect.Effect<
|
|
135
|
+
Response,
|
|
136
|
+
unknown,
|
|
137
|
+
FetchContext<NoInfer<RRuntime | REvent | RAlarm>>
|
|
138
|
+
>;
|
|
122
139
|
|
|
123
|
-
|
|
140
|
+
/** Register handlers to provide their typed scheduler to application layers and all DO events. */
|
|
141
|
+
readonly alarms?:
|
|
142
|
+
| Effect.Effect<unknown, unknown, HandlerContext<NoInfer<RRuntime> | REvent | NoInfer<RAlarm>>>
|
|
143
|
+
| AlarmRegistration<
|
|
144
|
+
RAlarm,
|
|
145
|
+
HandlerContext<NoInfer<RRuntime> | REvent | NoInfer<RAlarm>>,
|
|
146
|
+
unknown
|
|
147
|
+
>;
|
|
124
148
|
/**
|
|
125
149
|
* Optional raw alarm handler.
|
|
126
150
|
*
|
|
@@ -131,23 +155,58 @@ export interface DurableObjectOptions<
|
|
|
131
155
|
*/
|
|
132
156
|
readonly alarm?: (
|
|
133
157
|
alarmInfo?: globalThis.AlarmInvocationInfo,
|
|
134
|
-
) => Effect.Effect<void, unknown, HandlerContext<RRuntime | REvent
|
|
158
|
+
) => Effect.Effect<void, unknown, HandlerContext<NoInfer<RRuntime> | REvent | NoInfer<RAlarm>>>;
|
|
135
159
|
readonly webSocketMessage?: (
|
|
136
160
|
socket: DurableWebSocket,
|
|
137
161
|
message: string | ArrayBuffer,
|
|
138
|
-
) => Effect.Effect<void, unknown, HandlerContext<RRuntime | REvent
|
|
162
|
+
) => Effect.Effect<void, unknown, HandlerContext<NoInfer<RRuntime> | REvent | NoInfer<RAlarm>>>;
|
|
139
163
|
readonly webSocketClose?: (
|
|
140
164
|
socket: DurableWebSocket,
|
|
141
165
|
code: number,
|
|
142
166
|
reason: string,
|
|
143
167
|
wasClean: boolean,
|
|
144
|
-
) => Effect.Effect<void, unknown, HandlerContext<RRuntime | REvent
|
|
168
|
+
) => Effect.Effect<void, unknown, HandlerContext<NoInfer<RRuntime> | REvent | NoInfer<RAlarm>>>;
|
|
145
169
|
readonly webSocketError?: (
|
|
146
170
|
socket: DurableWebSocket,
|
|
147
171
|
cause: unknown,
|
|
148
|
-
) => Effect.Effect<void, unknown, HandlerContext<RRuntime | REvent
|
|
172
|
+
) => Effect.Effect<void, unknown, HandlerContext<NoInfer<RRuntime> | REvent | NoInfer<RAlarm>>>;
|
|
149
173
|
}
|
|
150
174
|
|
|
175
|
+
// Providing a scheduler through an ordinary layer does not install its dispatcher.
|
|
176
|
+
type CheckAlarmRegistration<RProvided, RAlarm> = [
|
|
177
|
+
Exclude<Extract<NoInfer<RProvided>, AlarmService>, NoInfer<RAlarm>>,
|
|
178
|
+
] extends [never]
|
|
179
|
+
? unknown
|
|
180
|
+
: never;
|
|
181
|
+
|
|
182
|
+
/** A declared event or alarm service must have its corresponding provider. */
|
|
183
|
+
export type DurableObjectOptions<
|
|
184
|
+
RRuntime,
|
|
185
|
+
REvent = never,
|
|
186
|
+
EventLayerError = never,
|
|
187
|
+
Rpc extends DurableObjectRpc<RRuntime | REvent | RAlarm> = Record<never, never>,
|
|
188
|
+
RAlarm = never,
|
|
189
|
+
> = DurableObjectOptionsBase<RRuntime, REvent, EventLayerError, Rpc, RAlarm> &
|
|
190
|
+
CheckAlarmRegistration<RRuntime | REvent, RAlarm> &
|
|
191
|
+
([Exclude<REvent, RuntimeContext<RRuntime | RAlarm> | Scope.Scope>] extends [never]
|
|
192
|
+
? unknown
|
|
193
|
+
: {
|
|
194
|
+
readonly eventLayer: Layer.Layer<
|
|
195
|
+
REvent,
|
|
196
|
+
EventLayerError,
|
|
197
|
+
RuntimeContext<NoInfer<RRuntime | RAlarm>>
|
|
198
|
+
>;
|
|
199
|
+
}) &
|
|
200
|
+
([RAlarm] extends [never]
|
|
201
|
+
? unknown
|
|
202
|
+
: {
|
|
203
|
+
readonly alarms: AlarmRegistration<
|
|
204
|
+
RAlarm,
|
|
205
|
+
HandlerContext<NoInfer<RRuntime> | REvent | NoInfer<RAlarm>>,
|
|
206
|
+
unknown
|
|
207
|
+
>;
|
|
208
|
+
});
|
|
209
|
+
|
|
151
210
|
/**
|
|
152
211
|
* Cloudflare `DurableObject` constructor produced by {@link make}.
|
|
153
212
|
*/
|
|
@@ -168,27 +227,62 @@ export type DurableObjectClass<Rpc extends DurableObjectRpc<ROut>, ROut> = new (
|
|
|
168
227
|
* that layer are not guaranteed to run. Put resources that require timely
|
|
169
228
|
* release in `eventLayer`, whose scope closes after each handled event.
|
|
170
229
|
*/
|
|
171
|
-
export
|
|
230
|
+
export function make<ROut, LayerError>(
|
|
231
|
+
layer: Layer.Layer<ROut, LayerError, RuntimeContext<never>> & CheckAlarmRegistration<ROut, never>,
|
|
232
|
+
): DurableObjectClass<Record<never, never>, ROut>;
|
|
233
|
+
export function make<
|
|
234
|
+
ROut,
|
|
235
|
+
LayerError,
|
|
236
|
+
REvent = never,
|
|
237
|
+
EventLayerError = never,
|
|
238
|
+
const Rpc extends DurableObjectRpc<NoInfer<ROut> | REvent | NoInfer<RAlarm>> = Record<
|
|
239
|
+
never,
|
|
240
|
+
never
|
|
241
|
+
>,
|
|
242
|
+
RAlarm = never,
|
|
243
|
+
>(
|
|
244
|
+
layer: Layer.Layer<ROut, LayerError, RuntimeContext<NoInfer<RAlarm>>>,
|
|
245
|
+
options: DurableObjectOptions<ROut, REvent, EventLayerError, Rpc, RAlarm>,
|
|
246
|
+
): DurableObjectClass<Rpc, ROut | REvent | RAlarm>;
|
|
247
|
+
export function make<
|
|
172
248
|
ROut,
|
|
173
249
|
LayerError,
|
|
174
250
|
REvent = never,
|
|
175
251
|
EventLayerError = never,
|
|
176
|
-
const Rpc extends DurableObjectRpc<ROut | REvent
|
|
252
|
+
const Rpc extends DurableObjectRpc<NoInfer<ROut> | REvent | NoInfer<RAlarm>> = Record<
|
|
253
|
+
never,
|
|
254
|
+
never
|
|
255
|
+
>,
|
|
256
|
+
RAlarm = never,
|
|
177
257
|
>(
|
|
178
|
-
layer: Layer.Layer<ROut, LayerError,
|
|
179
|
-
options:
|
|
180
|
-
): DurableObjectClass<Rpc, ROut | REvent>
|
|
258
|
+
layer: Layer.Layer<ROut, LayerError, RuntimeContext<NoInfer<RAlarm>>>,
|
|
259
|
+
options: DurableObjectOptionsBase<ROut, REvent, EventLayerError, Rpc, RAlarm> = {},
|
|
260
|
+
): DurableObjectClass<Rpc, ROut | REvent | RAlarm> {
|
|
261
|
+
const registration =
|
|
262
|
+
options.alarms !== undefined && !Effect.isEffect(options.alarms) ? options.alarms : undefined;
|
|
263
|
+
const logicalAlarms = (
|
|
264
|
+
registration?.run ?? (Effect.isEffect(options.alarms) ? options.alarms : undefined)
|
|
265
|
+
)?.pipe(Effect.asVoid);
|
|
266
|
+
|
|
181
267
|
class EffectDurableObject extends CloudflareDurableObject<WorkerEnv> {
|
|
182
|
-
readonly runtime: ManagedRuntime.ManagedRuntime<RuntimeContext<ROut>, LayerError>;
|
|
268
|
+
readonly runtime: ManagedRuntime.ManagedRuntime<RuntimeContext<ROut | RAlarm>, LayerError>;
|
|
183
269
|
|
|
184
270
|
constructor(state: globalThis.DurableObjectState, env: WorkerEnv) {
|
|
185
271
|
super(state, env);
|
|
186
272
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
env,
|
|
190
|
-
Layer.succeed(DurableObjectState, fromDurableObjectState(state)),
|
|
273
|
+
const baseServices = DurableObjectAlarm.layer.pipe(
|
|
274
|
+
Layer.provideMerge(Layer.succeed(DurableObjectState, fromDurableObjectState(state))),
|
|
191
275
|
);
|
|
276
|
+
// SAFETY: RAlarm is inferred only from the registration's layer; without a registration it is never.
|
|
277
|
+
const services = (registration?.layer ?? Layer.empty).pipe(
|
|
278
|
+
Layer.provideMerge(baseServices),
|
|
279
|
+
) as Layer.Layer<DurableObjectState | DurableObjectAlarm | RAlarm>;
|
|
280
|
+
|
|
281
|
+
this.runtime = Runtime.makeEntrypointRuntime<
|
|
282
|
+
ROut,
|
|
283
|
+
LayerError,
|
|
284
|
+
DurableObjectState | DurableObjectAlarm | RAlarm
|
|
285
|
+
>(layer, env, services);
|
|
192
286
|
|
|
193
287
|
const initialize = options.initialize;
|
|
194
288
|
|
|
@@ -198,7 +292,7 @@ export const make = <
|
|
|
198
292
|
}
|
|
199
293
|
|
|
200
294
|
[RunSymbol]<A, E>(
|
|
201
|
-
effect: Effect.Effect<A, E, HandlerContext<ROut | REvent>>,
|
|
295
|
+
effect: Effect.Effect<A, E, HandlerContext<ROut | REvent | RAlarm>>,
|
|
202
296
|
runOptions: RunOptions = {},
|
|
203
297
|
): Promise<A> {
|
|
204
298
|
const eventLayer = runOptions.eventLayer === false ? undefined : options.eventLayer;
|
|
@@ -206,11 +300,11 @@ export const make = <
|
|
|
206
300
|
const parentSpan = parent === undefined ? undefined : Tracer.externalSpan(parent);
|
|
207
301
|
|
|
208
302
|
if (eventLayer === undefined) {
|
|
209
|
-
// SAFETY:
|
|
303
|
+
// SAFETY: initialize cannot require event services; without an event layer, all requirements are already provided.
|
|
210
304
|
return Runtime.runEventPromise(
|
|
211
305
|
this.runtime,
|
|
212
306
|
// @effect-diagnostics-next-line unsafeEffectTypeAssertion:off
|
|
213
|
-
effect as Effect.Effect<A, E, HandlerContext<ROut>>,
|
|
307
|
+
effect as Effect.Effect<A, E, HandlerContext<ROut | RAlarm>>,
|
|
214
308
|
undefined,
|
|
215
309
|
parentSpan,
|
|
216
310
|
);
|
|
@@ -219,7 +313,7 @@ export const make = <
|
|
|
219
313
|
return Runtime.runEventPromise<
|
|
220
314
|
A,
|
|
221
315
|
E,
|
|
222
|
-
RuntimeContext<ROut>,
|
|
316
|
+
RuntimeContext<ROut | RAlarm>,
|
|
223
317
|
REvent,
|
|
224
318
|
EventLayerError,
|
|
225
319
|
LayerError
|
|
@@ -239,7 +333,6 @@ export const make = <
|
|
|
239
333
|
}
|
|
240
334
|
|
|
241
335
|
alarm(alarmInfo?: globalThis.AlarmInvocationInfo): Promise<void> | void {
|
|
242
|
-
const logicalAlarms = options.alarms?.pipe(Effect.asVoid);
|
|
243
336
|
const rawAlarm = options.alarm?.(alarmInfo);
|
|
244
337
|
const alarmEffect =
|
|
245
338
|
logicalAlarms !== undefined && rawAlarm !== undefined
|
|
@@ -297,10 +390,10 @@ export const make = <
|
|
|
297
390
|
options.rpcTracing,
|
|
298
391
|
);
|
|
299
392
|
|
|
300
|
-
return Entrypoint.assumeEntrypointClass<DurableObjectClass<Rpc, ROut | REvent>>(
|
|
393
|
+
return Entrypoint.assumeEntrypointClass<DurableObjectClass<Rpc, ROut | REvent | RAlarm>>(
|
|
301
394
|
EffectDurableObject,
|
|
302
395
|
);
|
|
303
|
-
}
|
|
396
|
+
}
|
|
304
397
|
|
|
305
398
|
export type {
|
|
306
399
|
Api,
|
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Schema as S,
|
|
12
|
-
} from "effect";
|
|
1
|
+
import * as Clock from "effect/Clock";
|
|
2
|
+
import * as Context from "effect/Context";
|
|
3
|
+
import * as Data from "effect/Data";
|
|
4
|
+
import * as DateTime from "effect/DateTime";
|
|
5
|
+
import * as Duration from "effect/Duration";
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import * as Exit from "effect/Exit";
|
|
8
|
+
import * as Layer from "effect/Layer";
|
|
9
|
+
import * as Predicate from "effect/Predicate";
|
|
10
|
+
import * as S from "effect/Schema";
|
|
13
11
|
|
|
14
12
|
import { DurableObjectState } from "./DurableObjectState";
|
|
15
13
|
import { type SqlStorageValue, StorageOperationError } from "./DurableObjectStorage";
|
|
@@ -368,6 +366,63 @@ export type DefinedAlarmHandlers<Definitions extends AlarmDefinitions, R = never
|
|
|
368
366
|
) => Effect.Effect<void, E, R>;
|
|
369
367
|
};
|
|
370
368
|
|
|
369
|
+
/** A discriminated union keeps each tag paired with its decoded payload type. */
|
|
370
|
+
export type DefinedScheduleAlarmInput<Definitions extends AlarmDefinitions> = {
|
|
371
|
+
readonly [Tag in keyof Definitions & string]: Omit<ScheduleAlarmInput<Tag>, "payload"> & {
|
|
372
|
+
readonly payload: AlarmDefinitionPayload<Definitions[Tag]>;
|
|
373
|
+
};
|
|
374
|
+
}[keyof Definitions & string];
|
|
375
|
+
|
|
376
|
+
export interface DefinedAlarmTransaction<Definitions extends AlarmDefinitions> {
|
|
377
|
+
readonly scheduleAlarm: (
|
|
378
|
+
input: DefinedScheduleAlarmInput<Definitions>,
|
|
379
|
+
) => ReturnType<AlarmScheduler["scheduleAlarm"]>;
|
|
380
|
+
readonly cancelAlarm: (
|
|
381
|
+
input: AlarmRef<keyof Definitions & string>,
|
|
382
|
+
) => ReturnType<AlarmScheduler["cancelAlarm"]>;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const TypedAlarmSchedulerTypeId: unique symbol = Symbol.for(
|
|
386
|
+
"effect-cf/DurableObjectAlarm/TypedScheduler",
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
/** Identifies service requirements whose scheduler needs a registered dispatcher. */
|
|
390
|
+
export interface AlarmService {
|
|
391
|
+
readonly Service: {
|
|
392
|
+
readonly [TypedAlarmSchedulerTypeId]: typeof TypedAlarmSchedulerTypeId;
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export interface DefinedAlarmScheduler<
|
|
397
|
+
Definitions extends AlarmDefinitions,
|
|
398
|
+
> extends DefinedAlarmTransaction<Definitions> {
|
|
399
|
+
readonly [TypedAlarmSchedulerTypeId]: typeof TypedAlarmSchedulerTypeId;
|
|
400
|
+
readonly transaction: <A, E, R>(
|
|
401
|
+
closure: (alarms: DefinedAlarmTransaction<Definitions>) => Effect.Effect<A, E, R>,
|
|
402
|
+
) => Effect.Effect<A, E | StorageOperationError, R>;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/** Registers a dispatcher and the service authorized to schedule its alarms. */
|
|
406
|
+
export interface AlarmRegistration<Self, R = never, E = never> {
|
|
407
|
+
readonly layer: Layer.Layer<Self, never, DurableObjectAlarm>;
|
|
408
|
+
readonly run: Effect.Effect<
|
|
409
|
+
ProcessDueAlarmsResult,
|
|
410
|
+
E | DurableObjectAlarmError,
|
|
411
|
+
R | DurableObjectAlarm
|
|
412
|
+
>;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
export interface AlarmTagClass<
|
|
416
|
+
Self,
|
|
417
|
+
Id extends string,
|
|
418
|
+
Definitions extends AlarmDefinitions,
|
|
419
|
+
> extends Context.ServiceClass<Self, Id, DefinedAlarmScheduler<Definitions>> {
|
|
420
|
+
readonly handlers: <R = never, E = never>(
|
|
421
|
+
handlers: DefinedAlarmHandlers<Definitions, R, E>,
|
|
422
|
+
options?: ProcessDueAlarmsOptions<R, E>,
|
|
423
|
+
) => AlarmRegistration<Self, R, E>;
|
|
424
|
+
}
|
|
425
|
+
|
|
371
426
|
const isAlarmDefinitionConfig = (
|
|
372
427
|
definition: AlarmDefinitionEntry,
|
|
373
428
|
): definition is AlarmDefinitionConfig => Predicate.isObject(definition) && "payload" in definition;
|
|
@@ -391,55 +446,133 @@ const getAlarmDefinitionFailureAction = (
|
|
|
391
446
|
: { mode: definition.failure };
|
|
392
447
|
};
|
|
393
448
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
options?: ProcessDueAlarmsOptions<R, E>,
|
|
398
|
-
) =>
|
|
399
|
-
processDue(
|
|
400
|
-
(event) =>
|
|
401
|
-
Effect.gen(function* () {
|
|
402
|
-
const definition = definitions[event.tag];
|
|
449
|
+
const makeDefinition = <const Definitions extends AlarmDefinitions>(definitions: Definitions) => {
|
|
450
|
+
const definitionFor = (tag: string) =>
|
|
451
|
+
Object.hasOwn(definitions, tag) ? definitions[tag] : undefined;
|
|
403
452
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
453
|
+
const bind = (mutations: AlarmTransaction): DefinedAlarmTransaction<Definitions> => ({
|
|
454
|
+
scheduleAlarm: Effect.fn("DefinedAlarms.scheduleAlarm")(function* (
|
|
455
|
+
input: DefinedScheduleAlarmInput<Definitions>,
|
|
456
|
+
) {
|
|
457
|
+
const definition = definitionFor(input.tag);
|
|
407
458
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
459
|
+
if (definition === undefined) {
|
|
460
|
+
return yield* Effect.fail(
|
|
461
|
+
new InvalidAlarmRefError({
|
|
462
|
+
cause: new Error(`Unknown alarm tag "${input.tag}"`),
|
|
463
|
+
}),
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
const payload = yield* S.encodeEffect(getAlarmDefinitionSchema(definition))(
|
|
467
|
+
input.payload,
|
|
468
|
+
).pipe(
|
|
469
|
+
Effect.flatMap(S.decodeUnknownEffect(S.Json)),
|
|
470
|
+
Effect.mapError((cause) => new InvalidAlarmPayloadError({ cause })),
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
yield* mutations.scheduleAlarm({ ...input, payload });
|
|
474
|
+
}),
|
|
475
|
+
cancelAlarm: Effect.fn("DefinedAlarms.cancelAlarm")(function* (
|
|
476
|
+
input: AlarmRef<keyof Definitions & string>,
|
|
477
|
+
) {
|
|
478
|
+
if (definitionFor(input.tag) === undefined) {
|
|
479
|
+
return yield* Effect.fail(
|
|
480
|
+
new InvalidAlarmRefError({
|
|
481
|
+
cause: new Error(`Unknown alarm tag "${input.tag}"`),
|
|
482
|
+
}),
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
yield* mutations.cancelAlarm(input);
|
|
487
|
+
}),
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
return {
|
|
491
|
+
make: (alarms: AlarmScheduler): DefinedAlarmScheduler<Definitions> => ({
|
|
492
|
+
[TypedAlarmSchedulerTypeId]: TypedAlarmSchedulerTypeId,
|
|
493
|
+
...bind(alarms),
|
|
494
|
+
transaction: (closure) => alarms.transaction((tx) => closure(bind(tx))),
|
|
495
|
+
}),
|
|
496
|
+
handlers: <R = never, E = never>(
|
|
497
|
+
handlers: DefinedAlarmHandlers<Definitions, R, E>,
|
|
498
|
+
options?: ProcessDueAlarmsOptions<R, E>,
|
|
499
|
+
) =>
|
|
500
|
+
processDue(
|
|
501
|
+
(event) =>
|
|
502
|
+
Effect.gen(function* () {
|
|
503
|
+
const definition = definitionFor(event.tag);
|
|
504
|
+
|
|
505
|
+
if (definition === undefined) {
|
|
506
|
+
return yield* Effect.fail(
|
|
418
507
|
new StoredAlarmDecodeError({
|
|
419
|
-
cause,
|
|
508
|
+
cause: new Error(`Unknown alarm tag "${event.tag}"`),
|
|
420
509
|
storageId: getScheduledEventId(event),
|
|
421
510
|
}),
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
const handler = handlers[event.tag];
|
|
511
|
+
);
|
|
512
|
+
}
|
|
425
513
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
514
|
+
const schema = getAlarmDefinitionSchema(definition);
|
|
515
|
+
// SAFETY: schema is selected from the same tagged definition used to select its handler.
|
|
516
|
+
const payload = yield* (
|
|
517
|
+
S.decodeUnknownEffect(schema)(event.payload) as Effect.Effect<
|
|
518
|
+
AlarmDefinitionPayload<Definitions[keyof Definitions & string]>,
|
|
519
|
+
unknown
|
|
520
|
+
>
|
|
521
|
+
).pipe(
|
|
522
|
+
Effect.mapError(
|
|
523
|
+
(cause) =>
|
|
524
|
+
new StoredAlarmDecodeError({
|
|
525
|
+
cause,
|
|
526
|
+
storageId: getScheduledEventId(event),
|
|
527
|
+
}),
|
|
528
|
+
),
|
|
529
|
+
);
|
|
530
|
+
const handler = handlers[event.tag];
|
|
436
531
|
|
|
437
|
-
|
|
532
|
+
// SAFETY: event.tag indexes the matching definition and handler, whose payload schema was decoded above.
|
|
533
|
+
yield* handler({ ...event, payload } as never);
|
|
438
534
|
}),
|
|
439
|
-
|
|
440
|
-
|
|
535
|
+
{
|
|
536
|
+
...options,
|
|
537
|
+
onFailure: (failure) =>
|
|
538
|
+
Effect.gen(function* () {
|
|
539
|
+
const action = getAlarmDefinitionFailureAction(definitionFor(failure.tag));
|
|
540
|
+
const optionAction =
|
|
541
|
+
options?.onFailure === undefined ? undefined : yield* options.onFailure(failure);
|
|
542
|
+
|
|
543
|
+
return action ?? optionAction;
|
|
544
|
+
}),
|
|
545
|
+
},
|
|
546
|
+
),
|
|
547
|
+
};
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
/** Handler-only definitions for applications using the raw scheduler. Prefer Tag for typed scheduling. */
|
|
551
|
+
export const define = <const Definitions extends AlarmDefinitions>(definitions: Definitions) => ({
|
|
552
|
+
handlers: makeDefinition(definitions).handlers,
|
|
441
553
|
});
|
|
442
554
|
|
|
555
|
+
/** The typed scheduler becomes available when its handlers are registered on a Durable Object. */
|
|
556
|
+
export const Tag =
|
|
557
|
+
<Self>() =>
|
|
558
|
+
<const Id extends string, const Definitions extends AlarmDefinitions>(
|
|
559
|
+
id: Id,
|
|
560
|
+
definitions: Definitions,
|
|
561
|
+
): AlarmTagClass<Self, Id, Definitions> => {
|
|
562
|
+
const tag = Context.Service<Self, DefinedAlarmScheduler<Definitions>>()(id);
|
|
563
|
+
const definition = makeDefinition(definitions);
|
|
564
|
+
|
|
565
|
+
return Object.assign(tag, {
|
|
566
|
+
handlers: <R = never, E = never>(
|
|
567
|
+
handlers: DefinedAlarmHandlers<Definitions, R, E>,
|
|
568
|
+
options?: ProcessDueAlarmsOptions<R, E>,
|
|
569
|
+
): AlarmRegistration<Self, R, E> => ({
|
|
570
|
+
layer: Layer.effect(tag, Effect.map(DurableObjectAlarm, definition.make)),
|
|
571
|
+
run: definition.handlers(handlers, options),
|
|
572
|
+
}),
|
|
573
|
+
});
|
|
574
|
+
};
|
|
575
|
+
|
|
443
576
|
export class DurableObjectAlarm extends Context.Service<DurableObjectAlarm, AlarmScheduler>()(
|
|
444
577
|
"effect-cf/DurableObjectAlarm",
|
|
445
578
|
) {
|
|
@@ -723,6 +856,6 @@ export class DurableObjectAlarm extends Context.Service<DurableObjectAlarm, Alar
|
|
|
723
856
|
scheduleAlarm: (input) => transaction((alarms) => alarms.scheduleAlarm(input)),
|
|
724
857
|
transaction,
|
|
725
858
|
});
|
|
726
|
-
})
|
|
859
|
+
}),
|
|
727
860
|
);
|
|
728
861
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as Context from "effect/Context";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import type { Layer, Schema as S } from "effect";
|
|
2
4
|
|
|
3
5
|
import type * as Binding from "./Binding";
|
|
4
6
|
import * as DurableObjectEntrypoint from "./DurableObject";
|
|
5
7
|
import type { DurableObjectHandler } from "./DurableObject";
|
|
6
8
|
import * as DurableObjectNamespace from "./DurableObjectNamespace";
|
|
7
|
-
import type { DurableObjectState } from "./DurableObjectState";
|
|
8
9
|
import type * as Rpc from "./Rpc";
|
|
9
10
|
import * as RpcDefinition from "./RpcDefinition";
|
|
10
11
|
import { recordDecodedArgs } from "./internal/RpcInvocation";
|
|
@@ -118,22 +119,21 @@ type MutableBoundaryHandlers<ROut, Self extends Definition.Any> = {
|
|
|
118
119
|
/**
|
|
119
120
|
* Durable Object constructor options for a specific RPC definition.
|
|
120
121
|
*/
|
|
121
|
-
export
|
|
122
|
+
export type Options<
|
|
122
123
|
ROut,
|
|
123
124
|
Self extends Definition.Any,
|
|
124
125
|
REvent = never,
|
|
125
126
|
EventLayerError = never,
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
>
|
|
135
|
-
|
|
136
|
-
}
|
|
127
|
+
RAlarm = never,
|
|
128
|
+
> = DurableObjectEntrypoint.DurableObjectOptions<
|
|
129
|
+
ROut,
|
|
130
|
+
REvent,
|
|
131
|
+
EventLayerError,
|
|
132
|
+
Handlers<NoInfer<ROut> | REvent | NoInfer<RAlarm>, Self>,
|
|
133
|
+
RAlarm
|
|
134
|
+
> & {
|
|
135
|
+
readonly rpc: Handlers<NoInfer<ROut> | REvent | NoInfer<RAlarm>, Self>;
|
|
136
|
+
};
|
|
137
137
|
|
|
138
138
|
export type LayerOptions = {
|
|
139
139
|
readonly binding: string;
|
|
@@ -159,12 +159,12 @@ export type TagClass<
|
|
|
159
159
|
> & {
|
|
160
160
|
readonly id: Id;
|
|
161
161
|
readonly methods: MethodDefinitions;
|
|
162
|
-
readonly make: <ROut, LayerError, REvent = never, EventLayerError = never>(
|
|
163
|
-
layer: Layer.Layer<ROut, LayerError,
|
|
164
|
-
options: Options<ROut, Definition<Id, MethodDefinitions>, REvent, EventLayerError>,
|
|
162
|
+
readonly make: <ROut, LayerError, REvent = never, EventLayerError = never, RAlarm = never>(
|
|
163
|
+
layer: Layer.Layer<ROut, LayerError, DurableObjectEntrypoint.RuntimeContext<NoInfer<RAlarm>>>,
|
|
164
|
+
options: Options<ROut, Definition<Id, MethodDefinitions>, REvent, EventLayerError, RAlarm>,
|
|
165
165
|
) => DurableObjectEntrypoint.DurableObjectClass<
|
|
166
|
-
Handlers<ROut | REvent, Definition<Id, MethodDefinitions>>,
|
|
167
|
-
ROut | REvent
|
|
166
|
+
Handlers<ROut | REvent | RAlarm, Definition<Id, MethodDefinitions>>,
|
|
167
|
+
ROut | REvent | RAlarm
|
|
168
168
|
>;
|
|
169
169
|
readonly layer: (
|
|
170
170
|
options: LayerOptions,
|
|
@@ -201,9 +201,9 @@ const makeDefinition = <Id extends string, const MethodDefinitions extends Metho
|
|
|
201
201
|
const definition: SelfDefinition = RpcDefinition.make(id, methods);
|
|
202
202
|
|
|
203
203
|
return Object.assign(definition, {
|
|
204
|
-
make: <ROut, LayerError, REvent = never, EventLayerError = never>(
|
|
205
|
-
layer: Layer.Layer<ROut, LayerError,
|
|
206
|
-
options: Options<ROut, SelfDefinition, REvent, EventLayerError>,
|
|
204
|
+
make: <ROut, LayerError, REvent = never, EventLayerError = never, RAlarm = never>(
|
|
205
|
+
layer: Layer.Layer<ROut, LayerError, DurableObjectEntrypoint.RuntimeContext<NoInfer<RAlarm>>>,
|
|
206
|
+
options: Options<ROut, SelfDefinition, REvent, EventLayerError, RAlarm>,
|
|
207
207
|
) =>
|
|
208
208
|
DurableObjectEntrypoint.make(layer, {
|
|
209
209
|
...options,
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as Data from "effect/Data";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Predicate from "effect/Predicate";
|
|
4
|
+
import type { Context, Layer, Scope } from "effect";
|
|
2
5
|
|
|
3
6
|
import * as Binding from "./Binding";
|
|
4
7
|
import type { WorkerEnvironment } from "./Environment";
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as Context from "effect/Context";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Layer from "effect/Layer";
|
|
4
|
+
import * as Option from "effect/Option";
|
|
5
|
+
import * as Predicate from "effect/Predicate";
|
|
6
|
+
import * as Queue from "effect/Queue";
|
|
7
|
+
import * as Schema from "effect/Schema";
|
|
2
8
|
import * as RpcMessage from "effect/unstable/rpc/RpcMessage";
|
|
3
9
|
import * as RpcSerialization from "effect/unstable/rpc/RpcSerialization";
|
|
4
10
|
import * as RpcServer from "effect/unstable/rpc/RpcServer";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SqliteClient } from "@effect/sql-sqlite-do";
|
|
2
|
-
import
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Layer from "effect/Layer";
|
|
3
4
|
import type { SqlClient } from "effect/unstable/sql";
|
|
4
5
|
|
|
5
6
|
import { DurableObjectState } from "./DurableObjectState";
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as Cause from "effect/Cause";
|
|
2
|
+
import * as Context from "effect/Context";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Exit from "effect/Exit";
|
|
2
5
|
|
|
3
6
|
import { fromDurableObjectStorage, type DurableObjectStorage } from "./DurableObjectStorage";
|
|
4
7
|
import { fromWebSocket, type DurableWebSocket } from "./DurableObjectWebSocket";
|