From fac46ce03a01446da5fe74a95f18754068b40f24 Mon Sep 17 00:00:00 2001 From: Zane Date: Sun, 23 Aug 2026 22:42:36 +0800 Subject: [PATCH] fix(effect): forward all RivetKit actor options, not just name and icon --- .../packages/effect/src/Actor.test.ts | 86 ++++++++++++++++++- .../packages/effect/src/Actor.ts | 22 +++-- 2 files changed, 94 insertions(+), 14 deletions(-) diff --git a/rivetkit-typescript/packages/effect/src/Actor.test.ts b/rivetkit-typescript/packages/effect/src/Actor.test.ts index 281bc9d57a..d3ad4d78d3 100644 --- a/rivetkit-typescript/packages/effect/src/Actor.test.ts +++ b/rivetkit-typescript/packages/effect/src/Actor.test.ts @@ -1,6 +1,6 @@ import { assert, describe, it } from "@effect/vitest"; -import { Actor, State } from "@rivetkit/effect"; -import { Context, Effect, Layer } from "effect"; +import { Action, Actor, Registry, State } from "@rivetkit/effect"; +import { Context, Effect, Layer, Schema } from "effect"; import type * as Rivetkit from "rivetkit"; class Prefix extends Context.Service()( @@ -207,3 +207,85 @@ describe("Actor.toWakeHandler", () => { }), ); }); + +describe("Actor.toLayer options", () => { + const Optioned = Actor.make("Optioned", { + actions: [Action.make("Noop")], + }); + const noop = { Noop: () => Effect.void }; + + /** + * Builds an actor layer against a throwaway registry and hands back + * the options the underlying RivetKit actor was actually created + * with, after RivetKit has parsed them and filled in its defaults. + */ + const builtOptions = ( + actorsLayer: Layer.Layer, + ) => + Effect.gen(function* () { + const registry = yield* Registry.Registry; + const built = registry.rivetkitActors.get("Optioned"); + + assert.ok(built, "expected Optioned to be registered"); + + return built.config.options; + }).pipe( + Effect.provide( + actorsLayer.pipe( + Layer.provideMerge(Registry.layer({ noWelcome: true })), + ), + ), + ); + + it.effect("forwards instance options to the RivetKit actor", () => + Effect.gen(function* () { + const options = yield* builtOptions( + Optioned.toLayer(noop, { + actionTimeout: 1_234, + sleepTimeout: 5_678, + maxQueueMessageSize: 128 * 1024, + }), + ); + + assert.strictEqual(options.actionTimeout, 1_234); + assert.strictEqual(options.sleepTimeout, 5_678); + assert.strictEqual(options.maxQueueMessageSize, 128 * 1024); + }), + ); + + it.effect("keeps forwarding the Inspector display options", () => + Effect.gen(function* () { + const options = yield* builtOptions( + Optioned.toLayer(noop, { name: "Display", icon: "rocket" }), + ); + + assert.strictEqual(options.name, "Display"); + assert.strictEqual(options.icon, "rocket"); + }), + ); + + it.effect("leaves RivetKit's own defaults in place when unset", () => + Effect.gen(function* () { + const options = yield* builtOptions(Optioned.toLayer(noop, {})); + + assert.isNumber(options.actionTimeout); + assert.isNumber(options.sleepTimeout); + }), + ); + + it.effect("does not forward the SDK-only options", () => + Effect.gen(function* () { + const options = yield* builtOptions( + Optioned.toLayer(noop, { + state: { + schema: Schema.Number, + initialValue: () => 0, + }, + }), + ); + + assert.isFalse("state" in options); + assert.isFalse("db" in options); + }), + ); +}); diff --git a/rivetkit-typescript/packages/effect/src/Actor.ts b/rivetkit-typescript/packages/effect/src/Actor.ts index a9383c567e..237f52393c 100644 --- a/rivetkit-typescript/packages/effect/src/Actor.ts +++ b/rivetkit-typescript/packages/effect/src/Actor.ts @@ -27,17 +27,15 @@ const TypeId = "~@rivetkit/effect/Actor"; export const isActor = (u: unknown): u is Actor => Predicate.hasProperty(u, TypeId); -const rivetkitActorOptionsKeys = [ - "name", - "icon", -] as const satisfies ReadonlyArray< - keyof NonNullable ->; +/** + * The option keys this SDK consumes itself instead of handing to + * RivetKit. Everything else in {@link Options} is forwarded verbatim, + * so a new RivetKit actor option is usable here as soon as it lands + * upstream, with no change to this file. + */ +const effectActorOptionsKeys = ["state", "db"] as const; -export type RivetkitActorOptions = Pick< - NonNullable, - (typeof rivetkitActorOptionsKeys)[number] ->; +export type RivetkitActorOptions = NonNullable; /** * Per-actor instance options. Combines the public @@ -73,8 +71,8 @@ const splitOptions = < >( options: Options, ) => ({ - rivetkitOptions: Struct.pick(options, rivetkitActorOptionsKeys), - effectOptions: Struct.omit(options, rivetkitActorOptionsKeys), + rivetkitOptions: Struct.omit(options, effectActorOptionsKeys), + effectOptions: Struct.pick(options, effectActorOptionsKeys), }); /**