Skip to content

Instantly share code, notes, and snippets.

@dmmulroy
Created January 13, 2026 14:26
Show Gist options
  • Select an option

  • Save dmmulroy/a487ac1e6eb830471da2c077d7f26b80 to your computer and use it in GitHub Desktop.

Select an option

Save dmmulroy/a487ac1e6eb830471da2c077d7f26b80 to your computer and use it in GitHub Desktop.
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