Skip to content

Commit f57edd7

Browse files
fix(otel): preserve typed sync contract errors
1 parent 2e289e8 commit f57edd7

2 files changed

Lines changed: 48 additions & 6 deletions

File tree

packages/@overeng/otel-contract/src/mod.ts

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
1-
import { DateTime, Duration, Effect, Either, Option, Redacted, Schema, Stream } from 'effect'
1+
import {
2+
Cause,
3+
DateTime,
4+
Duration,
5+
Effect,
6+
Either,
7+
Exit,
8+
Option,
9+
Redacted,
10+
Schema,
11+
Stream,
12+
} from 'effect'
213
import * as AST from 'effect/SchemaAST'
314

415
type OtelPrimitive = string | number | boolean
@@ -365,6 +376,16 @@ const primitiveFromUnknown = ({
365376
const effectFromEither = <A, E>(either: Either.Either<A, E>): Effect.Effect<A, E> =>
366377
Either.isRight(either) === true ? Effect.succeed(either.right) : Effect.fail(either.left)
367378

379+
const runSyncOrThrow = <A, E>(effect: Effect.Effect<A, E>): A =>
380+
Exit.match(Effect.runSyncExit(effect), {
381+
onSuccess: (value) => value,
382+
onFailure: (cause) => {
383+
const failure = Option.getOrUndefined(Cause.failureOption(cause))
384+
if (failure !== undefined) throw failure
385+
throw Cause.squash(cause)
386+
},
387+
})
388+
368389
const encodeUnknown = ({
369390
key,
370391
schema,
@@ -694,13 +715,13 @@ export const OtelAttrs = {
694715
(field) => field.attrKey === 'span.label' && field.role === 'span.label',
695716
),
696717
encode,
697-
encodeSync: (value) => Effect.runSync(encode(value).pipe(Effect.orDie)),
698-
unsafeEncode: (value) => Effect.runSync(encode(value).pipe(Effect.orDie)),
718+
encodeSync: (value) => runSyncOrThrow(encode(value)),
719+
unsafeEncode: (value) => runSyncOrThrow(encode(value)),
699720
}
700721
})
701722
},
702723
defineSync<S extends Schema.Schema.AnyNoContext>(schema: S): OtelAttrs<S> {
703-
return Effect.runSync(OtelAttrs.define(schema).pipe(Effect.orDie))
724+
return runSyncOrThrow(OtelAttrs.define(schema))
704725
},
705726
}
706727

@@ -1049,8 +1070,8 @@ function defineOperation<S extends Schema.Schema.AnyNoContext>(
10491070
...(options.root === undefined ? {} : { root: options.root }),
10501071
metadata,
10511072
encode,
1052-
encodeSync: (value) => Effect.runSync(encode(value).pipe(Effect.orDie)),
1053-
unsafeEncode: (value) => Effect.runSync(encode(value).pipe(Effect.orDie)),
1073+
encodeSync: (value) => runSyncOrThrow(encode(value)),
1074+
unsafeEncode: (value) => runSyncOrThrow(encode(value)),
10541075
with: withOperation as OtelOperationDefinition<S>['with'],
10551076
withRoot: withRootOperation as OtelOperationDefinition<S>['withRoot'],
10561077
withStream: withOperationStream as OtelOperationDefinition<S>['withStream'],

packages/@overeng/otel-contract/src/mod.unit.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,27 @@ describe('OtelAttrs', () => {
192192
})
193193
})
194194

195+
it('preserves typed contract errors in sync APIs', async () => {
196+
expect(() =>
197+
OtelAttrs.defineSync(
198+
Schema.Struct({
199+
nested: Schema.Struct({ id: Schema.String }).pipe(OtelAttr.key({ key: 'nested' })),
200+
}),
201+
),
202+
).toThrow(OtelAttrPlanError)
203+
204+
const attrs = await Effect.runPromise(
205+
OtelAttrs.define(
206+
Schema.Struct({
207+
count: Schema.Number.pipe(OtelAttr.key({ key: 'count' })),
208+
}),
209+
),
210+
)
211+
212+
expect(() => attrs.encodeSync({ count: Number.NaN })).toThrow(OtelAttrEncodeError)
213+
expect(() => attrs.unsafeEncode({ count: Number.NaN })).toThrow(OtelAttrEncodeError)
214+
})
215+
195216
it('validates explicit policy inputs before encoding', async () => {
196217
const Attrs = Schema.Struct({
197218
asJson: Schema.Struct({ id: Schema.String }).pipe(

0 commit comments

Comments
 (0)