@rivetkit/effect 0.0.0-sqlite-uds.37f9e48 → 0.0.0-sqlite-profiling-compat.87d61c0

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 +3 -2
  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 +4 -6
  7. package/dist/Client.js.map +1 -1
  8. package/dist/Logger.d.ts +29 -0
  9. package/dist/Logger.d.ts.map +1 -0
  10. package/dist/Logger.js +31 -0
  11. package/dist/Logger.js.map +1 -0
  12. package/dist/Registry.d.ts +7 -69
  13. package/dist/Registry.d.ts.map +1 -1
  14. package/dist/Registry.js +40 -93
  15. package/dist/Registry.js.map +1 -1
  16. package/dist/RivetError.d.ts +3 -18
  17. package/dist/RivetError.d.ts.map +1 -1
  18. package/dist/RivetError.js +0 -31
  19. package/dist/RivetError.js.map +1 -1
  20. package/dist/State.d.ts +56 -92
  21. package/dist/State.d.ts.map +1 -1
  22. package/dist/State.js +57 -51
  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 +7 -2
  30. package/dist/internal/ActorStateAdapter.js.map +1 -1
  31. package/dist/internal/StateOptions.d.ts +0 -1
  32. package/dist/internal/StateOptions.d.ts.map +1 -1
  33. package/dist/internal/logging.d.ts +7 -6
  34. package/dist/internal/logging.d.ts.map +1 -1
  35. package/dist/internal/logging.js +102 -78
  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 +0 -32
  43. package/src/Actor.ts +4 -29
  44. package/src/Client.test.ts +18 -21
  45. package/src/Client.ts +4 -6
  46. package/src/Logger.ts +43 -0
  47. package/src/Registry.test.ts +11 -7
  48. package/src/Registry.ts +50 -116
  49. package/src/RivetError.test.ts +0 -11
  50. package/src/RivetError.ts +0 -37
  51. package/src/State.test.ts +4 -163
  52. package/src/State.ts +97 -293
  53. package/src/internal/ActorInstanceManager.ts +10 -4
  54. package/src/internal/ActorStateAdapter.ts +11 -5
  55. package/src/internal/StateOptions.ts +0 -4
  56. package/src/internal/logging.test.ts +88 -65
  57. package/src/internal/logging.ts +143 -103
  58. package/src/mod.ts +1 -1
  59. package/dist/RivetLogger.d.ts +0 -41
  60. package/dist/RivetLogger.d.ts.map +0 -1
  61. package/dist/RivetLogger.js +0 -41
  62. package/dist/RivetLogger.js.map +0 -1
  63. package/src/RivetLogger.ts +0 -60
