@rivetkit/effect 0.0.0-codex-rivetkit-cli-preview.5e414a8

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.
Files changed (89) hide show
  1. package/dist/Action.d.ts +104 -0
  2. package/dist/Action.d.ts.map +1 -0
  3. package/dist/Action.js +50 -0
  4. package/dist/Action.js.map +1 -0
  5. package/dist/Actor.d.ts +133 -0
  6. package/dist/Actor.d.ts.map +1 -0
  7. package/dist/Actor.js +104 -0
  8. package/dist/Actor.js.map +1 -0
  9. package/dist/Client.d.ts +31 -0
  10. package/dist/Client.d.ts.map +1 -0
  11. package/dist/Client.js +98 -0
  12. package/dist/Client.js.map +1 -0
  13. package/dist/Logger.d.ts +29 -0
  14. package/dist/Logger.d.ts.map +1 -0
  15. package/dist/Logger.js +31 -0
  16. package/dist/Logger.js.map +1 -0
  17. package/dist/Registry.d.ts +72 -0
  18. package/dist/Registry.d.ts.map +1 -0
  19. package/dist/Registry.js +125 -0
  20. package/dist/Registry.js.map +1 -0
  21. package/dist/RivetError.d.ts +438 -0
  22. package/dist/RivetError.d.ts.map +1 -0
  23. package/dist/RivetError.js +873 -0
  24. package/dist/RivetError.js.map +1 -0
  25. package/dist/State.d.ts +123 -0
  26. package/dist/State.d.ts.map +1 -0
  27. package/dist/State.js +104 -0
  28. package/dist/State.js.map +1 -0
  29. package/dist/internal/ActionDispatcher.d.ts +14 -0
  30. package/dist/internal/ActionDispatcher.d.ts.map +1 -0
  31. package/dist/internal/ActionDispatcher.js +100 -0
  32. package/dist/internal/ActionDispatcher.js.map +1 -0
  33. package/dist/internal/ActionErrorEnvelope.d.ts +11 -0
  34. package/dist/internal/ActionErrorEnvelope.d.ts.map +1 -0
  35. package/dist/internal/ActionErrorEnvelope.js +14 -0
  36. package/dist/internal/ActionErrorEnvelope.js.map +1 -0
  37. package/dist/internal/ActorInstanceManager.d.ts +28 -0
  38. package/dist/internal/ActorInstanceManager.d.ts.map +1 -0
  39. package/dist/internal/ActorInstanceManager.js +51 -0
  40. package/dist/internal/ActorInstanceManager.js.map +1 -0
  41. package/dist/internal/ActorStateAdapter.d.ts +18 -0
  42. package/dist/internal/ActorStateAdapter.d.ts.map +1 -0
  43. package/dist/internal/ActorStateAdapter.js +29 -0
  44. package/dist/internal/ActorStateAdapter.js.map +1 -0
  45. package/dist/internal/StateOptions.d.ts +12 -0
  46. package/dist/internal/StateOptions.d.ts.map +1 -0
  47. package/dist/internal/StateOptions.js +2 -0
  48. package/dist/internal/StateOptions.js.map +1 -0
  49. package/dist/internal/logging.d.ts +23 -0
  50. package/dist/internal/logging.d.ts.map +1 -0
  51. package/dist/internal/logging.js +162 -0
  52. package/dist/internal/logging.js.map +1 -0
  53. package/dist/internal/tracing.d.ts +23 -0
  54. package/dist/internal/tracing.d.ts.map +1 -0
  55. package/dist/internal/tracing.js +30 -0
  56. package/dist/internal/tracing.js.map +1 -0
  57. package/dist/internal/utils.d.ts +7 -0
  58. package/dist/internal/utils.d.ts.map +1 -0
  59. package/dist/internal/utils.js +7 -0
  60. package/dist/internal/utils.js.map +1 -0
  61. package/dist/mod.d.ts +8 -0
  62. package/dist/mod.d.ts.map +1 -0
  63. package/dist/mod.js +8 -0
  64. package/dist/mod.js.map +1 -0
  65. package/package.json +46 -0
  66. package/src/Action.ts +231 -0
  67. package/src/Actor.test-d.ts +603 -0
  68. package/src/Actor.test.ts +206 -0
  69. package/src/Actor.ts +550 -0
  70. package/src/Client.test.ts +210 -0
  71. package/src/Client.ts +216 -0
  72. package/src/Logger.ts +43 -0
  73. package/src/Registry.test-d.ts +126 -0
  74. package/src/Registry.test.ts +411 -0
  75. package/src/Registry.ts +243 -0
  76. package/src/RivetError.test.ts +188 -0
  77. package/src/RivetError.ts +1044 -0
  78. package/src/State.test.ts +181 -0
  79. package/src/State.ts +224 -0
  80. package/src/internal/ActionDispatcher.ts +192 -0
  81. package/src/internal/ActionErrorEnvelope.ts +19 -0
  82. package/src/internal/ActorInstanceManager.ts +143 -0
  83. package/src/internal/ActorStateAdapter.ts +88 -0
  84. package/src/internal/StateOptions.ts +17 -0
  85. package/src/internal/logging.test.ts +288 -0
  86. package/src/internal/logging.ts +237 -0
  87. package/src/internal/tracing.ts +42 -0
  88. package/src/internal/utils.ts +12 -0
  89. package/src/mod.ts +7 -0
