effect-cf 0.25.1 → 0.25.2
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 +6 -0
- package/dist/Vitest.d.mts +1 -1
- package/dist/{WorkflowBinding-Bzs5kmoE.d.mts → WorkflowBinding-D5iWzCUa.d.mts} +6 -1
- package/dist/index.d.mts +7 -2
- package/dist/index.mjs +26 -11
- package/package.json +1 -1
- package/src/CloudflareOtlp.ts +6 -2
- package/src/DurableObject.ts +13 -1
- package/src/Worker.ts +13 -19
- package/src/internal/Entrypoint.ts +6 -3
- package/src/internal/Telemetry.ts +27 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# effect-cf
|
|
2
2
|
|
|
3
|
+
## 0.25.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#90](https://github.com/danieljvdm/effect-cf/pull/90) [`56c1b2d`](https://github.com/danieljvdm/effect-cf/commit/56c1b2dfa30cbcc2336f28a8d8f7a48032f565df) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Flush configured OTLP telemetry after native Worker and Durable Object RPC handlers complete. Flush and scheduling failures keep the handler outcome unchanged and emit only bounded framework diagnostics.
|
|
8
|
+
|
|
3
9
|
## 0.25.1
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/dist/Vitest.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as WorkerEnvironment, s as WorkerEnv } from "./Binding-COCtoJXa.mjs";
|
|
2
|
-
import { c as DurableObjectState, l as DurableObjectStateService, o as WorkflowInstanceStatusName } from "./WorkflowBinding-
|
|
2
|
+
import { c as DurableObjectState, l as DurableObjectStateService, o as WorkflowInstanceStatusName } from "./WorkflowBinding-D5iWzCUa.mjs";
|
|
3
3
|
import { Effect, Layer } from "effect";
|
|
4
4
|
import { DurableObject } from "cloudflare:workers";
|
|
5
5
|
import { adminSecretsStore as adminSecretsStore$1, getQueueResult, introspectWorkflow as introspectWorkflow$1, introspectWorkflowInstance as introspectWorkflowInstance$1 } from "cloudflare:test";
|
|
@@ -881,7 +881,12 @@ interface WorkerOptions<RRuntime, REvent = never, EventLayerError = never, Rpc e
|
|
|
881
881
|
readonly fetch?: Effect.Effect<WorkerFetchSuccess, unknown, WorkerFetchContext<RRuntime | REvent>>;
|
|
882
882
|
/** Queue consumer handler invoked per message batch. */
|
|
883
883
|
readonly queue?: QueueHandler<RRuntime | REvent>;
|
|
884
|
-
/**
|
|
884
|
+
/**
|
|
885
|
+
* Optional RPC methods exposed as class instance methods.
|
|
886
|
+
*
|
|
887
|
+
* After every invocation, the configured OTLP flusher is scheduled through
|
|
888
|
+
* `WorkerContext.waitUntil`, including when the handler fails.
|
|
889
|
+
*/
|
|
885
890
|
readonly rpc?: Rpc;
|
|
886
891
|
}
|
|
887
892
|
type FetchWorkerOptions<RRuntime, REvent = never, EventLayerError = never> = Omit<WorkerOptions<RRuntime, REvent, EventLayerError, Record<never, never>>, "rpc"> & {
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as Environment_d_exports, c as WorkerEnvironment, i as Binding_d_exports, n as BindingService, o as WorkerConfig, r as BindingValidationError, s as WorkerEnv, t as BindingNotFoundError } from "./Binding-COCtoJXa.mjs";
|
|
2
2
|
import { t as Hyperdrive_d_exports } from "./Hyperdrive-6p59yyVk.mjs";
|
|
3
|
-
import { A as Rpc_d_exports, C as QueueBinding_d_exports, D as RpcDefinition_d_exports, E as ReservedMethodName$2, M as DurableWebSocket, N as DurableObjectStorage_d_exports, O as ServiceFreeSchema$1, P as StorageOperationError, S as QueueDefinition_d_exports, T as Method$2, _ as AsyncMethodArgs, a as WorkflowInstanceCreateOptions, b as AsyncMethodSuccess, c as DurableObjectState, d as ExecutionContext, f as NativeRequest, g as ServiceBinding_d_exports, h as WorkerDefinition_d_exports, i as WorkflowInstanceCreateBatchOptions, j as DurableObjectWebSocket_d_exports, k as Provider, m as Worker_d_exports, n as WorkflowBinding_d_exports, p as WorkerContext, r as WorkflowInstance, s as WorkflowOperationError, t as WorkflowBindingClient, u as DurableObjectState_d_exports, v as AsyncMethodCloudflareReturn, w as Definition$4, x as Queue_d_exports, y as AsyncMethodKey } from "./WorkflowBinding-
|
|
3
|
+
import { A as Rpc_d_exports, C as QueueBinding_d_exports, D as RpcDefinition_d_exports, E as ReservedMethodName$2, M as DurableWebSocket, N as DurableObjectStorage_d_exports, O as ServiceFreeSchema$1, P as StorageOperationError, S as QueueDefinition_d_exports, T as Method$2, _ as AsyncMethodArgs, a as WorkflowInstanceCreateOptions, b as AsyncMethodSuccess, c as DurableObjectState, d as ExecutionContext, f as NativeRequest, g as ServiceBinding_d_exports, h as WorkerDefinition_d_exports, i as WorkflowInstanceCreateBatchOptions, j as DurableObjectWebSocket_d_exports, k as Provider, m as Worker_d_exports, n as WorkflowBinding_d_exports, p as WorkerContext, r as WorkflowInstance, s as WorkflowOperationError, t as WorkflowBindingClient, u as DurableObjectState_d_exports, v as AsyncMethodCloudflareReturn, w as Definition$4, x as Queue_d_exports, y as AsyncMethodKey } from "./WorkflowBinding-D5iWzCUa.mjs";
|
|
4
4
|
import { Config, Context, Data, DateTime, Duration, Effect, Layer, Option, Redacted, Schema, Scope } from "effect";
|
|
5
5
|
import { Headers as Headers$1, HttpClient, HttpClientResponse } from "effect/unstable/http";
|
|
6
6
|
import { OtlpExporter } from "effect/unstable/observability";
|
|
@@ -1099,7 +1099,12 @@ interface DurableObjectOptions<RRuntime, REvent = never, EventLayerError = never
|
|
|
1099
1099
|
* Object storage if work must happen only once per id.
|
|
1100
1100
|
*/
|
|
1101
1101
|
readonly initialize?: Effect.Effect<void, unknown, HandlerContext<RRuntime>>;
|
|
1102
|
-
/**
|
|
1102
|
+
/**
|
|
1103
|
+
* Optional RPC methods exposed as Durable Object instance methods.
|
|
1104
|
+
*
|
|
1105
|
+
* After every invocation, the configured OTLP flusher is scheduled through
|
|
1106
|
+
* `DurableObjectState.waitUntil`, including when the handler fails.
|
|
1107
|
+
*/
|
|
1103
1108
|
readonly rpc?: Rpc;
|
|
1104
1109
|
/** Optional fetch handler for HTTP/WebSocket requests. */
|
|
1105
1110
|
readonly fetch?: Effect.Effect<Response, unknown, FetchContext<RRuntime | REvent>>;
|
package/dist/index.mjs
CHANGED
|
@@ -683,8 +683,12 @@ const layer$14 = Layer.sync(CacheStorage, () => makeClient$11(caches));
|
|
|
683
683
|
*
|
|
684
684
|
* Every layer exposes {@link OtlpExporter.Flusher}, which drains buffered
|
|
685
685
|
* telemetry on demand. Cloudflare isolates can freeze before the periodic
|
|
686
|
-
* export interval fires
|
|
687
|
-
*
|
|
686
|
+
* export interval fires. Effect-backed Worker fetch handlers and Worker or
|
|
687
|
+
* Durable Object native RPC handlers schedule this flusher automatically.
|
|
688
|
+
* Flush or scheduling failures do not replace the handler outcome and emit
|
|
689
|
+
* only bounded framework diagnostics without attaching the foreign cause.
|
|
690
|
+
* Other entrypoint lifecycles can flush explicitly or hand the flush to their
|
|
691
|
+
* `waitUntil` mechanism:
|
|
688
692
|
*
|
|
689
693
|
* ```ts
|
|
690
694
|
* const flusher = yield* OtlpExporter.Flusher;
|
|
@@ -1347,13 +1351,14 @@ const make$11 = (id, methods) => {
|
|
|
1347
1351
|
//#endregion
|
|
1348
1352
|
//#region src/internal/Entrypoint.ts
|
|
1349
1353
|
const provideEntrypointServices = (layer, services) => layer.pipe(Layer.provideMerge(services));
|
|
1350
|
-
const defineEntrypointRpcMethods = (target, prototype, rpc, reservedMethodNames, run) => {
|
|
1354
|
+
const defineEntrypointRpcMethods = (target, prototype, rpc, reservedMethodNames, run, onExit) => {
|
|
1351
1355
|
const methods = rpc ?? {};
|
|
1352
1356
|
assertNoReservedMethods(target, methods, reservedMethodNames);
|
|
1353
1357
|
for (const [key, method] of Object.entries(methods)) Object.defineProperty(prototype, key, {
|
|
1354
1358
|
enumerable: true,
|
|
1355
1359
|
value(...args) {
|
|
1356
|
-
|
|
1360
|
+
const handler = Effect.suspend(() => method(...args)).pipe(Effect.mapError(encodeWireError));
|
|
1361
|
+
return run(this, onExit === void 0 ? handler : handler.pipe(Effect.onExit(() => onExit(this))));
|
|
1357
1362
|
}
|
|
1358
1363
|
});
|
|
1359
1364
|
};
|
|
@@ -1386,6 +1391,18 @@ const runEventPromise = (runtime, effect, eventLayer) => {
|
|
|
1386
1391
|
return runtime.runPromise(Effect.scoped(withEventLayer));
|
|
1387
1392
|
};
|
|
1388
1393
|
//#endregion
|
|
1394
|
+
//#region src/internal/Telemetry.ts
|
|
1395
|
+
const logFlushFailure = Effect.logError("Telemetry flush failed").pipe(Effect.ignoreCause);
|
|
1396
|
+
const logFlushSchedulingFailure = Effect.logError("Telemetry flush scheduling failed").pipe(Effect.ignoreCause);
|
|
1397
|
+
/**
|
|
1398
|
+
* Schedules a configured OTLP flusher through the current Cloudflare event.
|
|
1399
|
+
*/
|
|
1400
|
+
const scheduleTelemetryFlush$2 = (waitUntil) => Effect.gen(function* () {
|
|
1401
|
+
const flusher = yield* Effect.serviceOption(OtlpExporter.Flusher);
|
|
1402
|
+
if (Option.isNone(flusher)) return;
|
|
1403
|
+
yield* waitUntil(flusher.value.flush.pipe(Effect.catchCause(() => logFlushFailure)));
|
|
1404
|
+
}).pipe(Effect.catchCause(() => logFlushSchedulingFailure));
|
|
1405
|
+
//#endregion
|
|
1389
1406
|
//#region src/Rpc.ts
|
|
1390
1407
|
var Rpc_exports = /* @__PURE__ */ __exportAll({
|
|
1391
1408
|
RpcStub: () => RpcStub,
|
|
@@ -1755,10 +1772,7 @@ const isWorkerOptions = (options) => !Effect.isEffect(options);
|
|
|
1755
1772
|
* (including streaming bodies still being consumed). It is a no-op when the
|
|
1756
1773
|
* context does not contain an `OtlpExporter.Flusher`.
|
|
1757
1774
|
*/
|
|
1758
|
-
const scheduleTelemetryFlush = Effect.flatMap(
|
|
1759
|
-
onNone: () => Effect.void,
|
|
1760
|
-
onSome: (flusher) => Effect.flatMap(WorkerContext, (workerContext) => workerContext.waitUntil(flusher.flush))
|
|
1761
|
-
}));
|
|
1775
|
+
const scheduleTelemetryFlush$1 = scheduleTelemetryFlush$2((flush) => Effect.flatMap(WorkerContext, (workerContext) => workerContext.waitUntil(flush)));
|
|
1762
1776
|
function make$9(layer, optionsOrFetch) {
|
|
1763
1777
|
const options = isWorkerOptions(optionsOrFetch) ? optionsOrFetch : { fetch: optionsOrFetch };
|
|
1764
1778
|
class EffectWorker extends WorkerEntrypoint {
|
|
@@ -1778,7 +1792,7 @@ function make$9(layer, optionsOrFetch) {
|
|
|
1778
1792
|
if (fetchHandler === void 0) return Promise.resolve(new Response("Not Found", { status: 404 }));
|
|
1779
1793
|
const ctx = requestContext ?? this.ctx;
|
|
1780
1794
|
const requestServices = Layer.mergeAll(Layer.succeed(NativeRequest, request), Layer.succeed(HttpServerRequest.HttpServerRequest, HttpServerRequest.fromWeb(request)), Layer.succeed(ExecutionContext, ctx), Layer.succeed(WorkerContext, fromExecutionContext(ctx)));
|
|
1781
|
-
return this[RunSymbol$1](renderFetchSuccess(fetchHandler).pipe(Effect.tap(() => scheduleTelemetryFlush), Effect.provide(requestServices)));
|
|
1795
|
+
return this[RunSymbol$1](renderFetchSuccess(fetchHandler).pipe(Effect.tap(() => scheduleTelemetryFlush$1), Effect.provide(requestServices)));
|
|
1782
1796
|
}
|
|
1783
1797
|
queue(batch) {
|
|
1784
1798
|
const queueHandler = options.queue;
|
|
@@ -1787,7 +1801,7 @@ function make$9(layer, optionsOrFetch) {
|
|
|
1787
1801
|
return this[RunSymbol$1](queueHandler(fromMessageBatch(batch, messages)));
|
|
1788
1802
|
}
|
|
1789
1803
|
}
|
|
1790
|
-
defineEntrypointRpcMethods("Worker", EffectWorker.prototype, options.rpc, reservedMethodNames$2, (self, effect) => self[RunSymbol$1](effect));
|
|
1804
|
+
defineEntrypointRpcMethods("Worker", EffectWorker.prototype, options.rpc, reservedMethodNames$2, (self, effect) => self[RunSymbol$1](effect), () => scheduleTelemetryFlush$1);
|
|
1791
1805
|
return assumeEntrypointClass(EffectWorker);
|
|
1792
1806
|
}
|
|
1793
1807
|
/**
|
|
@@ -2080,6 +2094,7 @@ const reservedMethodNames = /* @__PURE__ */ new Set([
|
|
|
2080
2094
|
"webSocketError"
|
|
2081
2095
|
]);
|
|
2082
2096
|
const RunSymbol = Symbol.for("effect-cf/DurableObject/run");
|
|
2097
|
+
const scheduleTelemetryFlush = scheduleTelemetryFlush$2((flush) => Effect.flatMap(DurableObjectState, (state) => state.waitUntil(flush)));
|
|
2083
2098
|
/**
|
|
2084
2099
|
* Creates a Durable Object class backed by a single managed Effect runtime.
|
|
2085
2100
|
*/
|
|
@@ -2120,7 +2135,7 @@ const make$7 = (layer, options = {}) => {
|
|
|
2120
2135
|
if (options.webSocketError !== void 0) return this[RunSymbol](options.webSocketError(fromWebSocket(socket), error));
|
|
2121
2136
|
}
|
|
2122
2137
|
}
|
|
2123
|
-
defineEntrypointRpcMethods("Durable Object", EffectDurableObject.prototype, options.rpc, reservedMethodNames, (self, effect) => self[RunSymbol](effect));
|
|
2138
|
+
defineEntrypointRpcMethods("Durable Object", EffectDurableObject.prototype, options.rpc, reservedMethodNames, (self, effect) => self[RunSymbol](effect), () => scheduleTelemetryFlush);
|
|
2124
2139
|
return assumeEntrypointClass(EffectDurableObject);
|
|
2125
2140
|
};
|
|
2126
2141
|
//#endregion
|
package/package.json
CHANGED
package/src/CloudflareOtlp.ts
CHANGED
|
@@ -3,8 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Every layer exposes {@link OtlpExporter.Flusher}, which drains buffered
|
|
5
5
|
* telemetry on demand. Cloudflare isolates can freeze before the periodic
|
|
6
|
-
* export interval fires
|
|
7
|
-
*
|
|
6
|
+
* export interval fires. Effect-backed Worker fetch handlers and Worker or
|
|
7
|
+
* Durable Object native RPC handlers schedule this flusher automatically.
|
|
8
|
+
* Flush or scheduling failures do not replace the handler outcome and emit
|
|
9
|
+
* only bounded framework diagnostics without attaching the foreign cause.
|
|
10
|
+
* Other entrypoint lifecycles can flush explicitly or hand the flush to their
|
|
11
|
+
* `waitUntil` mechanism:
|
|
8
12
|
*
|
|
9
13
|
* ```ts
|
|
10
14
|
* const flusher = yield* OtlpExporter.Flusher;
|
package/src/DurableObject.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { DurableObjectState, fromDurableObjectState } from "./DurableObjectState
|
|
|
7
7
|
import { fromWebSocket, type DurableWebSocket } from "./DurableObjectWebSocket";
|
|
8
8
|
import * as Entrypoint from "./internal/Entrypoint";
|
|
9
9
|
import * as Runtime from "./internal/Runtime";
|
|
10
|
+
import * as Telemetry from "./internal/Telemetry";
|
|
10
11
|
|
|
11
12
|
const reservedMethodNames = new Set<string>([
|
|
12
13
|
"constructor",
|
|
@@ -29,6 +30,11 @@ type RunOptions = {
|
|
|
29
30
|
|
|
30
31
|
const RunSymbol = Symbol.for("effect-cf/DurableObject/run");
|
|
31
32
|
|
|
33
|
+
const scheduleTelemetryFlush: Effect.Effect<void, never, DurableObjectState> =
|
|
34
|
+
Telemetry.scheduleTelemetryFlush((flush) =>
|
|
35
|
+
Effect.flatMap(DurableObjectState, (state) => state.waitUntil(flush)),
|
|
36
|
+
);
|
|
37
|
+
|
|
32
38
|
/**
|
|
33
39
|
* Effect type for Durable Object lifecycle and RPC handlers.
|
|
34
40
|
*/
|
|
@@ -98,7 +104,12 @@ export interface DurableObjectOptions<
|
|
|
98
104
|
* Object storage if work must happen only once per id.
|
|
99
105
|
*/
|
|
100
106
|
readonly initialize?: Effect.Effect<void, unknown, HandlerContext<RRuntime>>;
|
|
101
|
-
/**
|
|
107
|
+
/**
|
|
108
|
+
* Optional RPC methods exposed as Durable Object instance methods.
|
|
109
|
+
*
|
|
110
|
+
* After every invocation, the configured OTLP flusher is scheduled through
|
|
111
|
+
* `DurableObjectState.waitUntil`, including when the handler fails.
|
|
112
|
+
*/
|
|
102
113
|
readonly rpc?: Rpc;
|
|
103
114
|
/** Optional fetch handler for HTTP/WebSocket requests. */
|
|
104
115
|
readonly fetch?: Effect.Effect<Response, unknown, FetchContext<RRuntime | REvent>>;
|
|
@@ -244,6 +255,7 @@ export const make = <
|
|
|
244
255
|
options.rpc,
|
|
245
256
|
reservedMethodNames,
|
|
246
257
|
(self, effect) => self[RunSymbol](effect),
|
|
258
|
+
() => scheduleTelemetryFlush,
|
|
247
259
|
);
|
|
248
260
|
|
|
249
261
|
return Entrypoint.assumeEntrypointClass<DurableObjectClass<Rpc, ROut | REvent>>(
|
package/src/Worker.ts
CHANGED
|
@@ -1,21 +1,13 @@
|
|
|
1
1
|
import { WorkerEntrypoint as CloudflareWorkerEntrypoint } from "cloudflare:workers";
|
|
2
|
-
import {
|
|
3
|
-
type Cause,
|
|
4
|
-
Context,
|
|
5
|
-
Effect,
|
|
6
|
-
Layer,
|
|
7
|
-
type ManagedRuntime,
|
|
8
|
-
Option,
|
|
9
|
-
type Scope,
|
|
10
|
-
} from "effect";
|
|
2
|
+
import { type Cause, Context, Effect, Layer, type ManagedRuntime, type Scope } from "effect";
|
|
11
3
|
import { HttpEffect, HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
|
|
12
|
-
import { OtlpExporter } from "effect/unstable/observability";
|
|
13
4
|
|
|
14
5
|
import { WorkerEnvironment, type WorkerEnv } from "./Environment";
|
|
15
6
|
import { fromMessage, fromMessageBatch, type QueueHandler } from "./Queue";
|
|
16
7
|
import type * as RpcDefinition from "./RpcDefinition";
|
|
17
8
|
import * as Entrypoint from "./internal/Entrypoint";
|
|
18
9
|
import * as Runtime from "./internal/Runtime";
|
|
10
|
+
import * as Telemetry from "./internal/Telemetry";
|
|
19
11
|
import { fromExecutionContext } from "./internal/WorkerContext";
|
|
20
12
|
|
|
21
13
|
/**
|
|
@@ -208,7 +200,12 @@ export interface WorkerOptions<
|
|
|
208
200
|
>;
|
|
209
201
|
/** Queue consumer handler invoked per message batch. */
|
|
210
202
|
readonly queue?: QueueHandler<RRuntime | REvent>;
|
|
211
|
-
/**
|
|
203
|
+
/**
|
|
204
|
+
* Optional RPC methods exposed as class instance methods.
|
|
205
|
+
*
|
|
206
|
+
* After every invocation, the configured OTLP flusher is scheduled through
|
|
207
|
+
* `WorkerContext.waitUntil`, including when the handler fails.
|
|
208
|
+
*/
|
|
212
209
|
readonly rpc?: Rpc;
|
|
213
210
|
}
|
|
214
211
|
|
|
@@ -292,14 +289,10 @@ const isWorkerOptions = <ROut, REvent, EventLayerError, Rpc extends WorkerRpc<RO
|
|
|
292
289
|
* (including streaming bodies still being consumed). It is a no-op when the
|
|
293
290
|
* context does not contain an `OtlpExporter.Flusher`.
|
|
294
291
|
*/
|
|
295
|
-
const scheduleTelemetryFlush: Effect.Effect<void, never, WorkerContext> =
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
onSome: (flusher) =>
|
|
300
|
-
Effect.flatMap(WorkerContext, (workerContext) => workerContext.waitUntil(flusher.flush)),
|
|
301
|
-
}),
|
|
302
|
-
);
|
|
292
|
+
const scheduleTelemetryFlush: Effect.Effect<void, never, WorkerContext> =
|
|
293
|
+
Telemetry.scheduleTelemetryFlush((flush) =>
|
|
294
|
+
Effect.flatMap(WorkerContext, (workerContext) => workerContext.waitUntil(flush)),
|
|
295
|
+
);
|
|
303
296
|
|
|
304
297
|
/**
|
|
305
298
|
* Creates a Cloudflare worker class backed by a single managed Effect runtime.
|
|
@@ -424,6 +417,7 @@ export function make<
|
|
|
424
417
|
options.rpc,
|
|
425
418
|
reservedMethodNames,
|
|
426
419
|
(self, effect) => self[RunSymbol](effect),
|
|
420
|
+
() => scheduleTelemetryFlush,
|
|
427
421
|
);
|
|
428
422
|
|
|
429
423
|
return Entrypoint.assumeEntrypointClass<WorkerClass<Rpc, ROut | REvent>>(EffectWorker);
|
|
@@ -19,6 +19,7 @@ export const defineEntrypointRpcMethods = <Self>(
|
|
|
19
19
|
rpc: EntrypointRpc | undefined,
|
|
20
20
|
reservedMethodNames: ReadonlySet<string>,
|
|
21
21
|
run: (self: Self, effect: Effect.Effect<any, any, any>) => Promise<unknown>,
|
|
22
|
+
onExit?: (self: Self) => Effect.Effect<void, never, any>,
|
|
22
23
|
): void => {
|
|
23
24
|
const methods = rpc ?? {};
|
|
24
25
|
|
|
@@ -28,11 +29,13 @@ export const defineEntrypointRpcMethods = <Self>(
|
|
|
28
29
|
Object.defineProperty(prototype, key, {
|
|
29
30
|
enumerable: true,
|
|
30
31
|
value(this: Self, ...args: AnyArgs) {
|
|
32
|
+
const handler = Effect.suspend(() => method(...args)).pipe(
|
|
33
|
+
Effect.mapError(RpcDefinition.encodeWireError),
|
|
34
|
+
);
|
|
35
|
+
|
|
31
36
|
return run(
|
|
32
37
|
this,
|
|
33
|
-
Effect.
|
|
34
|
-
Effect.mapError(RpcDefinition.encodeWireError),
|
|
35
|
-
),
|
|
38
|
+
onExit === undefined ? handler : handler.pipe(Effect.onExit(() => onExit(this))),
|
|
36
39
|
);
|
|
37
40
|
},
|
|
38
41
|
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Effect, Option } from "effect";
|
|
2
|
+
import { OtlpExporter } from "effect/unstable/observability";
|
|
3
|
+
|
|
4
|
+
const logFlushFailure = Effect.logError("Telemetry flush failed").pipe(Effect.ignoreCause);
|
|
5
|
+
|
|
6
|
+
const logFlushSchedulingFailure = Effect.logError("Telemetry flush scheduling failed").pipe(
|
|
7
|
+
Effect.ignoreCause,
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Schedules a configured OTLP flusher through the current Cloudflare event.
|
|
12
|
+
*/
|
|
13
|
+
export const scheduleTelemetryFlush = <R>(
|
|
14
|
+
waitUntil: (flush: Effect.Effect<void>) => Effect.Effect<void, never, R>,
|
|
15
|
+
): Effect.Effect<void, never, R> =>
|
|
16
|
+
Effect.gen(function* () {
|
|
17
|
+
const flusher = yield* Effect.serviceOption(OtlpExporter.Flusher);
|
|
18
|
+
|
|
19
|
+
if (Option.isNone(flusher)) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Exporters and platform schedulers are foreign boundaries. Preserve a
|
|
24
|
+
// useful diagnostic without retaining their arbitrary failure causes in
|
|
25
|
+
// logs, where payloads, headers, or credentials could otherwise escape.
|
|
26
|
+
yield* waitUntil(flusher.value.flush.pipe(Effect.catchCause(() => logFlushFailure)));
|
|
27
|
+
}).pipe(Effect.catchCause(() => logFlushSchedulingFailure));
|