@rivetkit/effect 0.0.0-main.9d355a2 → 0.0.0-main.b2d3b94

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 (63) hide show
  1. package/dist/Actor.d.ts +2 -3
  2. package/dist/Actor.d.ts.map +1 -1
  3. package/dist/Actor.js +2 -2
  4. package/dist/Actor.js.map +1 -1
  5. package/dist/Client.d.ts.map +1 -1
  6. package/dist/Client.js +6 -4
  7. package/dist/Client.js.map +1 -1
  8. package/dist/Registry.d.ts +69 -7
  9. package/dist/Registry.d.ts.map +1 -1
  10. package/dist/Registry.js +93 -40
  11. package/dist/Registry.js.map +1 -1
  12. package/dist/RivetError.d.ts +45 -3
  13. package/dist/RivetError.d.ts.map +1 -1
  14. package/dist/RivetError.js +112 -0
  15. package/dist/RivetError.js.map +1 -1
  16. package/dist/RivetLogger.d.ts +41 -0
  17. package/dist/RivetLogger.d.ts.map +1 -0
  18. package/dist/RivetLogger.js +41 -0
  19. package/dist/RivetLogger.js.map +1 -0
  20. package/dist/State.d.ts +92 -56
  21. package/dist/State.d.ts.map +1 -1
  22. package/dist/State.js +51 -57
  23. package/dist/State.js.map +1 -1
  24. package/dist/internal/ActorInstanceManager.d.ts.map +1 -1
  25. package/dist/internal/ActorInstanceManager.js +4 -4
  26. package/dist/internal/ActorInstanceManager.js.map +1 -1
  27. package/dist/internal/ActorStateAdapter.d.ts +1 -1
  28. package/dist/internal/ActorStateAdapter.d.ts.map +1 -1
  29. package/dist/internal/ActorStateAdapter.js +2 -7
  30. package/dist/internal/ActorStateAdapter.js.map +1 -1
  31. package/dist/internal/StateOptions.d.ts +1 -0
  32. package/dist/internal/StateOptions.d.ts.map +1 -1
  33. package/dist/internal/logging.d.ts +6 -7
  34. package/dist/internal/logging.d.ts.map +1 -1
  35. package/dist/internal/logging.js +78 -102
  36. package/dist/internal/logging.js.map +1 -1
  37. package/dist/mod.d.ts +1 -1
  38. package/dist/mod.d.ts.map +1 -1
  39. package/dist/mod.js +1 -1
  40. package/dist/mod.js.map +1 -1
  41. package/package.json +8 -3
  42. package/src/Actor.test-d.ts +38 -0
  43. package/src/Actor.ts +29 -4
  44. package/src/Client.test.ts +21 -18
  45. package/src/Client.ts +6 -4
  46. package/src/Registry.test.ts +7 -11
  47. package/src/Registry.ts +116 -50
  48. package/src/RivetError.test.ts +11 -0
  49. package/src/RivetError.ts +119 -0
  50. package/src/RivetLogger.ts +60 -0
  51. package/src/State.test.ts +163 -4
  52. package/src/State.ts +293 -97
  53. package/src/internal/ActorInstanceManager.ts +4 -10
  54. package/src/internal/ActorStateAdapter.ts +5 -11
  55. package/src/internal/StateOptions.ts +4 -0
  56. package/src/internal/logging.test.ts +65 -88
  57. package/src/internal/logging.ts +103 -143
  58. package/src/mod.ts +1 -1
  59. package/dist/Logger.d.ts +0 -29
  60. package/dist/Logger.d.ts.map +0 -1
  61. package/dist/Logger.js +0 -31
  62. package/dist/Logger.js.map +0 -1
  63. package/src/Logger.ts +0 -43
@@ -1,12 +1,8 @@
1
1
  import { assert, describe, it } from "@effect/vitest";
2
- import { Client, Logger, RivetError } from "@rivetkit/effect";
2
+ import { Client, RivetError, RivetLogger } from "@rivetkit/effect";
3
3
  import { Effect, Layer, Schema } from "effect";
4
4
  import * as RivetkitErrors from "rivetkit/errors";
