refactor(api): converge the Remote failure vocabulary and client surface

Single RemoteError with a merge-extensible, domain-prefixed code map;
owners throw at the failure point; streams surface marked failures;
clients consume ctx.remote directly with isRemoteFailure as the only
discrimination point and construct no failure instances.
This commit is contained in:
imccyu
2026-08-28 22:37:36 +08:00
parent 12d7b4ed0c
commit 804b1ffbfc
252 changed files with 3182 additions and 3832 deletions
@@ -6,12 +6,14 @@
import { Context } from '@deepseek-ai/cordis'
import { z } from 'zod'
import { DirectoryPickerError } from '@deepseek-ai/dsh-host-directory-picker'
import type { DirectoryPickerCapabilities } from '@deepseek-ai/dsh-host-directory-picker'
import type {
DirectoryPickerCapabilities, DirectoryPickerErrorCode,
} from '@deepseek-ai/dsh-host-directory-picker'
// The seam owns the listing declaration; the generator requires the reference
// site to name that package rather than this package's re-export of it.
import type { DirectoryListing } from '@deepseek-ai/dsh-host-directory-picker/types'
import { Remote, TypertRemoteFailure, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'
import type { DirectoryPickerErrorDetailsMap } from './types.ts'
import { Remote, RemoteError, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'
import type { RemoteErrorCode } from '@deepseek-ai/dsh-typert-protocol'
const createDirectoryRequestSchema = z.object({
path: z.string(),
@@ -86,8 +88,8 @@ export class DirectoryPickerController extends TypertRemoteService {
async createDirectory(path: string, name: string): Promise<string> {
const request = createDirectoryRequestSchema.safeParse({ path, name })
if (!request.success) {
throw pickerFailureOf(
'bad-request',
throw new RemoteError(
'gateway/bad-request',
'invalid payload for host.createDirectory',
{ issues: request.error.issues },
)
@@ -107,8 +109,8 @@ export class DirectoryPickerController extends TypertRemoteService {
): DirectoryPickerCapabilities[Kind] {
const capability = this.ctx.directoryPicker.capability()
if (capability.kind !== kind) {
throw pickerFailureOf(
'directory-picker-unavailable',
throw new RemoteError(
'directory-picker/unavailable',
`directoryPicker.${method} needs the ${kind} capability; the composed picker serves "${capability.kind}"`,
{ capability: capability.kind },
)
@@ -118,19 +120,15 @@ export class DirectoryPickerController extends TypertRemoteService {
}
/**
* Raise one entry of the picking wire failure vocabulary.
* @param code - the failure code a caller discriminates on.
* @param message - operator-facing description.
* @param details - the payload this code carries.
* @returns the failure to throw across the Remote boundary.
* Wire code answered for each seam browse failure. The seam's closed codes are
* its own local vocabulary, so this controller owns the projection onto the
* `directory-picker/*` codes a Remote caller discriminates on.
*/
function pickerFailureOf<Code extends keyof DirectoryPickerErrorDetailsMap>(
code: Code,
message: string,
details: DirectoryPickerErrorDetailsMap[Code],
): TypertRemoteFailure {
return new TypertRemoteFailure({ code, message, details })
}
const BROWSE_FAILURE_CODES = {
'directory-unreadable': 'directory-picker/unreadable',
'directory-exists': 'directory-picker/exists',
'directory-create-failed': 'directory-picker/create-failed',
} as const satisfies Record<DirectoryPickerErrorCode, RemoteErrorCode>
/**
* Classify a browse-primitive rejection: the seam's own closed codes carry the
@@ -138,16 +136,21 @@ function pickerFailureOf<Code extends keyof DirectoryPickerErrorDetailsMap>(
* @param error - the primitive's rejection.
* @returns the failure to throw across the Remote boundary.
*/
function browseFailure(error: unknown): TypertRemoteFailure {
function browseFailure(error: unknown): RemoteError {
if (error instanceof DirectoryPickerError) {
return pickerFailureOf(error.code, error.message, { path: error.path })
return new RemoteError(
BROWSE_FAILURE_CODES[error.code],
error.message,
{ path: error.path },
{ cause: error },
)
}
return pickerFailureOf('internal', errorMessage(error), {})
return new RemoteError('gateway/internal', errorMessage(error), {}, { cause: error })
}
/**
* Classify a cancellable primitive's rejection. An abort is the caller's own
* timeout or disconnect, not a backend failure, so it answers `cancelled`
* timeout or disconnect, not a backend failure, so it answers `gateway/cancelled`
* before the business classification runs.
* @param error - the primitive's rejection.
* @param signal - the caller lifetime the primitive ran under.
@@ -160,10 +163,10 @@ function cancellableFailure(
signal: AbortSignal,
cancelled: string,
failed?: string,
): TypertRemoteFailure {
if (signal.aborted) return pickerFailureOf('cancelled', cancelled, {})
): RemoteError {
if (signal.aborted) return new RemoteError('gateway/cancelled', cancelled, {}, { cause: error })
if (failed === undefined) return browseFailure(error)
return pickerFailureOf('internal', `${failed}: ${errorMessage(error)}`, {})
return new RemoteError('gateway/internal', `${failed}: ${errorMessage(error)}`, {}, { cause: error })
}
function errorMessage(error: unknown): string {