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
@@ -7,7 +7,7 @@ import {
type ClientRemote,
} from '@deepseek-ai/dsh-api-gateway/client'
import type { WorkspaceFollowFrame, WorkspaceFollowIncrement } from '../types.ts'
import type { WorkspaceFollowSink, WorkspaceRemote } from './model.ts'
import type { WorkspaceFollowSink } from './model.ts'
import { ClientWorkspaceModel } from './model.ts'
import { WorkspaceController } from './service.ts'
@@ -19,10 +19,6 @@ export { WorkspaceController, WorkspaceCreateError } from './service.ts'
export type { IWorkspaces, WorkspaceSource } from './service.ts'
export type { WorkspaceId, WorkspaceView } from '../types.ts'
type WorkspaceStreamRemote = Pick<ClientRemote, '$stream'> & {
readonly workspace: WorkspaceRemote
}
type WorkspaceBaselineFrame = Extract<WorkspaceFollowFrame, { type: 'baseline' }>
/** Gateway-owned snapshot stream configured for Workspace state. */
@@ -46,10 +42,9 @@ export const inject = ['remote', 'remote.workspace']
* @param ctx - Client root Context.
*/
export function apply(ctx: Context): void {
const remote = ctx.remote as WorkspaceStreamRemote
const model = new ClientWorkspaceModel(remote.workspace)
const model = new ClientWorkspaceModel(ctx.remote.workspace)
new WorkspaceController(ctx, model)
const control = createWorkspaceStateStream(remote, {
const control = createWorkspaceStateStream(ctx.remote, {
accept: model,
carrierFailed: () => { model.handleCarrierFailure() },
failed: (error) => { model.handleStreamFailure(error) },
@@ -73,12 +68,12 @@ export interface WorkspaceStateStreamOptions {
/**
* Create the reconnecting Workspace state stream.
* @param remote - generated Workspace namespace and Gateway stream factory.
* @param remote - Client Remote face carrying the Workspace namespace and the stream factory.
* @param options - Workspace state destinations.
* @returns an unstarted stream owned by the Client Workspace runtime.
*/
export function createWorkspaceStateStream(
remote: WorkspaceStreamRemote,
remote: ClientRemote,
options: WorkspaceStateStreamOptions,
): WorkspaceStateStream {
const stream = remote.$stream<WorkspaceFollowFrame>({
@@ -2,6 +2,7 @@
import { notifySubscribers } from '@deepseek-ai/dsh-client-store'
import type {} from '@deepseek-ai/dsh-api-workspace-controller/remote'
import { isRemoteFailure } from '@deepseek-ai/dsh-api-gateway/client'
import type { RemoteFailure, RemoteResult, TypertClientRemote } from '@deepseek-ai/dsh-typert-protocol'
import type {
WorkspaceArchiveSessionRequest,
@@ -82,12 +83,7 @@ export class ClientWorkspaceModel implements WorkspaceFollowSink {
* @returns generated Remote result.
*/
async create(input: WorkspaceCreateRequest): Promise<RemoteResult<WorkspaceCreateValue>> {
let result: RemoteResult<WorkspaceCreateValue>
try {
result = await this.remote.create(input)
} catch (error) {
result = failureResult(error)
}
const result = await this.remote.create(input)
if (result.ok) this.upsert(result.value.workspace)
return result
}
@@ -129,19 +125,10 @@ export class ClientWorkspaceModel implements WorkspaceFollowSink {
const frameGeneration = this.orderFrameGeneration
const localOrder = this.items.map(workspace => workspace.workspaceId)
this.installOrder(insertIdBefore(localOrder, workspaceId, beforeWorkspaceId))
let result: RemoteResult<WorkspaceOrderValue>
try {
result = await this.remote.insertBefore({
workspaceId,
...beforeWorkspaceId === undefined ? {} : { beforeWorkspaceId },
})
} catch (error) {
if (requestGeneration === this.orderRequestGeneration
&& frameGeneration === this.orderFrameGeneration) {
this.installOrder(this.committedOrder)
}
throw error
}
const result = await this.remote.insertBefore({
workspaceId,
...beforeWorkspaceId === undefined ? {} : { beforeWorkspaceId },
})
if (requestGeneration === this.orderRequestGeneration
&& frameGeneration === this.orderFrameGeneration) {
this.installOrder(result.ok ? result.value.workspaceIds : this.committedOrder, result.ok)
@@ -233,8 +220,9 @@ export class ClientWorkspaceModel implements WorkspaceFollowSink {
* @param error - terminal stream failure.
*/
handleStreamFailure(error: unknown): void {
if (!isRemoteFailure(error)) throw error
this.state = 'error'
this.error = failureOf(error)
this.error = error
this.invalidate()
}
@@ -369,15 +357,3 @@ function insertIdBefore(
const at = beforeId === undefined ? without.length : without.indexOf(beforeId)
return [...without.slice(0, at), id, ...without.slice(at)]
}
function failureResult<T>(error: unknown): RemoteResult<T> {
return { ok: false, error: failureOf(error) }
}
function failureOf(error: unknown): RemoteFailure {
return {
code: 'internal',
message: error instanceof Error ? error.message : String(error),
details: {},
}
}
@@ -11,7 +11,7 @@ import type { ClientWorkspaceModel, WorkspaceSnapshot } from './model.ts'
export class WorkspaceCreateError extends Error {
override readonly name = 'WorkspaceCreateError'
/** @param rpcError - Host business or folded transport failure. */
/** @param rpcError - Host business or folded carrier failure. */
constructor(readonly rpcError: RemoteFailure) {
super(`workspace create failed: ${rpcError.code}: ${rpcError.message}`)
}