5
- import {
6
- configureDefaultLogger,
7
- getBaseLogger,
8
- type Logger as PinoLogger,
9
- } from "rivetkit/log";
5
+ import * as RivetkitLog from "rivetkit/log";
10
6
  import * as ActionErrorEnvelope from "./internal/ActionErrorEnvelope";
11
7
 
12
8
  function makeTestLogger(
@@ -15,7 +11,7 @@ function makeTestLogger(
15
11
  readonly fields: Record<string, unknown>;
16
12
  readonly msg: string | undefined;
17
13
  }>,
18
- ): PinoLogger {
14
+ ): RivetkitLog.Logger {
19
15
  const logger: Record<string, unknown> = {
20
16
  level: "debug",
21
17
  child: () => logger,
@@ -29,7 +25,7 @@ function makeTestLogger(
29
25
  };
30
26
  }
31
27
 
32
- return logger as unknown as PinoLogger;
28
+ return logger as unknown as RivetkitLog.Logger;
33
29
  }
34
30
 
35
31
  describe("Client", () => {
@@ -39,13 +35,15 @@ describe("Client", () => {
39
35
  const baseLogger = makeTestLogger();
40
36
 
41
37
  yield* Effect.addFinalizer(() =>
42
- Effect.sync(() => configureDefaultLogger("silent")),
38
+ Effect.sync(() =>
39
+ RivetkitLog.configureDefaultLogger("silent"),
40
+ ),
43
41
  );
44
42
  yield* Client.make({
45
43
  endpoint: "http://127.0.0.1:6420",
46
- }).pipe(Effect.provide(Logger.layerPino(baseLogger)));
44
+ }).pipe(Effect.provide(RivetLogger.layerFromPino(baseLogger)));
47
45
 
48
- assert.strictEqual(getBaseLogger(), baseLogger);
46
+ assert.strictEqual(RivetkitLog.getBaseLogger(), baseLogger);
49
47
  }),
50
48
  ),
51
49
  );