@@ -77,9 +77,7 @@ export const make = Effect.fnUntraced(function* <
77
77
  const scope = yield* Scope.make();
78
78
  return yield* Effect.gen(function* () {
79
79
  const state = stateAdapter
80
- ? yield* stateAdapter
81
- .makeStateView(c)
82
- .pipe(Effect.provideService(Scope.Scope, scope))
80
+ ? yield* stateAdapter.makeStateView(c)
83
81
  : undefined;
84
82
  const context = makeContext(c, scope);
85
83
  const actionHandlers = yield* wakeHandler(
@@ -107,7 +105,15 @@ export const make = Effect.fnUntraced(function* <
107
105
  return {
108
106
  get: (actorId: string) => instances.get(actorId),
109
107
  onWake: async (c: WakeContext<StateDefinition, Database>) => {
110
- instances.set(c.actorId, await runPromise(makeInstance(c)));
108
+ await runPromise(
109
+ makeInstance(c).pipe(
110
+ Effect.tap((instance) =>
111
+ Effect.sync(() => {
112
+ instances.set(c.actorId, instance);
113
+ }),
114
+ ),
115
+ ),
116
+ );
111
117
  },
112
118
  onStateChange: stateAdapter
113
119
  ? (
@@ -1,11 +1,10 @@
1
- import { Effect, type Fiber, Schema } from "effect";
1
+ import { Effect, type Fiber, Schema, Semaphore } from "effect";
2
2
  import * as State from "../State.ts";
3
3
  import type * as StateOptions from "./StateOptions.ts";
4
4
 
5
5
  export type ActorState<StateDefinition extends StateOptions.Any> = State.State<
6
6
  StateOptions.Decoded<StateDefinition>,
7
- Schema.SchemaError,
8
- StateOptions.Services<StateDefinition>
7
+ Schema.SchemaError
9
8
  >;
10
9
 
11
10
  type StateInstance<StateDefinition extends StateOptions.Any> = {
@@ -72,8 +71,15 @@ export const make = Effect.fnUntraced(function* <
72
71
  const state = yield* Effect.fromNullishOr(
73
72
  instance.state,
74
73
  ).pipe(Effect.orDie);
75
- yield* state[State.RuntimeTypeId].publishEffect(
76
- stateCodec.decodeUnknown(newState).pipe(Effect.orDie),
74
+
75
+ yield* Semaphore.withPermit(
76
+ state.semaphore,
77
+ Effect.gen(function* () {
78
+ const decoded = yield* stateCodec
79
+ .decodeUnknown(newState)
80
+ .pipe(Effect.orDie);
81
+ State.publishUnsafe(state, decoded);
82
+ }),
77
83
  );
78
84
  }),
79
85
  );
@@ -10,10 +10,6 @@ export interface Any {
10
10
  readonly initialValue: () => unknown;
11
11
  }
12
12
 
13
- export type Services<State extends Any> =
14
- | State["schema"]["DecodingServices"]
15
- | State["schema"]["EncodingServices"];
16
-
17
13
  export type Encoded<State extends Any> =
18
14
  | State["schema"]["Encoded"]
19
15
  | ([State] extends [never] ? undefined : never);
@@ -1,6 +1,13 @@
1
1
  import { assert, describe, it } from "@effect/vitest";
2
- import { ConfigProvider, Effect, Logger, References } from "effect";
3
- import * as RivetkitLog from "rivetkit/log";
2
+ import {
3
+ Config,
4
+ ConfigProvider,
5
+ Effect,
6
+ Layer,
7
+ Logger as EffectLogger,
8
+ References,
9
+ } from "effect";
10
+ import type { Logger as PinoLogger } from "rivetkit/log";
4
11
  import * as Logging from "./logging.ts";
5
12
 
6
13
  type LogEntry = {
@@ -9,7 +16,7 @@ type LogEntry = {
9
16
  readonly msg: string | undefined;
10
17
  };
11
18
 
12
- function makeTestLogger(entries: Array<LogEntry>): RivetkitLog.Logger {
19
+ function makeTestLogger(entries: Array<LogEntry>): PinoLogger {
13
20
  const logger: Record<string, unknown> = {};
14
21
  for (const level of ["trace", "debug", "info", "warn", "error", "fatal"]) {
15
22
  logger[level] = (
@@ -20,7 +27,7 @@ function makeTestLogger(entries: Array<LogEntry>): RivetkitLog.Logger {
20
27
  };
21
28
  }
22
29
 
23
- return logger as unknown as RivetkitLog.Logger;
30
+ return logger as unknown as PinoLogger;
24
31
  }
25
32
 
26
33
  describe("internal/logging", () => {
@@ -58,21 +65,23 @@ describe("internal/logging", () => {
58
65
  key: "room-1",
59
66
  actorId: "actor-1",
60
67
  }),
61
- Effect.provide(Logger.layer([Logging.makeLogger(baseLogger)])),
68
+ Effect.provide(
69
+ EffectLogger.layer([Logging.makeEffectLogger(baseLogger)]),
70
+ ),
62
71
  );
63
72
 
64
- const entry = entries[0];
65
- assert.ok(entry !== undefined);
66
- assert.strictEqual(entry.level, "info");
67
- assert.strictEqual(entry.msg, "room awake");
68
- assert.deepStrictEqual(entry.fields, {
69
- roomId: "abc",
70
- actor: "ChatRoom",
71
- key: "room-1",
72
- actorId: "actor-1",
73
- fiberId: entry.fields.fiberId,
74
- });
75
- assert.strictEqual(typeof entry.fields.fiberId, "string");
73
+ assert.deepStrictEqual(entries, [
74
+ {
75
+ level: "info",
76
+ fields: {
77
+ roomId: "abc",
78
+ actor: "ChatRoom",
79
+ key: "room-1",
80
+ actorId: "actor-1",
81
+ },
82
+ msg: "room awake",
83
+ },
84
+ ]);
76
85
  }),
77
86
  );
78
87
 
@@ -83,7 +92,9 @@ describe("internal/logging", () => {
83
92
  const error = new Error("room failed to wake");
84
93
 
85
94
  yield* Effect.logError(error).pipe(
86
- Effect.provide(Logger.layer([Logging.makeLogger(baseLogger)])),
95
+ Effect.provide(
96
+ EffectLogger.layer([Logging.makeEffectLogger(baseLogger)]),
97
+ ),
87
98
  );
88
99
 
89
100
  const entry = entries[0];
@@ -104,7 +115,9 @@ describe("internal/logging", () => {
104
115
  actorId: "actor-1",
105
116
  action: "SendMessage",
106
117
  }).pipe(
107
- Effect.provide(Logger.layer([Logging.makeLogger(baseLogger)])),
118
+ Effect.provide(
119
+ EffectLogger.layer([Logging.makeEffectLogger(baseLogger)]),
120
+ ),
108
121
  );
109
122
 
110
123
  const entry = entries[0];
@@ -117,7 +130,17 @@ describe("internal/logging", () => {
117
130
  }),
118
131
  );
119
132
 
120
- it.effect("accepts RIVET_LOG_LEVEL values", () =>
133
+ it.effect(
134
+ "uses References.MinimumLogLevel when creating the base logger",
135
+ () =>
136
+ Effect.gen(function* () {
137
+ const baseLogger = yield* Logging.makeDefaultBaseLogger;
138
+
139
+ assert.strictEqual(baseLogger.level, "debug");
140
+ }).pipe(Effect.provideService(References.MinimumLogLevel, "Debug")),
141
+ );
142
+
143
+ it.effect("accepts the shared Pino RIVET_LOG_LEVEL values", () =>
121
144
  Effect.gen(function* () {
122
145
  const baseLogger = yield* Logging.makeDefaultBaseLogger;
123
146
 
@@ -134,24 +157,25 @@ describe("internal/logging", () => {
134
157
  ),
135
158
  );
136
159
 
137
- it.effect("accepts uppercase RIVET_LOG_LEVEL values", () =>
160
+ it.effect("prefers References.MinimumLogLevel over shared env values", () =>
138
161
  Effect.gen(function* () {
139
162
  const baseLogger = yield* Logging.makeDefaultBaseLogger;
140
163
 
141
164
  assert.strictEqual(baseLogger.level, "debug");
142
165
  }).pipe(
166
+ Effect.provideService(References.MinimumLogLevel, "Debug"),
143
167
  Effect.provideService(
144
168
  ConfigProvider.ConfigProvider,
145
169
  ConfigProvider.fromEnv({
146
170
  env: {
147
- RIVET_LOG_LEVEL: "DEBUG",
171
+ RIVET_LOG_LEVEL: "silent",
148
172
  },
149
173
  }),
150
174
  ),
151
175
  ),
152
176
  );
153
177
 
154
- it.effect("ignores Effect-only RIVET_LOG_LEVEL values", () =>
178
+ it.effect("preserves an explicit Info minimum log level", () =>
155
179
  Effect.gen(function* () {
156
180
  const baseLogger = yield* Logging.makeDefaultBaseLogger;
157
181
 
@@ -162,45 +186,36 @@ describe("internal/logging", () => {
162
186
  ConfigProvider.ConfigProvider,
163
187
  ConfigProvider.fromEnv({
164
188
  env: {
165
- RIVET_LOG_LEVEL: "None",
189
+ RIVET_LOG_LEVEL: "silent",
166
190
  },
167
191
  }),
168
192
  ),
169
193
  ),
170
194
  );
171
195
 
172
- it.effect("falls back to References.MinimumLogLevel without env", () =>
173
- Effect.gen(function* () {
174
- const baseLogger = yield* Logging.makeDefaultBaseLogger;
175
-
176
- assert.strictEqual(baseLogger.level, "debug");
177
- }).pipe(
178
- Effect.provideService(References.MinimumLogLevel, "Debug"),
179
- Effect.provideService(
180
- ConfigProvider.ConfigProvider,
181
- ConfigProvider.fromEnv({
182
- env: {},
183
- }),
184
- ),
185
- ),
186
- );
187
-
188
- it.effect("RIVET_LOG_LEVEL overrides References.MinimumLogLevel", () =>
189
- Effect.gen(function* () {
190
- const baseLogger = yield* Logging.makeDefaultBaseLogger;
196
+ it.effect(
197
+ "uses Config.logLevel values provided to References.MinimumLogLevel",
198
+ () =>
199
+ Effect.gen(function* () {
200
+ const baseLogger = yield* Logging.makeDefaultBaseLogger;
191
201
 
192
- assert.strictEqual(baseLogger.level, "silent");
193
- }).pipe(
194
- Effect.provideService(References.MinimumLogLevel, "Debug"),
195
- Effect.provideService(
196
- ConfigProvider.ConfigProvider,
197
- ConfigProvider.fromEnv({
198
- env: {
199
- RIVET_LOG_LEVEL: "silent",
200
- },
201
- }),
202
+ assert.strictEqual(baseLogger.level, "trace");
203
+ }).pipe(
204
+ Effect.provide(
205
+ Layer.effect(
206
+ References.MinimumLogLevel,
207
+ Config.logLevel("RIVET_LOG_LEVEL"),
208
+ ),
209
+ ),
210
+ Effect.provideService(
211
+ ConfigProvider.ConfigProvider,
212
+ ConfigProvider.fromEnv({
213
+ env: {
214
+ RIVET_LOG_LEVEL: "Trace",
215
+ },
216
+ }),
217
+ ),
202
218
  ),
203
- ),
204
219
  );
205
220
 
206
221
  it.effect(
@@ -214,18 +229,19 @@ describe("internal/logging", () => {
214
229
  Effect.provideService(References.CurrentLogLevel, "Debug"),
215
230
  Effect.provideService(References.MinimumLogLevel, "Debug"),
216
231
  Effect.provide(
217
- Logger.layer([Logging.makeLogger(baseLogger)]),
232
+ EffectLogger.layer([
233
+ Logging.makeEffectLogger(baseLogger),
234
+ ]),
218
235
  ),
219
236
  );
220
237
 
221
- const entry = entries[0];
222
- assert.ok(entry !== undefined);
223
- assert.strictEqual(entry.level, "debug");
224
- assert.strictEqual(entry.msg, "plain log");
225
- assert.deepStrictEqual(entry.fields, {
226
- fiberId: entry.fields.fiberId,
227
- });
228
- assert.strictEqual(typeof entry.fields.fiberId, "string");
238
+ assert.deepStrictEqual(entries, [
239
+ {
240
+ level: "debug",
241
+ fields: {},
242
+ msg: "plain log",
243
+ },
244
+ ]);
229
245
  }),
230
246
  );
231
247
 
@@ -240,7 +256,9 @@ describe("internal/logging", () => {
240
256
  Effect.provideService(References.CurrentLogLevel, "None"),
241
257
  Effect.provideService(References.MinimumLogLevel, "All"),
242
258
  Effect.provide(
243
- Logger.layer([Logging.makeLogger(baseLogger)]),
259
+ EffectLogger.layer([
260
+ Logging.makeEffectLogger(baseLogger),
261
+ ]),
244
262
  ),
245
263
  );
246
264
 
@@ -258,13 +276,18 @@ describe("internal/logging", () => {
258
276
  yield* Effect.logInfo("checkout complete").pipe(
259
277
  Effect.withLogSpan("checkout"),
260
278
  Effect.provide(
261
- Logger.layer([Logging.makeLogger(baseLogger)]),
279
+ EffectLogger.layer([
280
+ Logging.makeEffectLogger(baseLogger),
281
+ ]),
262
282
  ),
263
283
  );
264
284
 
265
285
  assert.strictEqual(entries.length, 1);
266
286
  assert.strictEqual(entries[0]?.level, "info");
267
287
  assert.strictEqual(entries[0]?.msg, "checkout complete");
288
+ assert.deepStrictEqual(Object.keys(entries[0]?.fields ?? {}), [
289
+ "spans",
290
+ ]);
268
291
  const spans = entries[0]?.fields.spans as
269
292
  | Record<string, unknown>
270
293
  | undefined;
@@ -1,16 +1,19 @@
1
1
  import {
2
+ Cause,
2
3
  Config,
3
4
  Context,
4
5
  Effect,
5
- Logger,
6
- Option,
7
- Predicate,
8
- Record as EffectRecord,
6
+ Logger as EffectLogger,
9
7
  type LogLevel,
10
8
  References,
11
9
  } from "effect";
12
10
  import type * as Rivetkit from "rivetkit";
13
- import * as RivetkitLog from "rivetkit/log";
11
+ import {
12
+ configureDefaultLogger,
13
+ getBaseLogger,
14
+ type Logger as PinoLogger,
15
+ type LogLevel as PinoLogLevel,
16
+ } from "rivetkit/log";
14
17
 
15
18
  const EMPTY_KEY = "/";
16
19
  const KEY_SEPARATOR = "/";
@@ -21,14 +24,25 @@ type ActorLogContext = {
21
24
  readonly actorId: string;
22
25
  };
23
26
 
24
- export class BaseLogger extends Context.Service<
25
- BaseLogger,
26
- RivetkitLog.Logger
27
- >()("@rivetkit/effect/RivetLogger/BaseLogger") {}
27
+ export class BaseLogger extends Context.Service<BaseLogger, PinoLogger>()(
28
+ "@rivetkit/effect/Logger/BaseLogger",
29
+ ) {}
30
+
31
+ const PinoLevelByEffectLevel: Record<LogLevel.LogLevel, PinoLogLevel> = {
32
+ All: "trace",
33
+ Trace: "trace",
34
+ Debug: "debug",
35
+ Info: "info",
36
+ Warn: "warn",
37
+ Error: "error",
38
+ Fatal: "fatal",
39
+ None: "silent",
40
+ };
28
41
 
29
- const RivetkitLogLevels = RivetkitLog.LogLevelSchema.options;
42
+ export const toPinoLevel = (logLevel: LogLevel.LogLevel): PinoLogLevel =>
43
+ PinoLevelByEffectLevel[logLevel];
30
44
 
31
- const EffectLevelByRivetkitLevel = {
45
+ const EffectLevelByPinoLevel: Record<PinoLogLevel, LogLevel.LogLevel> = {
32
46
  trace: "Trace",
33
47
  debug: "Debug",
34
48
  info: "Info",
@@ -36,54 +50,56 @@ const EffectLevelByRivetkitLevel = {
36
50
  error: "Error",
37
51
  fatal: "Fatal",
38
52
  silent: "None",
39
- } as const satisfies Record<
40
- RivetkitLog.LogLevel,
41
- Exclude<LogLevel.LogLevel, "All">
42
- >;
43
-
44
- const RivetkitLevelByEffectLevel = {
45
- ...Object.fromEntries(
46
- RivetkitLogLevels.map((level) => [
47
- EffectLevelByRivetkitLevel[level],
48
- level,
49
- ]),
50
- ),
51
- All: "trace",
52
- } as Record<LogLevel.LogLevel, RivetkitLog.LogLevel>;
53
+ };
53
54
 
54
- const rivetLogLevelFromEnv = Config.string("RIVET_LOG_LEVEL").pipe(
55
- Effect.option,
56
- Effect.map((maybeRivetLogLevel) => {
57
- if (Option.isNone(maybeRivetLogLevel)) return Option.none();
55
+ const pinoLogLevelFromEnv = Config.string("RIVET_LOG_LEVEL").pipe(
56
+ Config.map((value) => {
57
+ const pinoLevel = value.toLowerCase();
58
+ if (pinoLevel in EffectLevelByPinoLevel) {
59
+ return EffectLevelByPinoLevel[pinoLevel as PinoLogLevel];
60
+ }
58
61
 
59
- const parsed = RivetkitLog.LogLevelSchema.safeParse(
60
- maybeRivetLogLevel.value.toLowerCase(),
61
- );
62
- return parsed.success ? Option.some(parsed.data) : Option.none();
62
+ return "Info";
63
63
  }),
64
64
  );
65
65
 
66
- export const makeDefaultBaseLogger: Effect.Effect<RivetkitLog.Logger> =
67
- Effect.gen(function* () {
68
- const maybeRivetLogLevel = yield* rivetLogLevelFromEnv;
69
- const logLevel = Option.isSome(maybeRivetLogLevel)
70
- ? maybeRivetLogLevel.value
71
- : RivetkitLevelByEffectLevel[yield* References.MinimumLogLevel];
66
+ const logLevelFromEnv = Config.logLevel("RIVET_LOG_LEVEL").pipe(
67
+ Config.orElse(() => pinoLogLevelFromEnv),
68
+ Effect.option,
69
+ );
72
70
 
73
- return yield* Effect.sync(() =>
74
- RivetkitLog.makeDefaultLogger(logLevel),
71
+ export const makeDefaultBaseLogger: Effect.Effect<PinoLogger> = Effect.gen(
72
+ function* () {
73
+ const context = yield* Effect.context();
74
+ const providedMinimumLogLevel = Context.getOrUndefined(
75
+ context,
76
+ References.MinimumLogLevel,
75
77
  );
76
- });
78
+ const envLogLevel = yield* logLevelFromEnv;
79
+ const logLevel =
80
+ providedMinimumLogLevel !== undefined
81
+ ? providedMinimumLogLevel
82
+ : envLogLevel._tag === "Some"
83
+ ? envLogLevel.value
84
+ : yield* References.MinimumLogLevel;
85
+
86
+ return yield* Effect.sync(() => {
87
+ configureDefaultLogger(toPinoLevel(logLevel));
88
+ return getBaseLogger();
89
+ });
90
+ },
91
+ );
77
92
 
78
- export const getOrCreateBaseLogger: Effect.Effect<RivetkitLog.Logger> =
79
- Effect.gen(function* () {
80
- const maybeBaseLogger = yield* Effect.serviceOption(BaseLogger);
81
- if (Option.isSome(maybeBaseLogger)) {
82
- return maybeBaseLogger.value;
93
+ export const getOrCreateBaseLogger: Effect.Effect<PinoLogger> = Effect.gen(
94
+ function* () {
95
+ const provided = yield* Effect.serviceOption(BaseLogger);
96
+ if (provided._tag === "Some") {
97
+ return provided.value;
83
98
  }
84
99
 
85
100
  return yield* makeDefaultBaseLogger;
86
- });
101
+ },
102
+ );
87
103
 
88
104
  export function makeActorLogAnnotations(context: ActorLogContext): {
89
105
  readonly actor: string;
@@ -115,64 +131,59 @@ export function serializeActorKey(key: Rivetkit.ActorKey): string {
115
131
  .join(KEY_SEPARATOR);
116
132
  }
117
133
 
118
- export function makeLogger(
119
- baseLogger: RivetkitLog.Logger,
120
- ): Logger.Logger<unknown, void> {
121
- return Logger.make((options) => {
122
- if (options.logLevel === "None") return;
123
- const rivetkitLevel = RivetkitLevelByEffectLevel[options.logLevel];
124
- const structured = Logger.formatStructured.log(options);
125
- const { msg, fields: messageFields } = extractMessage(
126
- structured.message,
127
- );
128
- const fields: Record<string, unknown> = {
129
- ...messageFields,
130
- ...structured.annotations,
131
- fiberId: structured.fiberId,
132
- };
133
-
134
- if (!EffectRecord.isEmptyRecord(structured.spans)) {
135
- fields.spans = structured.spans;
136
- }
137
- if (structured.cause !== undefined) {
138
- fields.cause = structured.cause;
139
- }
134
+ function structuredValue(value: unknown): unknown {
135
+ if (value instanceof Error) {
136
+ return value;
137
+ }
140
138
 
141
- const logger = baseLogger[rivetkitLevel];
142
- if (msg === undefined) {
143
- logger.call(baseLogger, fields);
144
- } else {
145
- logger.call(baseLogger, fields, msg);
146
- }
147
- });
139
+ return value;
148
140
  }
149
141
 
150
- function extractMessage(message: unknown): {
142
+ function extractMessageAndFields(message: unknown): {
151
143
  readonly msg: string | undefined;
152
144
  readonly fields: Record<string, unknown>;
153
145
  } {
154
146
  const values = Array.isArray(message) ? message : [message];
147
+ if (values.length === 0) {
148
+ return { msg: undefined, fields: {} };
149
+ }
150
+
151
+ const [first, ...rest] = values;
155
152
  const fields: Record<string, unknown> = {};
156
- const args: Array<unknown> = [];
157
153
  let msg: string | undefined;
158
154
 
159
- for (const [index, value] of values.entries()) {
160
- if (Predicate.isError(value)) {
161
- fields.error = value;
162
- if (index === 0) msg = value.message;
163
- } else if (isStructuredError(value)) {
164
- fields.error = value;
165
- if (index === 0) msg = value.error;
166
- } else if (Predicate.isObject(value)) {
167
- if (index === 0) {
168
- const { msg: valueMsg, ...rest } = value;
169
- Object.assign(fields, rest);
170
- if (valueMsg !== undefined) msg = String(valueMsg);
155
+ if (first instanceof Error) {
156
+ fields.error = first;
157
+ msg = first.message;
158
+ } else if (first !== null && typeof first === "object") {
159
+ const firstFields = first as Record<string, unknown>;
160
+ for (const [key, value] of Object.entries(firstFields)) {
161
+ if (key === "msg") {
162
+ if (value !== undefined) {
163
+ msg = String(value);
164
+ }
171
165
  } else {
172
- Object.assign(fields, value);
166
+ fields[key] = structuredValue(value);
167
+ }
168
+ }
169
+ } else if (first !== undefined) {
170
+ msg = String(first);
171
+ }
172
+
173
+ const args: Array<unknown> = [];
174
+ for (const value of rest) {
175
+ if (value instanceof Error) {
176
+ fields.error = value;
177
+ } else if (
178
+ value !== null &&
179
+ typeof value === "object" &&
180
+ !Array.isArray(value)
181
+ ) {
182
+ for (const [key, fieldValue] of Object.entries(
183
+ value as Record<string, unknown>,
184
+ )) {
185
+ fields[key] = structuredValue(fieldValue);
173
186
  }
174
- } else if (index === 0) {
175
- msg = value === undefined ? undefined : String(value);
176
187
  } else {
177
188
  args.push(value);
178
189
  }
@@ -185,13 +196,42 @@ function extractMessage(message: unknown): {
185
196
  return { msg, fields };
186
197
  }
187
198
 
188
- function isStructuredError(
189
- value: unknown,
190
- ): value is { readonly error: string; readonly name: string } {
191
- return (
192
- Predicate.hasProperty(value, "error") &&
193
- Predicate.hasProperty(value, "name") &&
194
- Predicate.isString(value.error) &&
195
- Predicate.isString(value.name)
196
- );
199
+ export function makeEffectLogger(
200
+ baseLogger: PinoLogger,
201
+ ): EffectLogger.Logger<unknown, void> {
202
+ return EffectLogger.make(({ cause, date, fiber, logLevel, message }) => {
203
+ const { msg, fields } = extractMessageAndFields(message);
204
+
205
+ for (const [key, value] of Object.entries(
206
+ fiber.getRef(References.CurrentLogAnnotations),
207
+ )) {
208
+ fields[key] = structuredValue(value);
209
+ }
210
+
211
+ const spans: Record<string, number> = {};
212
+ for (const [label, startTime] of fiber.getRef(
213
+ References.CurrentLogSpans,
214
+ )) {
215
+ spans[label] = date.getTime() - startTime;
216
+ }
217
+ if (Object.keys(spans).length > 0) {
218
+ fields.spans = spans;
219
+ }
220
+
221
+ if (cause.reasons.length > 0) {
222
+ fields.cause = Cause.pretty(cause);
223
+ }
224
+
225
+ const pinoLevel = toPinoLevel(logLevel);
226
+ if (pinoLevel === "silent") {
227
+ return;
228
+ }
229
+
230
+ const logger = baseLogger[pinoLevel];
231
+ if (msg === undefined) {
232
+ logger.call(baseLogger, fields);
233
+ } else {
234
+ logger.call(baseLogger, fields, msg);
235
+ }
236
+ });
197
237
  }
package/src/mod.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export * as Action from "./Action.ts";
2
2
  export * as Actor from "./Actor.ts";
3
3
  export * as Client from "./Client.ts";
4
+ export * as Logger from "./Logger.ts";
4
5
  export * as Registry from "./Registry.ts";
5
- export * as RivetLogger from "./RivetLogger.ts";
6
6
  export * as RivetError from "./RivetError.ts";
7
7
  export * as State from "./State.ts";