@rivetkit/effect 0.0.0-06-09-refactor-rivetkit-split-workflow-context-into-workflowcontext-workflowstepcontext.e0c9540
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/dist/Action.d.ts +104 -0
- package/dist/Action.d.ts.map +1 -0
- package/dist/Action.js +50 -0
- package/dist/Action.js.map +1 -0
- package/dist/Actor.d.ts +133 -0
- package/dist/Actor.d.ts.map +1 -0
- package/dist/Actor.js +104 -0
- package/dist/Actor.js.map +1 -0
- package/dist/Client.d.ts +31 -0
- package/dist/Client.d.ts.map +1 -0
- package/dist/Client.js +98 -0
- package/dist/Client.js.map +1 -0
- package/dist/Logger.d.ts +29 -0
- package/dist/Logger.d.ts.map +1 -0
- package/dist/Logger.js +31 -0
- package/dist/Logger.js.map +1 -0
- package/dist/Registry.d.ts +72 -0
- package/dist/Registry.d.ts.map +1 -0
- package/dist/Registry.js +125 -0
- package/dist/Registry.js.map +1 -0
- package/dist/RivetError.d.ts +438 -0
- package/dist/RivetError.d.ts.map +1 -0
- package/dist/RivetError.js +873 -0
- package/dist/RivetError.js.map +1 -0
- package/dist/State.d.ts +123 -0
- package/dist/State.d.ts.map +1 -0
- package/dist/State.js +104 -0
- package/dist/State.js.map +1 -0
- package/dist/internal/ActionDispatcher.d.ts +14 -0
- package/dist/internal/ActionDispatcher.d.ts.map +1 -0
- package/dist/internal/ActionDispatcher.js +100 -0
- package/dist/internal/ActionDispatcher.js.map +1 -0
- package/dist/internal/ActionErrorEnvelope.d.ts +11 -0
- package/dist/internal/ActionErrorEnvelope.d.ts.map +1 -0
- package/dist/internal/ActionErrorEnvelope.js +14 -0
- package/dist/internal/ActionErrorEnvelope.js.map +1 -0
- package/dist/internal/ActorInstanceManager.d.ts +28 -0
- package/dist/internal/ActorInstanceManager.d.ts.map +1 -0
- package/dist/internal/ActorInstanceManager.js +51 -0
- package/dist/internal/ActorInstanceManager.js.map +1 -0
- package/dist/internal/ActorStateAdapter.d.ts +18 -0
- package/dist/internal/ActorStateAdapter.d.ts.map +1 -0
- package/dist/internal/ActorStateAdapter.js +29 -0
- package/dist/internal/ActorStateAdapter.js.map +1 -0
- package/dist/internal/StateOptions.d.ts +12 -0
- package/dist/internal/StateOptions.d.ts.map +1 -0
- package/dist/internal/StateOptions.js +2 -0
- package/dist/internal/StateOptions.js.map +1 -0
- package/dist/internal/logging.d.ts +23 -0
- package/dist/internal/logging.d.ts.map +1 -0
- package/dist/internal/logging.js +162 -0
- package/dist/internal/logging.js.map +1 -0
- package/dist/internal/tracing.d.ts +23 -0
- package/dist/internal/tracing.d.ts.map +1 -0
- package/dist/internal/tracing.js +30 -0
- package/dist/internal/tracing.js.map +1 -0
- package/dist/internal/utils.d.ts +7 -0
- package/dist/internal/utils.d.ts.map +1 -0
- package/dist/internal/utils.js +7 -0
- package/dist/internal/utils.js.map +1 -0
- package/dist/mod.d.ts +8 -0
- package/dist/mod.d.ts.map +1 -0
- package/dist/mod.js +8 -0
- package/dist/mod.js.map +1 -0
- package/package.json +46 -0
- package/src/Action.ts +231 -0
- package/src/Actor.test-d.ts +603 -0
- package/src/Actor.test.ts +206 -0
- package/src/Actor.ts +550 -0
- package/src/Client.test.ts +210 -0
- package/src/Client.ts +216 -0
- package/src/Logger.ts +43 -0
- package/src/Registry.test-d.ts +126 -0
- package/src/Registry.test.ts +411 -0
- package/src/Registry.ts +243 -0
- package/src/RivetError.test.ts +188 -0
- package/src/RivetError.ts +1044 -0
- package/src/State.test.ts +181 -0
- package/src/State.ts +224 -0
- package/src/internal/ActionDispatcher.ts +192 -0
- package/src/internal/ActionErrorEnvelope.ts +19 -0
- package/src/internal/ActorInstanceManager.ts +143 -0
- package/src/internal/ActorStateAdapter.ts +88 -0
- package/src/internal/StateOptions.ts +17 -0
- package/src/internal/logging.test.ts +288 -0
- package/src/internal/logging.ts +237 -0
- package/src/internal/tracing.ts +42 -0
- package/src/internal/utils.ts +12 -0
- package/src/mod.ts +7 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { Context, Effect, Exit, type Fiber, FiberSet, Scope } from "effect";
|
|
2
|
+
import type * as Rivetkit from "rivetkit";
|
|
3
|
+
import type * as RivetkitDb from "rivetkit/db";
|
|
4
|
+
import type * as ActorStateAdapter from "./ActorStateAdapter.ts";
|
|
5
|
+
import type * as StateOptions from "./StateOptions.ts";
|
|
6
|
+
|
|
7
|
+
type RivetkitDefinitionFor<
|
|
8
|
+
StateDefinition extends StateOptions.Any,
|
|
9
|
+
Database extends RivetkitDb.AnyDatabaseProvider,
|
|
10
|
+
> = Rivetkit.ActorDefinition<
|
|
11
|
+
StateOptions.Encoded<StateDefinition>,
|
|
12
|
+
undefined,
|
|
13
|
+
undefined,
|
|
14
|
+
undefined,
|
|
15
|
+
undefined,
|
|
16
|
+
Database,
|
|
17
|
+
Record<never, never>,
|
|
18
|
+
Record<never, never>,
|
|
19
|
+
any
|
|
20
|
+
>;
|
|
21
|
+
|
|
22
|
+
type WakeContext<
|
|
23
|
+
StateDefinition extends StateOptions.Any,
|
|
24
|
+
Database extends RivetkitDb.AnyDatabaseProvider,
|
|
25
|
+
> = Rivetkit.WakeContextOf<RivetkitDefinitionFor<StateDefinition, Database>>;
|
|
26
|
+
|
|
27
|
+
export type Instance<
|
|
28
|
+
ActionHandlers,
|
|
29
|
+
StateDefinition extends StateOptions.Any,
|
|
30
|
+
> = {
|
|
31
|
+
readonly actionHandlers: ActionHandlers;
|
|
32
|
+
readonly runFork: <A, E>(
|
|
33
|
+
effect: Effect.Effect<A, E, any>,
|
|
34
|
+
options?: Effect.RunOptions,
|
|
35
|
+
) => Fiber.Fiber<A, E>;
|
|
36
|
+
readonly scope: Scope.Closeable;
|
|
37
|
+
readonly state?: ActorStateAdapter.ActorState<StateDefinition>;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const make = Effect.fnUntraced(function* <
|
|
41
|
+
ActionHandlers,
|
|
42
|
+
StateDefinition extends StateOptions.Any,
|
|
43
|
+
Database extends RivetkitDb.AnyDatabaseProvider,
|
|
44
|
+
WakeOptions,
|
|
45
|
+
>({
|
|
46
|
+
wakeHandler,
|
|
47
|
+
stateAdapter,
|
|
48
|
+
makeContext,
|
|
49
|
+
makeWakeOptions,
|
|
50
|
+
}: {
|
|
51
|
+
readonly wakeHandler: (
|
|
52
|
+
wakeOptions: WakeOptions,
|
|
53
|
+
) => Effect.Effect<ActionHandlers, never, any>;
|
|
54
|
+
readonly stateAdapter:
|
|
55
|
+
| ActorStateAdapter.Adapter<StateDefinition>
|
|
56
|
+
| undefined;
|
|
57
|
+
readonly makeContext: (
|
|
58
|
+
c: WakeContext<StateDefinition, Database>,
|
|
59
|
+
scope: Scope.Closeable,
|
|
60
|
+
) => Context.Context<any>;
|
|
61
|
+
readonly makeWakeOptions: (
|
|
62
|
+
c: WakeContext<StateDefinition, Database>,
|
|
63
|
+
state: ActorStateAdapter.ActorState<StateDefinition> | undefined,
|
|
64
|
+
) => WakeOptions;
|
|
65
|
+
}) {
|
|
66
|
+
const instances = new Map<
|
|
67
|
+
string,
|
|
68
|
+
Instance<ActionHandlers, StateDefinition>
|
|
69
|
+
>();
|
|
70
|
+
|
|
71
|
+
const services = yield* Effect.context<any>();
|
|
72
|
+
const runPromise = Effect.runPromiseWith(services);
|
|
73
|
+
|
|
74
|
+
const makeInstance = Effect.fnUntraced(function* (
|
|
75
|
+
c: WakeContext<StateDefinition, Database>,
|
|
76
|
+
): Effect.fn.Return<Instance<ActionHandlers, StateDefinition>, never, any> {
|
|
77
|
+
const scope = yield* Scope.make();
|
|
78
|
+
return yield* Effect.gen(function* () {
|
|
79
|
+
const state = stateAdapter
|
|
80
|
+
? yield* stateAdapter.makeStateView(c)
|
|
81
|
+
: undefined;
|
|
82
|
+
const context = makeContext(c, scope);
|
|
83
|
+
const actionHandlers = yield* wakeHandler(
|
|
84
|
+
makeWakeOptions(c, state),
|
|
85
|
+
).pipe(Effect.provide(context));
|
|
86
|
+
const runFork = yield* FiberSet.makeRuntime<
|
|
87
|
+
any,
|
|
88
|
+
unknown,
|
|
89
|
+
unknown
|
|
90
|
+
>().pipe(Effect.provide(Context.merge(services, context)));
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
actionHandlers,
|
|
94
|
+
runFork,
|
|
95
|
+
scope,
|
|
96
|
+
state,
|
|
97
|
+
};
|
|
98
|
+
}).pipe(
|
|
99
|
+
Effect.onError((cause) =>
|
|
100
|
+
Scope.close(scope, Exit.failCause(cause)),
|
|
101
|
+
),
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
get: (actorId: string) => instances.get(actorId),
|
|
107
|
+
onWake: async (c: WakeContext<StateDefinition, Database>) => {
|
|
108
|
+
await runPromise(
|
|
109
|
+
makeInstance(c).pipe(
|
|
110
|
+
Effect.tap((instance) =>
|
|
111
|
+
Effect.sync(() => {
|
|
112
|
+
instances.set(c.actorId, instance);
|
|
113
|
+
}),
|
|
114
|
+
),
|
|
115
|
+
),
|
|
116
|
+
);
|
|
117
|
+
},
|
|
118
|
+
onStateChange: stateAdapter
|
|
119
|
+
? (
|
|
120
|
+
c: WakeContext<StateDefinition, Database>,
|
|
121
|
+
newState: unknown,
|
|
122
|
+
) => {
|
|
123
|
+
const instance = instances.get(c.actorId);
|
|
124
|
+
// State changes can arrive after teardown removes the instance.
|
|
125
|
+
if (!instance) return;
|
|
126
|
+
|
|
127
|
+
stateAdapter.publishChange(instance, newState);
|
|
128
|
+
}
|
|
129
|
+
: undefined,
|
|
130
|
+
onTeardown: async (c: { readonly actorId: string }) => {
|
|
131
|
+
return runPromise(
|
|
132
|
+
Effect.gen(function* () {
|
|
133
|
+
const instance = instances.get(c.actorId);
|
|
134
|
+
// Teardown can be reported through multiple lifecycle callbacks.
|
|
135
|
+
if (!instance) return;
|
|
136
|
+
|
|
137
|
+
instances.delete(c.actorId);
|
|
138
|
+
yield* Scope.close(instance.scope, Exit.void);
|
|
139
|
+
}),
|
|
140
|
+
);
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Effect, type Fiber, Schema, Semaphore } from "effect";
|
|
2
|
+
import * as State from "../State.ts";
|
|
3
|
+
import type * as StateOptions from "./StateOptions.ts";
|
|
4
|
+
|
|
5
|
+
export type ActorState<StateDefinition extends StateOptions.Any> = State.State<
|
|
6
|
+
StateOptions.Decoded<StateDefinition>,
|
|
7
|
+
Schema.SchemaError
|
|
8
|
+
>;
|
|
9
|
+
|
|
10
|
+
type StateInstance<StateDefinition extends StateOptions.Any> = {
|
|
11
|
+
readonly runFork: <A, E>(
|
|
12
|
+
effect: Effect.Effect<A, E, any>,
|
|
13
|
+
options?: Effect.RunOptions,
|
|
14
|
+
) => Fiber.Fiber<A, E>;
|
|
15
|
+
readonly state?: ActorState<StateDefinition>;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type Adapter<StateDefinition extends StateOptions.Any> = {
|
|
19
|
+
readonly makeStateView: (c: {
|
|
20
|
+
state: StateOptions.Encoded<StateDefinition>;
|
|
21
|
+
}) => Effect.Effect<ActorState<StateDefinition>, never, any>;
|
|
22
|
+
readonly createInitialState: () => Promise<
|
|
23
|
+
StateOptions.Encoded<StateDefinition>
|
|
24
|
+
>;
|
|
25
|
+
readonly publishChange: (
|
|
26
|
+
instance: StateInstance<StateDefinition>,
|
|
27
|
+
newState: unknown,
|
|
28
|
+
) => void;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const make = Effect.fnUntraced(function* <
|
|
32
|
+
StateDefinition extends StateOptions.Any,
|
|
33
|
+
>(
|
|
34
|
+
stateOptions: StateDefinition,
|
|
35
|
+
): Effect.fn.Return<Adapter<StateDefinition>, never, any> {
|
|
36
|
+
const services = yield* Effect.context<any>();
|
|
37
|
+
|
|
38
|
+
const stateCodec = {
|
|
39
|
+
decodeUnknown: Schema.decodeUnknownEffect(
|
|
40
|
+
Schema.toCodecJson(stateOptions.schema),
|
|
41
|
+
),
|
|
42
|
+
encode: Schema.encodeEffect(Schema.toCodecJson(stateOptions.schema)),
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
makeStateView: (c) =>
|
|
47
|
+
State.make(
|
|
48
|
+
() => stateCodec.decodeUnknown(c.state),
|
|
49
|
+
(next) =>
|
|
50
|
+
stateCodec.encode(next).pipe(
|
|
51
|
+
Effect.tap((encoded) =>
|
|
52
|
+
Effect.sync(() => {
|
|
53
|
+
c.state = encoded;
|
|
54
|
+
}),
|
|
55
|
+
),
|
|
56
|
+
Effect.asVoid,
|
|
57
|
+
),
|
|
58
|
+
).pipe(
|
|
59
|
+
Effect.orDie,
|
|
60
|
+
Effect.map((state) => state as ActorState<StateDefinition>),
|
|
61
|
+
),
|
|
62
|
+
createInitialState: () =>
|
|
63
|
+
Effect.runPromiseWith(services)(
|
|
64
|
+
stateCodec
|
|
65
|
+
.encode(stateOptions.initialValue())
|
|
66
|
+
.pipe(Effect.orDie),
|
|
67
|
+
),
|
|
68
|
+
publishChange: (instance, newState) => {
|
|
69
|
+
instance.runFork(
|
|
70
|
+
Effect.gen(function* () {
|
|
71
|
+
const state = yield* Effect.fromNullishOr(
|
|
72
|
+
instance.state,
|
|
73
|
+
).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
|
+
}),
|
|
83
|
+
);
|
|
84
|
+
}),
|
|
85
|
+
);
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Schema } from "effect";
|
|
2
|
+
|
|
3
|
+
export interface StateOptions<in out S extends Schema.Top> {
|
|
4
|
+
readonly schema: S;
|
|
5
|
+
readonly initialValue: () => S["Type"];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface Any {
|
|
9
|
+
readonly schema: Schema.Top;
|
|
10
|
+
readonly initialValue: () => unknown;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type Encoded<State extends Any> =
|
|
14
|
+
| State["schema"]["Encoded"]
|
|
15
|
+
| ([State] extends [never] ? undefined : never);
|
|
16
|
+
|
|
17
|
+
export type Decoded<State extends Any> = State["schema"]["Type"];
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { assert, describe, it } from "@effect/vitest";
|
|
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";
|
|
11
|
+
import * as Logging from "./logging.ts";
|
|
12
|
+
|
|
13
|
+
type LogEntry = {
|
|
14
|
+
readonly level: string;
|
|
15
|
+
readonly fields: Record<string, unknown>;
|
|
16
|
+
readonly msg: string | undefined;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function makeTestLogger(entries: Array<LogEntry>): PinoLogger {
|
|
20
|
+
const logger: Record<string, unknown> = {};
|
|
21
|
+
for (const level of [
|
|
22
|
+
"trace",
|
|
23
|
+
"debug",
|
|
24
|
+
"info",
|
|
25
|
+
"warn",
|
|
26
|
+
"error",
|
|
27
|
+
"fatal",
|
|
28
|
+
]) {
|
|
29
|
+
logger[level] = (
|
|
30
|
+
fields: Record<string, unknown>,
|
|
31
|
+
msg?: string,
|
|
32
|
+
): void => {
|
|
33
|
+
entries.push({ level, fields, msg });
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return logger as unknown as PinoLogger;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
describe("internal/logging", () => {
|
|
41
|
+
it("serializes actor keys like the RivetKit actor runtime logger", () => {
|
|
42
|
+
assert.strictEqual(Logging.serializeActorKey([]), "/");
|
|
43
|
+
assert.strictEqual(Logging.serializeActorKey(["room", "1"]), "room/1");
|
|
44
|
+
assert.strictEqual(Logging.serializeActorKey(["room/1"]), "room\\/1");
|
|
45
|
+
assert.strictEqual(Logging.serializeActorKey([""]), "\\0");
|
|
46
|
+
assert.strictEqual(Logging.serializeActorKey(["a\\b"]), "a\\\\b");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("builds actor log annotations with serialized keys", () => {
|
|
50
|
+
assert.deepStrictEqual(
|
|
51
|
+
Logging.makeActorLogAnnotations({
|
|
52
|
+
name: "ChatRoom",
|
|
53
|
+
key: ["room/1"],
|
|
54
|
+
actorId: "actor-1",
|
|
55
|
+
}),
|
|
56
|
+
{
|
|
57
|
+
actor: "ChatRoom",
|
|
58
|
+
key: "room\\/1",
|
|
59
|
+
actorId: "actor-1",
|
|
60
|
+
},
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it.effect("writes Effect logs through the RivetKit base logger", () =>
|
|
65
|
+
Effect.gen(function* () {
|
|
66
|
+
const entries: Array<LogEntry> = [];
|
|
67
|
+
const baseLogger = makeTestLogger(entries);
|
|
68
|
+
|
|
69
|
+
yield* Effect.logInfo("room awake", { roomId: "abc" }).pipe(
|
|
70
|
+
Effect.annotateLogs({
|
|
71
|
+
actor: "ChatRoom",
|
|
72
|
+
key: "room-1",
|
|
73
|
+
actorId: "actor-1",
|
|
74
|
+
}),
|
|
75
|
+
Effect.provide(
|
|
76
|
+
EffectLogger.layer([Logging.makeEffectLogger(baseLogger)]),
|
|
77
|
+
),
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
assert.deepStrictEqual(entries, [
|
|
81
|
+
{
|
|
82
|
+
level: "info",
|
|
83
|
+
fields: {
|
|
84
|
+
roomId: "abc",
|
|
85
|
+
actor: "ChatRoom",
|
|
86
|
+
key: "room-1",
|
|
87
|
+
actorId: "actor-1",
|
|
88
|
+
},
|
|
89
|
+
msg: "room awake",
|
|
90
|
+
},
|
|
91
|
+
]);
|
|
92
|
+
}),
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
it.effect("preserves Error log messages as structured error fields", () =>
|
|
96
|
+
Effect.gen(function* () {
|
|
97
|
+
const entries: Array<LogEntry> = [];
|
|
98
|
+
const baseLogger = makeTestLogger(entries);
|
|
99
|
+
const error = new Error("room failed to wake");
|
|
100
|
+
|
|
101
|
+
yield* Effect.logError(error).pipe(
|
|
102
|
+
Effect.provide(
|
|
103
|
+
EffectLogger.layer([Logging.makeEffectLogger(baseLogger)]),
|
|
104
|
+
),
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const entry = entries[0];
|
|
108
|
+
assert.ok(entry !== undefined);
|
|
109
|
+
assert.strictEqual(entry.level, "error");
|
|
110
|
+
assert.strictEqual(entry.fields.error, error);
|
|
111
|
+
assert.strictEqual(entry.msg, error.message);
|
|
112
|
+
}),
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
it.effect("preserves Error log messages with additional fields", () =>
|
|
116
|
+
Effect.gen(function* () {
|
|
117
|
+
const entries: Array<LogEntry> = [];
|
|
118
|
+
const baseLogger = makeTestLogger(entries);
|
|
119
|
+
const error = new Error("action dispatch failed");
|
|
120
|
+
|
|
121
|
+
yield* Effect.logError(error, {
|
|
122
|
+
actorId: "actor-1",
|
|
123
|
+
action: "SendMessage",
|
|
124
|
+
}).pipe(
|
|
125
|
+
Effect.provide(
|
|
126
|
+
EffectLogger.layer([Logging.makeEffectLogger(baseLogger)]),
|
|
127
|
+
),
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const entry = entries[0];
|
|
131
|
+
assert.ok(entry !== undefined);
|
|
132
|
+
assert.strictEqual(entry.level, "error");
|
|
133
|
+
assert.strictEqual(entry.fields.error, error);
|
|
134
|
+
assert.strictEqual(entry.fields.actorId, "actor-1");
|
|
135
|
+
assert.strictEqual(entry.fields.action, "SendMessage");
|
|
136
|
+
assert.strictEqual(entry.msg, error.message);
|
|
137
|
+
}),
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
it.effect("uses References.MinimumLogLevel when creating the base logger", () =>
|
|
141
|
+
Effect.gen(function* () {
|
|
142
|
+
const baseLogger = yield* Logging.makeDefaultBaseLogger;
|
|
143
|
+
|
|
144
|
+
assert.strictEqual(baseLogger.level, "debug");
|
|
145
|
+
}).pipe(Effect.provideService(References.MinimumLogLevel, "Debug")),
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
it.effect("accepts the shared Pino RIVET_LOG_LEVEL values", () =>
|
|
149
|
+
Effect.gen(function* () {
|
|
150
|
+
const baseLogger = yield* Logging.makeDefaultBaseLogger;
|
|
151
|
+
|
|
152
|
+
assert.strictEqual(baseLogger.level, "silent");
|
|
153
|
+
}).pipe(
|
|
154
|
+
Effect.provideService(
|
|
155
|
+
ConfigProvider.ConfigProvider,
|
|
156
|
+
ConfigProvider.fromEnv({
|
|
157
|
+
env: {
|
|
158
|
+
RIVET_LOG_LEVEL: "silent",
|
|
159
|
+
},
|
|
160
|
+
}),
|
|
161
|
+
),
|
|
162
|
+
),
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
it.effect("prefers References.MinimumLogLevel over shared env values", () =>
|
|
166
|
+
Effect.gen(function* () {
|
|
167
|
+
const baseLogger = yield* Logging.makeDefaultBaseLogger;
|
|
168
|
+
|
|
169
|
+
assert.strictEqual(baseLogger.level, "debug");
|
|
170
|
+
}).pipe(
|
|
171
|
+
Effect.provideService(References.MinimumLogLevel, "Debug"),
|
|
172
|
+
Effect.provideService(
|
|
173
|
+
ConfigProvider.ConfigProvider,
|
|
174
|
+
ConfigProvider.fromEnv({
|
|
175
|
+
env: {
|
|
176
|
+
RIVET_LOG_LEVEL: "silent",
|
|
177
|
+
},
|
|
178
|
+
}),
|
|
179
|
+
),
|
|
180
|
+
),
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
it.effect("preserves an explicit Info minimum log level", () =>
|
|
184
|
+
Effect.gen(function* () {
|
|
185
|
+
const baseLogger = yield* Logging.makeDefaultBaseLogger;
|
|
186
|
+
|
|
187
|
+
assert.strictEqual(baseLogger.level, "info");
|
|
188
|
+
}).pipe(
|
|
189
|
+
Effect.provideService(References.MinimumLogLevel, "Info"),
|
|
190
|
+
Effect.provideService(
|
|
191
|
+
ConfigProvider.ConfigProvider,
|
|
192
|
+
ConfigProvider.fromEnv({
|
|
193
|
+
env: {
|
|
194
|
+
RIVET_LOG_LEVEL: "silent",
|
|
195
|
+
},
|
|
196
|
+
}),
|
|
197
|
+
),
|
|
198
|
+
),
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
it.effect("uses Config.logLevel values provided to References.MinimumLogLevel", () =>
|
|
202
|
+
Effect.gen(function* () {
|
|
203
|
+
const baseLogger = yield* Logging.makeDefaultBaseLogger;
|
|
204
|
+
|
|
205
|
+
assert.strictEqual(baseLogger.level, "trace");
|
|
206
|
+
}).pipe(
|
|
207
|
+
Effect.provide(
|
|
208
|
+
Layer.effect(
|
|
209
|
+
References.MinimumLogLevel,
|
|
210
|
+
Config.logLevel("RIVET_LOG_LEVEL"),
|
|
211
|
+
),
|
|
212
|
+
),
|
|
213
|
+
Effect.provideService(
|
|
214
|
+
ConfigProvider.ConfigProvider,
|
|
215
|
+
ConfigProvider.fromEnv({
|
|
216
|
+
env: {
|
|
217
|
+
RIVET_LOG_LEVEL: "Trace",
|
|
218
|
+
},
|
|
219
|
+
}),
|
|
220
|
+
),
|
|
221
|
+
),
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
it.effect("uses References.CurrentLogLevel for plain Effect.log calls", () =>
|
|
225
|
+
Effect.gen(function* () {
|
|
226
|
+
const entries: Array<LogEntry> = [];
|
|
227
|
+
const baseLogger = makeTestLogger(entries);
|
|
228
|
+
|
|
229
|
+
yield* Effect.log("plain log").pipe(
|
|
230
|
+
Effect.provideService(References.CurrentLogLevel, "Debug"),
|
|
231
|
+
Effect.provideService(References.MinimumLogLevel, "Debug"),
|
|
232
|
+
Effect.provide(
|
|
233
|
+
EffectLogger.layer([Logging.makeEffectLogger(baseLogger)]),
|
|
234
|
+
),
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
assert.deepStrictEqual(entries, [
|
|
238
|
+
{
|
|
239
|
+
level: "debug",
|
|
240
|
+
fields: {},
|
|
241
|
+
msg: "plain log",
|
|
242
|
+
},
|
|
243
|
+
]);
|
|
244
|
+
}),
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
it.effect("does not call a Pino method for the None current log level", () =>
|
|
248
|
+
Effect.gen(function* () {
|
|
249
|
+
const entries: Array<LogEntry> = [];
|
|
250
|
+
const baseLogger = makeTestLogger(entries);
|
|
251
|
+
|
|
252
|
+
yield* Effect.log("hidden log").pipe(
|
|
253
|
+
Effect.provideService(References.CurrentLogLevel, "None"),
|
|
254
|
+
Effect.provideService(References.MinimumLogLevel, "All"),
|
|
255
|
+
Effect.provide(
|
|
256
|
+
EffectLogger.layer([Logging.makeEffectLogger(baseLogger)]),
|
|
257
|
+
),
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
assert.deepStrictEqual(entries, []);
|
|
261
|
+
}),
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
it.effect("emits References.CurrentLogSpans as structured span durations", () =>
|
|
265
|
+
Effect.gen(function* () {
|
|
266
|
+
const entries: Array<LogEntry> = [];
|
|
267
|
+
const baseLogger = makeTestLogger(entries);
|
|
268
|
+
|
|
269
|
+
yield* Effect.logInfo("checkout complete").pipe(
|
|
270
|
+
Effect.withLogSpan("checkout"),
|
|
271
|
+
Effect.provide(
|
|
272
|
+
EffectLogger.layer([Logging.makeEffectLogger(baseLogger)]),
|
|
273
|
+
),
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
assert.strictEqual(entries.length, 1);
|
|
277
|
+
assert.strictEqual(entries[0]?.level, "info");
|
|
278
|
+
assert.strictEqual(entries[0]?.msg, "checkout complete");
|
|
279
|
+
assert.deepStrictEqual(Object.keys(entries[0]?.fields ?? {}), [
|
|
280
|
+
"spans",
|
|
281
|
+
]);
|
|
282
|
+
const spans = entries[0]?.fields.spans as
|
|
283
|
+
| Record<string, unknown>
|
|
284
|
+
| undefined;
|
|
285
|
+
assert.strictEqual(typeof spans?.checkout, "number");
|
|
286
|
+
}),
|
|
287
|
+
);
|
|
288
|
+
});
|