import type { Span } from "@opentelemetry/api"; import { SpanKind } from "@opentelemetry/api"; import type { RunStore } from "@internal/run-store"; import type { PrismaClientOrTransaction } from "~/db.server"; import { $replica, prisma } from "~/db.server"; import type { AuthenticatedEnvironment } from "~/services/apiAuth.server"; import { attributesFromAuthenticatedEnv, tracer } from "../tracer.server"; import type { RunEngine } from "../runEngine.server"; import { engine } from "../runEngine.server"; import { runStore as defaultRunStore } from "../runStore.server"; import { ServiceValidationError } from "./common.server"; export { ServiceValidationError }; export abstract class BaseService { constructor( protected readonly _prisma: PrismaClientOrTransaction = prisma, protected readonly _replica: PrismaClientOrTransaction = $replica, protected readonly runStore: RunStore = defaultRunStore ) {} protected async traceWithEnv( trace: string, env: AuthenticatedEnvironment, fn: (span: Span) => Promise ): Promise { return tracer.startActiveSpan( `${this.constructor.name}.${trace}`, { attributes: attributesFromAuthenticatedEnv(env), kind: SpanKind.SERVER }, async (span) => { try { return await fn(span); } catch (e) { if (e instanceof ServiceValidationError) { throw e; } if (e instanceof Error) { span.recordException(e); } else { span.recordException(new Error(String(e))); } throw e; } finally { span.end(); } } ); } } export type WithRunEngineOptions = T & { prisma?: PrismaClientOrTransaction; engine?: RunEngine; }; export class WithRunEngine extends BaseService { protected readonly _engine: RunEngine; constructor(opts: { prisma?: PrismaClientOrTransaction; engine?: RunEngine } = {}) { super(opts.prisma); this._engine = opts.engine ?? engine; } }