@@ -61,7 +59,9 @@ describe("Client", () => {
61
59
  const baseLogger = makeTestLogger(entries);
62
60
 
63
61
  yield* Effect.addFinalizer(() =>
64
- Effect.sync(() => configureDefaultLogger("silent")),
62
+ Effect.sync(() =>
63
+ RivetkitLog.configureDefaultLogger("silent"),
64
+ ),
65
65
  );
66
66
  yield* Effect.gen(function* () {
67
67
  yield* Client.Client;
@@ -73,16 +73,19 @@ describe("Client", () => {
73
73
  Client.layer({
74
74
  endpoint: "http://127.0.0.1:6420",
75
75
  }).pipe(
76
- Layer.provideMerge(Logger.layerPino(baseLogger)),
76
+ Layer.provideMerge(
77
+ RivetLogger.layerFromPino(baseLogger),
78
+ ),
77
79
  ),
78
80
  ),
79
81
  );
80
82
 
81
- assert.deepStrictEqual(entries[0], {
82
- level: "info",
83
- fields: { clientId: "test-client" },
84
- msg: "client effect log",
85
- });
83
+ const entry = entries[0];
84
+ assert.ok(entry !== undefined);
85
+ assert.strictEqual(entry.level, "info");
86
+ assert.strictEqual(entry.msg, "client effect log");
87
+ assert.strictEqual(entry.fields.clientId, "test-client");
88
+ assert.strictEqual(typeof entry.fields.fiberId, "string");
86
89
  assert.ok(
87
90
  entries.some(
88
91
  (entry) =>
package/src/Client.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  import { Context, Effect, Layer, Record, Result, Schema } from "effect";
2
2
  import * as RivetkitClient from "rivetkit/client";
3
3
  import * as RivetkitErrors from "rivetkit/errors";
4
- import { configureBaseLogger } from "rivetkit/log";
4
+ import * as RivetkitLog from "rivetkit/log";
5
5
  import type * as Action from "./Action.ts";
6
6
  import type * as Actor from "./Actor.ts";
7
7
  import * as ActionErrorEnvelope from "./internal/ActionErrorEnvelope.ts";
8
8
  import { getOrCreateBaseLogger } from "./internal/logging.ts";
9
9
  import { rpcSystem, type TraceMeta } from "./internal/tracing.ts";
10
- import * as Logger from "./Logger.ts";
10
+ import * as RivetLogger from "./RivetLogger.ts";
11
11
  import * as RivetError from "./RivetError.ts";
12
12
 
13
13
  const TypeId = "~@rivetkit/effect/Client";
@@ -49,7 +49,9 @@ export const make = Effect.fnUntraced(function* (options: Options = {}) {
49
49
  const baseLogger = yield* getOrCreateBaseLogger;
50
50
  const rivetkitClient = yield* Effect.acquireRelease(
51
51
  Effect.sync(() => {
52
- configureBaseLogger(baseLogger);
52
+ // Raw RivetKit clients do not accept a logger in their config,
53
+ // so install the Effect-selected logger before construction.
54
+ RivetkitLog.configureBaseLogger(baseLogger);
53
55
  return RivetkitClient.createClient(options);
54
56
  }),
55
57
  (c) => Effect.promise(() => c.dispose()),
@@ -146,7 +148,7 @@ export const layer = (options: Options = {}): Layer.Layer<Client> =>
146
148
  Layer.unwrap(
147
149
  Effect.map(getOrCreateBaseLogger, (baseLogger) =>
148
150
  Layer.effect(Client, make(options)).pipe(
149
- Layer.provideMerge(Logger.layerPino(baseLogger)),
151
+ Layer.provideMerge(RivetLogger.layerFromPino(baseLogger)),
150
152
  ),
151
153
  ),
152
154
  );
@@ -1,12 +1,8 @@
1
1
  import { assert, describe, it } from "@effect/vitest";
2
- import { Action, Actor, Logger, Registry } from "@rivetkit/effect";
2
+ import { Action, Actor, Registry, RivetLogger } from "@rivetkit/effect";
3
3
  import { Effect, Layer } from "effect";
4
4
  import { HttpEffect } from "effect/unstable/http";
5
- import {
6
- configureDefaultLogger,
7
- getBaseLogger,
8
- type Logger as PinoLogger,
9
- } from "rivetkit/log";
5
+ import * as RivetkitLog from "rivetkit/log";
10
6
  import { vi } from "vitest";
11
7
 
12
8
  const TestActor = Actor.make("TestActor", {
@@ -28,7 +24,7 @@ const RegistryLive = ActorsLayer.pipe(
28
24
  ),
29
25
  );
30
26
 
31
- function makeTestLogger(): PinoLogger {
27
+ function makeTestLogger(): RivetkitLog.Logger {
32
28
  const logger: Record<string, unknown> = {
33
29
  level: "debug",
34
30
  child: () => logger,
@@ -37,7 +33,7 @@ function makeTestLogger(): PinoLogger {
37
33
  logger[level] = (): void => {};
38
34
  }
39
35
 
40
- return logger as unknown as PinoLogger;
36
+ return logger as unknown as RivetkitLog.Logger;
41
37
  }
42
38
 
43
39
  describe("Registry.toWebHandler", () => {
@@ -224,7 +220,7 @@ describe("Registry.toWebHandler", () => {
224
220
  it("uses a custom logger layer for the underlying RivetKit registry", async () => {
225
221
  const baseLogger = makeTestLogger();
226
222
  const CustomLoggerRegistryLive = RegistryLive.pipe(
227
- Layer.provide(Logger.layerPino(baseLogger)),
223
+ Layer.provide(RivetLogger.layerFromPino(baseLogger)),
228
224
  );
229
225
  const { handler, dispose } = Registry.toWebHandler(
230
226
  CustomLoggerRegistryLive,
@@ -236,10 +232,10 @@ describe("Registry.toWebHandler", () => {
236
232
  );
237
233
 
238
234
  assert.strictEqual(response.status, 200);
239
- assert.strictEqual(getBaseLogger(), baseLogger);
235
+ assert.strictEqual(RivetkitLog.getBaseLogger(), baseLogger);
240
236
  } finally {
241
237
  await dispose();
242
- configureDefaultLogger("silent");
238
+ RivetkitLog.configureDefaultLogger("silent");
243
239
  }
244
240
  });
245
241
 
package/src/Registry.ts CHANGED
@@ -7,10 +7,10 @@ import {
7
7
  type HttpServerResponse,
8
8
  } from "effect/unstable/http";
9
9
  import * as Rivetkit from "rivetkit";
10
- import { configureBaseLogger, type Logger as PinoLogger } from "rivetkit/log";
10
+ import * as RivetkitLog from "rivetkit/log";
11
11
  import * as Client from "./Client.ts";
12
12
  import { BaseLogger, getOrCreateBaseLogger } from "./internal/logging.ts";
13
- import * as Logger from "./Logger.ts";
13
+ import * as RivetLogger from "./RivetLogger.ts";
14
14
 
15
15
  const TypeId = "~@rivetkit/effect/Registry";
16
16
  type ServerlessOptions = NonNullable<
@@ -19,7 +19,7 @@ type ServerlessOptions = NonNullable<
19
19
 
20
20
  export type Options = Pick<
21
21
  Rivetkit.RegistryConfigInput<Rivetkit.RegistryActors>,
22
- "endpoint" | "token" | "namespace" | "noWelcome"
22
+ "endpoint" | "token" | "namespace" | "noWelcome" | "sqlite"
23
23
  >;
24
24
 
25
25
  export interface Registry {
@@ -27,7 +27,7 @@ export interface Registry {
27
27
 
28
28
  readonly options: Options;
29
29
 
30
- readonly baseLogger: PinoLogger;
30
+ readonly baseLogger: RivetkitLog.Logger;
31
31
 
32
32
  readonly rivetkitActors: Map<string, Rivetkit.AnyActorDefinition>;
33
33
  }
@@ -35,20 +35,49 @@ export interface Registry {
35
35
  export const Registry: Context.Service<Registry, Registry> =
36
36
  Context.Service<Registry>("@rivetkit/effect/Registry");
37
37
 
38
- const make = (options: Options, baseLogger: PinoLogger): Registry => {
39
- return Registry.of({
40
- [TypeId]: TypeId,
41
- options,
42
- baseLogger,
43
- rivetkitActors: new Map(),
44
- });
45
- };
46
-
38
+ /**
39
+ * Creates the shared registry that actor layers register into.
40
+ *
41
+ * @remarks
42
+ * Actor layers created with {@link Actor.Actor.toLayer|`Actor.toLayer()`}
43
+ * register into this service. Registry entrypoints such as
44
+ * {@link serve|`Registry.serve()`}, {@link toWebHandler|`Registry.toWebHandler()`},
45
+ * and {@link toHttpEffect|`Registry.toHttpEffect()`} later use the collected
46
+ * actors to create the underlying RivetKit runtime.
47
+ *
48
+ * @param options - RivetKit runtime options shared by the registered actors.
49
+ *
50
+ * @returns A layer that provides the {@link Registry|`Registry.Registry`}
51
+ * service.
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * import { Registry } from "@rivetkit/effect"
56
+ * import { Layer } from "effect"
57
+ *
58
+ * const ActorsLayer = Layer.mergeAll(ChatRoomLive, ModeratorLive)
59
+ *
60
+ * const MainLayer = Registry.serve(ActorsLayer).pipe(
61
+ * Layer.provide(Registry.layer({ endpoint: "http://127.0.0.1:6420" }))
62
+ * )
63
+ * ```
64
+ *
65
+ * @see {@link serve|`Registry.serve()`} for starting a long-running actor
66
+ * server.
67
+ * @see {@link toWebHandler|`Registry.toWebHandler()`} for Fetch-compatible
68
+ * serverless handlers.
69
+ * @see {@link toHttpEffect|`Registry.toHttpEffect()`} for Effect HTTP handlers.
70
+ */
47
71
  export const layer = (options: Options = {}): Layer.Layer<Registry> =>
48
72
  Layer.effect(
49
73
  Registry,
50
74
  Effect.map(getOrCreateBaseLogger, (baseLogger) =>
51
- make(options, baseLogger),
75
+ Registry.of({
76
+ [TypeId]: TypeId,
77
+ options,
78
+ baseLogger,
79
+ rivetkitActors: new Map(),
80
+ }),
52
81
  ),
53
82
  );
54
83
 
@@ -57,24 +86,53 @@ const setupRivetkitRegistry = (
57
86
  options?: {
58
87
  readonly serverless?: ServerlessOptions | undefined;
59
88
  },
60
- ) => {
61
- configureBaseLogger(registry.baseLogger);
62
- return Rivetkit.setup({
63
- use: Object.fromEntries(registry.rivetkitActors),
64
- ...registry.options,
65
- logging: { baseLogger: registry.baseLogger },
66
- ...(options?.serverless === undefined
67
- ? {}
68
- : { serverless: options.serverless }),
69
- });
70
- };
89
+ ) =>
90
+ Effect.sync(() =>
91
+ Rivetkit.setup({
92
+ use: Object.fromEntries(registry.rivetkitActors),
93
+ ...registry.options,
94
+ logging: { baseLogger: registry.baseLogger },
95
+ ...(options?.serverless === undefined
96
+ ? {}
97
+ : { serverless: options.serverless }),
98
+ }),
99
+ );
71
100
 
72
101
  /**
73
- * Runs an actor registration layer against the configured engine.
102
+ * Serves the actors registered by `actorsLayer`.
103
+ *
104
+ * @remarks
105
+ * Provide this with {@link layer|`Registry.layer()`} at the application root.
106
+ * Building the returned layer evaluates the actor layer, collects its registered
107
+ * actors, and starts the underlying RivetKit runtime using the options from
108
+ * {@link layer|`Registry.layer()`}. Launch the resulting layer with
109
+ * {@link Layer.launch|`Layer.launch()`} to keep the server process alive.
110
+ *
111
+ * @param actorsLayer - A layer containing one or more
112
+ * {@link Actor.Actor.toLayer|`Actor.toLayer()`} registrations.
113
+ *
114
+ * @returns A side-effecting startup layer that requires the
115
+ * {@link Registry|`Registry.Registry`} service and the services required by
116
+ * `actorsLayer`.
117
+ *
118
+ * @example
119
+ * ```ts
120
+ * import { Registry } from "@rivetkit/effect"
121
+ * import { Layer } from "effect"
122
+ *
123
+ * const ActorsLayer = Layer.mergeAll(ChatRoomLive, ModeratorLive)
124
+ *
125
+ * const MainLayer = Registry.serve(ActorsLayer).pipe(
126
+ * Layer.provide(Registry.layer({ endpoint: "http://127.0.0.1:6420" }))
127
+ * )
128
+ *
129
+ * Layer.launch(MainLayer).pipe(NodeRuntime.runMain);
130
+ * ```
131
+ *
132
+ * @see {@link toWebHandler|`Registry.toWebHandler()`} for Fetch-compatible
133
+ * serverless handlers.
134
+ * @see {@link toHttpEffect|`Registry.toHttpEffect()`} for Effect HTTP handlers.
74
135
  *
75
- * The actor layer is built in the server layer scope. Registered Rivet Actors
76
- * are collected from `Registry`, materialized into a single underlying RivetKit
77
- * registry, and started.
78
136
  */
79
137
  export const serve = <E, R>(
80
138
  actorsLayer: Layer.Layer<never, E, R>,
@@ -82,13 +140,14 @@ export const serve = <E, R>(
82
140
  Layer.effectDiscard(
83
141
  Effect.gen(function* () {
84
142
  const registry = yield* Registry;
85
- const baseLogger = registry.baseLogger;
86
143
  yield* Layer.build(
87
144
  actorsLayer.pipe(
88
- Layer.provideMerge(Logger.layerPino(baseLogger)),
145
+ Layer.provideMerge(
146
+ RivetLogger.layerFromPino(registry.baseLogger),
147
+ ),
89
148
  ),
90
149
  );
91
- const rivetkitRegistry = setupRivetkitRegistry(registry);
150
+ const rivetkitRegistry = yield* setupRivetkitRegistry(registry);
92
151
  yield* Effect.sync(() => rivetkitRegistry.start());
93
152
  }),
94
153
  );
@@ -107,7 +166,7 @@ export const test: Layer.Layer<Client.Client, never, Registry> = Layer.effect(
107
166
  Client.Client,
108
167
  Effect.gen(function* () {
109
168
  const registry = yield* Registry;
110
- const rivetkitRegistry = setupRivetkitRegistry(registry);
169
+ const rivetkitRegistry = yield* setupRivetkitRegistry(registry);
111
170
  rivetkitRegistry.config.test = {
112
171
  ...rivetkitRegistry.config.test,
113
172
  enabled: true,
@@ -145,17 +204,21 @@ const makeHttpEffect = (
145
204
  registry: Registry,
146
205
  options?: ToHttpEffectOptions,
147
206
  ): Effect.Effect<
148
- HttpServerResponse.HttpServerResponse,
149
- HttpServerError.HttpServerError,
150
- HttpServerRequest.HttpServerRequest
151
- > => {
152
- const rivetkitRegistry = setupRivetkitRegistry(registry, {
153
- serverless: options,
154
- });
155
- return HttpEffect.fromWebHandler((request) =>
156
- rivetkitRegistry.handler(request),
207
+ Effect.Effect<
208
+ HttpServerResponse.HttpServerResponse,
209
+ HttpServerError.HttpServerError,
210
+ HttpServerRequest.HttpServerRequest
211
+ >
212
+ > =>
213
+ Effect.map(
214
+ setupRivetkitRegistry(registry, {
215
+ serverless: options,
216
+ }),
217
+ (rivetkitRegistry) =>
218
+ HttpEffect.fromWebHandler((request) =>
219
+ rivetkitRegistry.handler(request),
220
+ ),
157
221
  );
158
- };
159
222
 
160
223
  export type ToHttpEffectOptions = ServerlessOptions;
161
224
 
@@ -179,12 +242,14 @@ export const toHttpEffect = Effect.fnUntraced(function* <E>(
179
242
  Scope.Scope
180
243
  > {
181
244
  const context = yield* Layer.build(
182
- registryLayer.pipe(Layer.provideMerge(Logger.layer)),
245
+ registryLayer.pipe(Layer.provideMerge(RivetLogger.defaultLayer)),
183
246
  );
184
- // @effect-diagnostics-next-line returnEffectInGen:off
185
- return makeHttpEffect(Context.get(context, Registry), options).pipe(
186
- Effect.provide(context),
247
+ const httpEffect = yield* makeHttpEffect(
248
+ Context.get(context, Registry),
249
+ options,
187
250
  );
251
+ // @effect-diagnostics-next-line returnEffectInGen:off
252
+ return httpEffect.pipe(Effect.provide(context));
188
253
  });
189
254
 
190
255
  export type ToWebHandlerOptions = ServerlessOptions & {
@@ -221,16 +286,17 @@ export const toWebHandler = <E>(
221
286
  }
222
287
 
223
288
  const registryLayerWithLogging = registryLayer.pipe(
224
- Layer.provideMerge(Logger.layer),
289
+ Layer.provideMerge(RivetLogger.defaultLayer),
225
290
  );
226
291
 
227
292
  return HttpEffect.toWebHandlerLayerWith(registryLayerWithLogging, {
228
293
  toHandler: (context) =>
229
- Effect.succeed(
294
+ Effect.map(
230
295
  makeHttpEffect(
231
296
  Context.get(context, Registry),
232
297
  serverlessOptions,
233
- ).pipe(Effect.provide(context)),
298
+ ),
299
+ (httpEffect) => httpEffect.pipe(Effect.provide(context)),
234
300
  ),
235
301
  middleware,
236
302
  memoMap,
@@ -121,6 +121,13 @@ describe("RivetError", () => {
121
121
  "actor ready timeout",
122
122
  ),
123
123
  );
124
+ const wakeRetriesExceeded = RivetError.fromUnknown(
125
+ new RivetkitErrors.RivetError(
126
+ "guard",
127
+ "actor_wake_retries_exceeded",
128
+ "actor wake retries exceeded",
129
+ ),
130
+ );
124
131
  const tunnelTimeout = RivetError.fromUnknown(
125
132
  new RivetkitErrors.RivetError(
126
133
  "guard",
@@ -137,6 +144,10 @@ describe("RivetError", () => {
137
144
  readyTimeout.reason,
138
145
  RivetError.GuardActorReadyTimeout,
139
146
  );
147
+ assert.instanceOf(
148
+ wakeRetriesExceeded.reason,
149
+ RivetError.GuardActorWakeRetriesExceeded,
150
+ );
140
151
  assert.instanceOf(
141
152
  tunnelTimeout.reason,
142
153
  RivetError.GuardTunnelMessageTimeout,