@@ -0,0 +1,210 @@
1
+ import { assert, describe, it } from "@effect/vitest";
2
+ import { Client, Logger, RivetError } from "@rivetkit/effect";
3
+ import { Effect, Layer, Schema } from "effect";
4
+ import * as RivetkitErrors from "rivetkit/errors";
5
+ import {
6
+ configureDefaultLogger,
7
+ getBaseLogger,
8
+ type Logger as PinoLogger,
9
+ } from "rivetkit/log";
10
+ import * as ActionErrorEnvelope from "./internal/ActionErrorEnvelope";
11
+
12
+ function makeTestLogger(
13
+ entries?: Array<{
14
+ readonly level: string;
15
+ readonly fields: Record<string, unknown>;
16
+ readonly msg: string | undefined;
17
+ }>,
18
+ ): PinoLogger {
19
+ const logger: Record<string, unknown> = {
20
+ level: "debug",
21
+ child: () => logger,
22
+ };
23
+ for (const level of [
24
+ "trace",
25
+ "debug",
26
+ "info",
27
+ "warn",
28
+ "error",
29
+ "fatal",
30
+ ]) {
31
+ logger[level] = (
32
+ fields: Record<string, unknown>,
33
+ msg?: string,
34
+ ): void => {
35
+ entries?.push({ level, fields, msg });
36
+ };
37
+ }
38
+
39
+ return logger as unknown as PinoLogger;
40
+ }
41
+
42
+ describe("Client", () => {
43
+ it.effect("configures the underlying RivetKit client logger", () =>
44
+ Effect.scoped(
45
+ Effect.gen(function* () {
46
+ const baseLogger = makeTestLogger();
47
+
48
+ yield* Effect.addFinalizer(() =>
49
+ Effect.sync(() => configureDefaultLogger("silent")),
50
+ );
51
+ yield* Client.make({
52
+ endpoint: "http://127.0.0.1:6420",
53
+ }).pipe(Effect.provide(Logger.layerPino(baseLogger)));
54
+
55
+ assert.strictEqual(getBaseLogger(), baseLogger);
56
+ }),
57
+ ),
58
+ );
59
+
60
+ it.effect("installs the RivetKit Effect logger for client programs", () =>
61
+ Effect.scoped(
62
+ Effect.gen(function* () {
63
+ const entries: Array<{
64
+ readonly level: string;
65
+ readonly fields: Record<string, unknown>;
66
+ readonly msg: string | undefined;
67
+ }> = [];
68
+ const baseLogger = makeTestLogger(entries);
69
+
70
+ yield* Effect.addFinalizer(() =>
71
+ Effect.sync(() => configureDefaultLogger("silent")),
72
+ );
73
+ yield* Effect.gen(function* () {
74
+ yield* Client.Client;
75
+ yield* Effect.logInfo("client effect log", {
76
+ clientId: "test-client",
77
+ });
78
+ }).pipe(
79
+ Effect.provide(
80
+ Client.layer({
81
+ endpoint: "http://127.0.0.1:6420",
82
+ }).pipe(
83
+ Layer.provideMerge(Logger.layerPino(baseLogger)),
84
+ ),
85
+ ),
86
+ );
87
+
88
+ assert.deepStrictEqual(entries[0], {
89
+ level: "info",
90
+ fields: { clientId: "test-client" },
91
+ msg: "client effect log",
92
+ });
93
+ assert.ok(
94
+ entries.some(
95
+ (entry) =>
96
+ entry.level === "debug" &&
97
+ (entry.fields as { msg?: unknown }).msg ===
98
+ "disposing client",
99
+ ),
100
+ );
101
+ }),
102
+ ),
103
+ );
104
+ });
105
+
106
+ describe("makeRivetkitActionFailureClassifier", () => {
107
+ const ExpectedError = Schema.Struct({
108
+ _tag: Schema.tag("CounterOverflow"),
109
+ message: Schema.String,
110
+ limit: Schema.Number,
111
+ });
112
+ const classifyRivetkitActionFailure =
113
+ Client.makeRivetkitActionFailureClassifier(ExpectedError);
114
+
115
+ it.effect("preserves non-Rivet failures as UnknownError", () =>
116
+ Effect.gen(function* () {
117
+ const cause = new Error("plain failure");
118
+ const error = yield* classifyRivetkitActionFailure(cause);
119
+
120
+ assert.instanceOf(error, RivetError.RivetError);
121
+ assert.instanceOf(error.reason, RivetError.UnknownError);
122
+ assert.strictEqual(error.reason.message, "plain failure");
123
+ assert.strictEqual(error.reason.cause, cause);
124
+ }),
125
+ );
126
+
127
+ it.effect("preserves structured non-action Rivet errors", () =>
128
+ Effect.gen(function* () {
129
+ const cause = new RivetkitErrors.RivetError(
130
+ "actor",
131
+ "not_found",
132
+ "actor not found",
133
+ );
134
+ const error = yield* classifyRivetkitActionFailure(cause);
135
+
136
+ assert.instanceOf(error, RivetError.RivetError);
137
+ assert.instanceOf(error.reason, RivetError.ActorNotFound);
138
+ assert.strictEqual(error.reason.cause.group, "actor");
139
+ assert.strictEqual(error.reason.cause.code, "not_found");
140
+ assert.strictEqual(error.reason.cause.message, "actor not found");
141
+ }),
142
+ );
143
+
144
+ it.effect(
145
+ "decodes action-error metadata into the declared error type",
146
+ () =>
147
+ Effect.gen(function* () {
148
+ const cause = new RivetkitErrors.RivetError(
149
+ "user",
150
+ "CounterOverflow",
151
+ "counter overflow",
152
+ {
153
+ public: true,
154
+ metadata: {
155
+ _tag: ActionErrorEnvelope.tag,
156
+ version: ActionErrorEnvelope.schemaVersion,
157
+ error: {
158
+ _tag: "CounterOverflow",
159
+ message: "counter overflow",
160
+ limit: 10,
161
+ },
162
+ },
163
+ },
164
+ );
165
+ const error = yield* classifyRivetkitActionFailure(cause);
166
+
167
+ assert.deepStrictEqual(error, {
168
+ _tag: "CounterOverflow",
169
+ message: "counter overflow",
170
+ limit: 10,
171
+ });
172
+ }),
173
+ );
174
+
175
+ it.effect(
176
+ "wraps invalid typed action-error payloads in ActionErrorDecodeFailed",
177
+ () =>
178
+ Effect.gen(function* () {
179
+ const cause = new RivetkitErrors.RivetError(
180
+ "user",
181
+ "CounterOverflow",
182
+ "counter overflow",
183
+ {
184
+ metadata: {
185
+ _tag: ActionErrorEnvelope.tag,
186
+ version: ActionErrorEnvelope.schemaVersion,
187
+ error: {
188
+ _tag: "CounterOverflow",
189
+ message: "counter overflow",
190
+ limit: "10",
191
+ },
192
+ },
193
+ },
194
+ );
195
+
196
+ const error = yield* classifyRivetkitActionFailure(cause);
197
+
198
+ assert.instanceOf(error, RivetError.RivetError);
199
+ assert.instanceOf(
200
+ error.reason,
201
+ RivetError.ActionErrorDecodeFailed,
202
+ );
203
+ assert.strictEqual(error.reason.rivetError.group, "user");
204
+ assert.strictEqual(
205
+ error.reason.rivetError.code,
206
+ "CounterOverflow",
207
+ );
208
+ }),
209
+ );
210
+ });
package/src/Client.ts ADDED
@@ -0,0 +1,216 @@
1
+ import { Context, Effect, Layer, Record, Result, Schema } from "effect";
2
+ import * as RivetkitClient from "rivetkit/client";
3
+ import * as RivetkitErrors from "rivetkit/errors";
4
+ import { configureBaseLogger } from "rivetkit/log";
5
+ import type * as Action from "./Action.ts";
6
+ import type * as Actor from "./Actor.ts";
7
+ import * as ActionErrorEnvelope from "./internal/ActionErrorEnvelope.ts";
8
+ import { getOrCreateBaseLogger } from "./internal/logging.ts";
9
+ import { rpcSystem, type TraceMeta } from "./internal/tracing.ts";
10
+ import * as Logger from "./Logger.ts";
11
+ import * as RivetError from "./RivetError.ts";
12
+
13
+ const TypeId = "~@rivetkit/effect/Client";
14
+
15
+ /**
16
+ * Connection options for the Rivet Engine client transport. Mirrors
17
+ * the `(endpoint, token, namespace)` subset of rivetkit's
18
+ * `ClientConfigInput`.
19
+ */
20
+ export type Options = Pick<
21
+ RivetkitClient.ClientConfigInput,
22
+ "endpoint" | "token" | "namespace"
23
+ >;
24
+
25
+ /**
26
+ * Per-call metadata envelope shipped as `args[1]` alongside the encoded
27
+ * payload. The SDK currently uses it for trace propagation (`trace`),
28
+ * but it's intentionally extensible so future cross-cutting concerns —
29
+ * idempotency keys, deadlines, custom headers — can land as additional
30
+ * optional fields without changing the wire shape.
31
+ */
32
+ export interface ActionMeta {
33
+ readonly trace?: TraceMeta;
34
+ }
35
+
36
+ export interface Client {
37
+ readonly [TypeId]: typeof TypeId;
38
+
39
+ readonly makeActorAccessor: <Actions extends Action.AnyWithProps>(
40
+ actor: Actor.Actor<string, Actions>,
41
+ ) => Actor.Accessor<Actions>;
42
+ }
43
+
44
+ export const Client: Context.Service<Client, Client> = Context.Service<Client>(
45
+ "@rivetkit/effect/Client",
46
+ );
47
+
48
+ export const make = Effect.fnUntraced(function* (options: Options = {}) {
49
+ const baseLogger = yield* getOrCreateBaseLogger;
50
+ const rivetkitClient = yield* Effect.acquireRelease(
51
+ Effect.sync(() => {
52
+ configureBaseLogger(baseLogger);
53
+ return RivetkitClient.createClient(options);
54
+ }),
55
+ (c) => Effect.promise(() => c.dispose()),
56
+ );
57
+
58
+ return Client.of({
59
+ [TypeId]: TypeId,
60
+ makeActorAccessor: (actor) => ({
61
+ getOrCreate: (key) => {
62
+ const rivetkitActorHandle = rivetkitClient.getOrCreate(
63
+ actor.name,
64
+ key,
65
+ );
66
+
67
+ return Record.fromIterableWith(actor.actions, (action) => {
68
+ const encodePayload = Schema.encodeEffect(
69
+ Schema.toCodecJson(action.payloadSchema),
70
+ );
71
+ const decodeSuccess = Schema.decodeUnknownEffect(
72
+ Schema.toCodecJson(action.successSchema),
73
+ );
74
+ const classifyRivetkitActionFailure =
75
+ makeRivetkitActionFailureClassifier(action.errorSchema);
76
+
77
+ const rpcMethod = `${actor.name}/${action._tag}`;
78
+
79
+ return [
80
+ action._tag,
81
+ Effect.fn(rpcMethod, {
82
+ kind: "client",
83
+ attributes: {
84
+ "rpc.system.name": rpcSystem,
85
+ "rpc.method": rpcMethod,
86
+ },
87
+ })(function* (payload: unknown) {
88
+ const span = yield* Effect.currentSpan;
89
+ const meta: ActionMeta = {
90
+ trace: {
91
+ traceId: span.traceId,
92
+ spanId: span.spanId,
93
+ sampled: span.sampled,
94
+ },
95
+ };
96
+ const encodedPayload = yield* encodePayload(
97
+ payload,
98
+ ).pipe(
99
+ Effect.mapError(
100
+ (cause) =>
101
+ new RivetError.RivetError({
102
+ reason: new RivetError.InvalidEncoding(
103
+ {
104
+ cause: new RivetkitErrors.RivetError(
105
+ "encoding",
106
+ "invalid",
107
+ "Could not encode action payload",
108
+ {
109
+ public: true,
110
+ metadata: cause,
111
+ },
112
+ ),
113
+ },
114
+ ),
115
+ }),
116
+ ),
117
+ );
118
+
119
+ const encodedSuccess = yield* Effect.tryPromise(
120
+ (abortSignal) =>
121
+ rivetkitActorHandle.action({
122
+ name: action._tag,
123
+ args: [encodedPayload, meta],
124
+ signal: abortSignal,
125
+ }),
126
+ ).pipe(
127
+ Effect.catch((unknownError) =>
128
+ classifyRivetkitActionFailure(
129
+ unknownError.cause,
130
+ ).pipe(Effect.flatMap(Effect.fail)),
131
+ ),
132
+ );
133
+
134
+ return yield* decodeSuccess(encodedSuccess).pipe(
135
+ Effect.orDie,
136
+ );
137
+ }),
138
+ ];
139
+ }) as Actor.Handle<(typeof actor.actions)[number]>;
140
+ },
141
+ }),
142
+ });
143
+ });
144
+
145
+ export const layer = (options: Options = {}): Layer.Layer<Client> =>
146
+ Layer.unwrap(
147
+ Effect.map(getOrCreateBaseLogger, (baseLogger) =>
148
+ Layer.effect(Client, make(options)).pipe(
149
+ Layer.provideMerge(Logger.layerPino(baseLogger)),
150
+ ),
151
+ ),
152
+ );
153
+
154
+ const decodeActionErrorEnvelope = Schema.decodeUnknownEffect(
155
+ ActionErrorEnvelope.ActionErrorEnvelope,
156
+ );
157
+
158
+ /** @internal */
159
+ export const makeRivetkitActionFailureClassifier = <
160
+ ActionErrorSchema extends Schema.Codec<unknown, unknown, unknown, unknown>,
161
+ >(
162
+ actionErrorSchema: ActionErrorSchema,
163
+ ): ((
164
+ cause: unknown,
165
+ ) => Effect.Effect<
166
+ ActionErrorSchema["Type"] | RivetError.RivetError,
167
+ never,
168
+ ActionErrorSchema["DecodingServices"]
169
+ >) => {
170
+ const decodeActionError = Schema.decodeUnknownEffect(
171
+ Schema.toCodecJson(actionErrorSchema),
172
+ );
173
+
174
+ return Effect.fnUntraced(function* (
175
+ cause: unknown,
176
+ ): Effect.fn.Return<
177
+ ActionErrorSchema["Type"] | RivetError.RivetError,
178
+ never,
179
+ ActionErrorSchema["DecodingServices"]
180
+ > {
181
+ // In the case where the `cause` is not a `RivetError`. In principle, this shouldn't happen.
182
+ if (!RivetkitErrors.isRivetErrorLike(cause)) {
183
+ return RivetError.fromUnknown(cause);
184
+ }
185
+
186
+ const rivetkitRivetError = RivetkitErrors.toRivetError(cause);
187
+
188
+ const actionErrorEnvelope = yield* Effect.result(
189
+ decodeActionErrorEnvelope(rivetkitRivetError.metadata),
190
+ );
191
+
192
+ // If the error's `metadata` is not a valid action error envelope, then
193
+ // it means it's not a user-declared action error.
194
+ if (Result.isFailure(actionErrorEnvelope)) {
195
+ return RivetError.fromRivetkitRivetError(rivetkitRivetError);
196
+ }
197
+
198
+ const actionErrorResult = yield* Effect.result(
199
+ decodeActionError(actionErrorEnvelope.success.error),
200
+ );
201
+
202
+ // The envelope was valid, but the inner payload doesn't match the
203
+ // declared schema — surface as `ActionErrorDecodeFailed`
204
+ if (Result.isFailure(actionErrorResult)) {
205
+ return new RivetError.RivetError({
206
+ reason: new RivetError.ActionErrorDecodeFailed({
207
+ cause: actionErrorResult.failure,
208
+ rivetError: rivetkitRivetError,
209
+ }),
210
+ });
211
+ }
212
+
213
+ // Successfully decoded user-declared action error
214
+ return actionErrorResult.success;
215
+ });
216
+ };
package/src/Logger.ts ADDED
@@ -0,0 +1,43 @@
1
+ import { Effect, Logger as EffectLogger, Layer } from "effect";
2
+ import type { Logger as PinoLogger } from "rivetkit/log";
3
+ import {
4
+ BaseLogger,
5
+ getOrCreateBaseLogger,
6
+ makeEffectLogger,
7
+ } from "./internal/logging.ts";
8
+
9
+ /**
10
+ * Builds a logging layer from a custom Pino-compatible logger.
11
+ *
12
+ * The layer installs the matching Effect logger and configures the underlying
13
+ * RivetKit TypeScript SDK logs to go through the same logger.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * import { Logger } from "@rivetkit/effect"
18
+ * import { pino } from "pino"
19
+ *
20
+ * const LoggerLive = Logger.layerPino(
21
+ * pino({ transport: { target: "pino-pretty" } })
22
+ * )
23
+ * ```
24
+ */
25
+ export const layerPino = (baseLogger: PinoLogger) =>
26
+ Layer.mergeAll(
27
+ Layer.succeed(BaseLogger, baseLogger),
28
+ EffectLogger.layer([
29
+ EffectLogger.tracerLogger,
30
+ makeEffectLogger(baseLogger),
31
+ ]),
32
+ );
33
+
34
+ /**
35
+ * Default RivetKit Effect logging layer.
36
+ *
37
+ * The layer creates a base logger from `References.MinimumLogLevel` and installs
38
+ * the Effect logger adapter. Applications that want custom formatting or
39
+ * transports should provide {@link layerPino} instead.
40
+ */
41
+ export const layer: Layer.Layer<never> = Layer.unwrap(
42
+ Effect.map(getOrCreateBaseLogger, layerPino),
43
+ );
@@ -0,0 +1,126 @@
1
+ import { Action, Actor, Registry } from "@rivetkit/effect";
2
+ import { type Context, Effect, Layer, type Scope } from "effect";
3
+ import type {
4
+ HttpServerError,
5
+ HttpServerRequest,
6
+ HttpServerResponse,
7
+ } from "effect/unstable/http";
8
+ import { describe, expectTypeOf, test } from "vitest";
9
+
10
+ const TestActor = Actor.make("TestActor", {
11
+ actions: [Action.make("Test")],
12
+ });
13
+
14
+ const TestActorLive = TestActor.toLayer({
15
+ Test: () => Effect.void,
16
+ });
17
+
18
+ const RegistryLive = TestActorLive.pipe(
19
+ Layer.provideMerge(Registry.layer({ endpoint: "http://127.0.0.1:6420" })),
20
+ );
21
+
22
+ describe("Registry.layer", () => {
23
+ test("accepts connection options", () => {
24
+ expectTypeOf(Registry.layer).toBeCallableWith({
25
+ endpoint: "http://127.0.0.1:6420",
26
+ token: "dev-token",
27
+ namespace: "default",
28
+ noWelcome: true,
29
+ });
30
+ });
31
+
32
+ test("does not accept serverless options", () => {
33
+ Registry.layer({
34
+ // @ts-expect-error: serverless routing belongs to toWebHandler and toHttpEffect options.
35
+ serverless: {
36
+ basePath: "/",
37
+ },
38
+ });
39
+ });
40
+ });
41
+
42
+ describe("Registry.serve", () => {
43
+ test("accepts an actor registration layer", () => {
44
+ expectTypeOf(Registry.serve).toBeCallableWith(TestActorLive);
45
+ });
46
+
47
+ test("returns a server layer that requires Registry", () => {
48
+ expectTypeOf(Registry.serve(TestActorLive)).toEqualTypeOf<
49
+ Layer.Layer<never, never, Registry.Registry>
50
+ >();
51
+ });
52
+ });
53
+
54
+ describe("Registry.toWebHandler", () => {
55
+ test("accepts a registry layer", () => {
56
+ expectTypeOf(Registry.toWebHandler).toBeCallableWith(RegistryLive);
57
+ });
58
+
59
+ test("rejects actor registration layers that do not provide Registry", () => {
60
+ // @ts-expect-error: actor registration layers require Registry but do not provide it.
61
+ // @effect-diagnostics effect/missingLayerContext:off effect/floatingEffect:off
62
+ Registry.toWebHandler(TestActorLive);
63
+ });
64
+
65
+ test("accepts serverless routing options", () => {
66
+ expectTypeOf(Registry.toWebHandler).toBeCallableWith(RegistryLive, {
67
+ basePath: "/",
68
+ maxStartPayloadBytes: 1024,
69
+ });
70
+ });
71
+
72
+ test("rejects registry options", () => {
73
+ Registry.toWebHandler(RegistryLive, {
74
+ // @ts-expect-error: noWelcome belongs to Registry.layer options.
75
+ noWelcome: true,
76
+ });
77
+ });
78
+
79
+ test("returns a Fetch-compatible handler", () => {
80
+ const handler = Registry.toWebHandler(RegistryLive);
81
+
82
+ expectTypeOf(handler.handler).toEqualTypeOf<
83
+ (
84
+ request: Request,
85
+ context?: Context.Context<never> | undefined,
86
+ ) => Promise<Response>
87
+ >();
88
+ expectTypeOf(handler.dispose).toEqualTypeOf<() => Promise<void>>();
89
+ });
90
+ });
91
+
92
+ describe("Registry.toHttpEffect", () => {
93
+ test("accepts serverless routing options", () => {
94
+ expectTypeOf(Registry.toHttpEffect).toBeCallableWith(RegistryLive, {
95
+ basePath: "/",
96
+ maxStartPayloadBytes: 1024,
97
+ });
98
+ });
99
+
100
+ test("rejects registry options", () => {
101
+ Registry.toHttpEffect(RegistryLive, {
102
+ // @ts-expect-error: noWelcome belongs to Registry.layer options.
103
+ noWelcome: true,
104
+ });
105
+ });
106
+
107
+ test("rejects actor registration layers that do not provide Registry", () => {
108
+ // @ts-expect-error: actor registration layers require Registry but do not provide it.
109
+ // @effect-diagnostics effect/missingLayerContext:off effect/floatingEffect:off
110
+ Registry.toHttpEffect(TestActorLive);
111
+ });
112
+
113
+ test("returns a scoped Effect HTTP handler", () => {
114
+ expectTypeOf(Registry.toHttpEffect(RegistryLive)).toEqualTypeOf<
115
+ Effect.Effect<
116
+ Effect.Effect<
117
+ HttpServerResponse.HttpServerResponse,
118
+ HttpServerError.HttpServerError,
119
+ HttpServerRequest.HttpServerRequest
120
+ >,
121
+ never,
122
+ Scope.Scope
123
+ >
124
+ >();
125
+ });
126
+ });