Created
January 13, 2026 14:26
-
-
Save dmmulroy/a487ac1e6eb830471da2c077d7f26b80 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { DurableObjectError } from "./errors"; | |
| import { Err, Ok, Result, type Result as ResultType } from "./result"; | |
| /** | |
| * Adds DurableObjectError to Result error unions, wraps non-Result returns. | |
| * - Promise<Result<T, E>> → Promise<Result<T, E | DurableObjectError>> | |
| * - Promise<T> → Promise<Result<T, DurableObjectError>> | |
| */ | |
| type AddDOError<R> = | |
| R extends Promise<ResultType<infer T, infer E>> | |
| ? Promise<ResultType<T, E | DurableObjectError>> | |
| : R extends Promise<infer T> | |
| ? Promise<ResultType<T, DurableObjectError>> | |
| : R; | |
| /** | |
| * Maps DO stub methods, adding DurableObjectError to all returns. | |
| * All async methods return Result with DurableObjectError in error union. | |
| */ | |
| export type EnhancedDOStub<T> = { | |
| [K in keyof T]: T[K] extends (...args: infer P) => infer R ? (...args: P) => AddDOError<R> : T[K]; | |
| }; | |
| /** | |
| * Wraps a Durable Object stub to: | |
| * 1. Rehydrate serialized Result returns (Ok/Err class instances) | |
| * 2. Catch DO infrastructure errors and wrap in DurableObjectError | |
| * 3. Wrap non-Result returns in Ok | |
| * | |
| * All methods return Result with DurableObjectError in error union. | |
| * | |
| * IMPORTANT: After any DO exception, stub may be broken. | |
| * Subsequent requests on same stub may fail. Recreate stub after errors. | |
| * | |
| * @param stub - DO stub to wrap | |
| * @returns Proxied stub with Result rehydration and error handling | |
| * | |
| * @example | |
| * const stub = EnhancedDOStub.from(namespace.get(id)); | |
| * const result = await stub.createUser(input); // Result<User, ParseError | DurableObjectError> | |
| */ | |
| const from = <T extends Rpc.DurableObjectBranded>( | |
| stub: DurableObjectStub<T>, | |
| ): EnhancedDOStub<T> => { | |
| return new Proxy(stub, { | |
| get(target, prop, receiver) { | |
| const value = Reflect.get(target, prop, receiver); | |
| if (typeof value !== "function") { | |
| return value; | |
| } | |
| return async (...args: unknown[]) => { | |
| let result: unknown; | |
| try { | |
| result = await value.apply(target, args); | |
| } catch (e) { | |
| // DO infrastructure error - wrap in DurableObjectError | |
| return new Err(DurableObjectError.from(e, String(prop))); | |
| } | |
| // Rehydrate serialized Results back into Ok/Err instances | |
| const hydrated = Result.hydrate(result); | |
| if (hydrated) { | |
| return hydrated; | |
| } | |
| // Non-Result return - wrap in Ok | |
| return new Ok(result); | |
| }; | |
| }, | |
| }) as unknown as EnhancedDOStub<T>; | |
| }; | |
| /** | |
| * Utilities for creating enhanced Durable Object stubs. | |
| * | |
| * @example | |
| * const stub = EnhancedDOStub.from(namespace.get(id)); | |
| * const result = await stub.method(); // Result<T, E | DurableObjectError> | |
| */ | |
| export const EnhancedDOStub = { | |
| from, | |
| } as const; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment