import type { WorkOS } from "@workos-inc/node/worker"; import { GenericServerException, NotFoundException, WorkOS as WorkOSClient, } from "@workos-inc/node/worker"; import { Data, Effect, Option, Result, Schema } from "effect"; export interface WorkOSVaultObjectMetadata { readonly context: Record; readonly id: string; readonly updatedAt: Date; readonly versionId: string; } export interface WorkOSVaultObject { readonly id: string; readonly metadata: WorkOSVaultObjectMetadata; readonly name: string; readonly value?: string; } const WORKOS_KEK_NOT_READY_MESSAGE = "KEK was created but is not yet ready. This request can be retried."; const CauseWithStatusSchema = Schema.Struct({ status: Schema.Number, }); const decodeCauseWithStatusOption = Schema.decodeUnknownOption(CauseWithStatusSchema); const statusFromWorkOSCause = (cause: unknown): number | undefined => { if (cause instanceof GenericServerException || cause instanceof NotFoundException) { return cause.status; } return Option.match(decodeCauseWithStatusOption(cause), { onNone: () => undefined, onSome: (decoded) => decoded.status, }); }; const isKekNotReadyWorkOSCause = (cause: unknown): boolean => cause instanceof GenericServerException && // oxlint-disable-next-line executor/no-unknown-error-message -- boundary: WorkOS only exposes this retryable Vault condition through its SDK exception message cause.message.endsWith(WORKOS_KEK_NOT_READY_MESSAGE); export class WorkOSVaultClientError extends Data.TaggedError("WorkOSVaultClientError")<{ readonly cause: unknown; readonly message: string; readonly operation: string; readonly retryKind?: "kek_not_ready"; readonly status?: number; }> { constructor(options: { readonly cause: unknown; readonly message?: string; readonly operation: string; readonly retryKind?: "kek_not_ready"; readonly status?: number; }) { super({ cause: options.cause, message: options.message ?? `WorkOS Vault ${options.operation} failed`, operation: options.operation, retryKind: options.retryKind ?? (isKekNotReadyWorkOSCause(options.cause) ? "kek_not_ready" : undefined), status: options.status ?? statusFromWorkOSCause(options.cause), }); } } export class WorkOSVaultClientInstantiationError extends Data.TaggedError( "WorkOSVaultClientInstantiationError", )<{ readonly cause: unknown; }> {} // Promise-shaped facade onto the underlying WorkOS SDK. Module-private — the // public surface in `WorkOSVaultClient` is Effect-only. Test doubles import // this type to stand up an in-memory equivalent. export interface WorkOSVaultPromiseApi { readonly createObject: (options: { readonly name: string; readonly value: string; readonly context: Record; }) => Promise; readonly readObjectByName: (name: string) => Promise; readonly updateObject: (options: { readonly id: string; readonly value: string; readonly versionCheck?: string; }) => Promise; readonly deleteObject: (options: { readonly id: string }) => Promise; } export interface WorkOSVaultCredentials { readonly apiKey: string; readonly clientId: string; /** Optional WorkOS API base-URL override (e.g. a WorkOS emulator in tests). */ readonly apiUrl?: string; } const apiUrlOptions = ( url: string | undefined, ): { apiHostname?: string; port?: number; https?: boolean } => { if (!url) return {}; const parsed = new URL(url); return { apiHostname: parsed.hostname, ...(parsed.port ? { port: Number(parsed.port) } : {}), https: parsed.protocol === "https:", }; }; interface WorkOSVaultUseOptions { readonly expectedErrorStatuses?: readonly number[]; readonly expectedErrorOutcome?: string; } export interface WorkOSVaultClient { readonly use: ( operation: string, fn: (client: WorkOSVaultPromiseApi) => Promise, options?: WorkOSVaultUseOptions, ) => Effect.Effect; readonly createObject: (options: { readonly name: string; readonly value: string; readonly context: Record; }) => Effect.Effect; readonly readObjectByName: ( name: string, ) => Effect.Effect; readonly updateObject: (options: { readonly id: string; readonly value: string; readonly versionCheck?: string; }) => Effect.Effect; readonly deleteObject: (options: { readonly id: string; }) => Effect.Effect; } const isExpectedVaultError = ( error: WorkOSVaultClientError, options: WorkOSVaultUseOptions | undefined, ): boolean => { if (error.status === undefined) return false; return options?.expectedErrorStatuses?.includes(error.status) ?? false; }; export const makeWorkOSVaultClient = (workos: Pick): WorkOSVaultClient => { const client: WorkOSVaultPromiseApi = workos.vault; const use = ( operation: string, fn: (vault: WorkOSVaultPromiseApi) => Promise, options?: WorkOSVaultUseOptions, ): Effect.Effect => { const attempt = Effect.tryPromise({ try: () => fn(client), catch: (cause) => new WorkOSVaultClientError({ cause, operation }), }); const observed = attempt.pipe( Effect.result, Effect.flatMap((outcome) => Effect.gen(function* () { if (Result.isSuccess(outcome)) { yield* Effect.annotateCurrentSpan({ "workos_vault.outcome": "ok" }); return outcome; } const status = outcome.failure.status; if (isExpectedVaultError(outcome.failure, options)) { yield* Effect.annotateCurrentSpan({ "workos_vault.outcome": options?.expectedErrorOutcome ?? "expected_error", "workos_vault.status": status ?? "unknown", }); return outcome; } yield* Effect.annotateCurrentSpan({ "workos_vault.outcome": "error", "workos_vault.status": status ?? "unknown", }); return yield* Effect.fail(outcome.failure); }), ), Effect.withSpan(`workos_vault.${operation}`), ); return observed.pipe( Effect.flatMap((outcome) => Result.isSuccess(outcome) ? Effect.succeed(outcome.success) : Effect.fail(outcome.failure), ), ); }; return { use, createObject: (options) => use("create_object", (vault) => vault.createObject(options)), readObjectByName: (name) => use("read_object_by_name", (vault) => vault.readObjectByName(name), { expectedErrorOutcome: "expected_missing_object", expectedErrorStatuses: [400, 404], }), updateObject: (options) => use("update_object", (vault) => vault.updateObject(options)), deleteObject: (options) => use("delete_object", (vault) => vault.deleteObject(options)), }; }; export const makeConfiguredWorkOSVaultClient = ( credentials: WorkOSVaultCredentials, ): Effect.Effect => Effect.try({ try: () => makeWorkOSVaultClient( new WorkOSClient({ apiKey: credentials.apiKey, clientId: credentials.clientId, ...apiUrlOptions(credentials.apiUrl), }), ), catch: (cause) => new WorkOSVaultClientInstantiationError({ cause }), }).pipe(Effect.withSpan("workos_vault.make_client"));