refactor(typert): rename RemoteContext to RemoteScope
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/api/gateway/README.md
|
||||
README.md: e37359db71c1388667e9e61f538354711e90c0c1
|
||||
README.zh.md: 2054febb9a5423297c32b029b40a035062250aab
|
||||
README.md: 0e1a03d2016b8cfbe165dbf1b0a9802290b29502
|
||||
README.zh.md: 6b5ccff2340405cc0045147239c5bd4f3eead7da
|
||||
|
||||
@@ -6,9 +6,9 @@ Two-sided TypeRT RPC endpoint for Host and Client Cordis environments. The Host
|
||||
|
||||
## Host service: `TypertGatewayService` (ctx key: `typertGateway`)
|
||||
|
||||
`ctx.typertGateway.invoke()` resolves the current descriptor and Cordis Service for each call, validates exact named arguments, resolves registered object or Context identities, invokes the public business method, and validates its result. Business Services extend `GatewayService` and mark methods with `@Remote` or `@RemoteContext` from [`dsh-type-meta`](../../typert/type-meta/README.md); `bindTypeRTGateway()` remains available when another base class owns inheritance.
|
||||
`ctx.typertGateway.invoke()` resolves the current descriptor and Cordis Service for each call, validates exact named arguments, resolves registered object or Context identities, invokes the public business method, and validates its result. Business Services extend `GatewayService` and mark methods with `@Remote` or `@RemoteScope` from [`dsh-type-meta`](../../typert/type-meta/README.md); `bindTypeRTGateway()` remains available when another base class owns inheritance.
|
||||
|
||||
Strict mode reads generated invocation descriptors from `ctx.typert.local`. Lookup parameters use the currently active resolver in `ctx.typert.lookups`: the business package registers the stable declaration and default policy, while Host composition can override resolution behavior with effect-scoped `configure()`; `@RemoteContext` resolves its receiver through a registered Host Context provider. SRC mode is a development fallback for endpoints that have never had a strict definition; it parses simple parameter names and accepts only JSON-safe values for non-lookup parameters. Withdrawing an observed strict definition fails instead of weakening validation.
|
||||
Strict mode reads generated invocation descriptors from `ctx.typert.local`. Lookup parameters use the currently active resolver in `ctx.typert.lookups`: the business package registers the stable declaration and default policy, while Host composition can override resolution behavior with effect-scoped `configure()`; `@RemoteScope` resolves its receiver through a registered Host Context provider. SRC mode is a development fallback for endpoints that have never had a strict definition; it parses simple parameter names and accepts only JSON-safe values for non-lookup parameters. Withdrawing an observed strict definition fails instead of weakening validation.
|
||||
|
||||
The Host entry registers a trusted-host interceptor on Connection's shared `/api` FetchHandler. Connection passes this composite handler through its HTTP bridge; the handler dispatches claimed endpoints to Gateway and unclaimed endpoints to API Proxy. Direct `invoke()` calls preserve business errors; `TypertGatewayError` distinguishes failures owned by dispatch, binding, providers, lookup, Context, arguments, and codecs. A resolver may use `TypeRTLookupFailure` to carry an existing RPC error, preserving its original error code for policy rejections such as cold-resume failures or ownership fences.
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
|
||||
## Host 服务:`TypertGatewayService`(ctx key:`typertGateway`)
|
||||
|
||||
每次调用时,`ctx.typertGateway.invoke()` 都会解析当前的描述符和 Cordis 服务,校验具名参数是否完全匹配,解析已注册的对象或 Context 身份标识,调用公开的业务方法,并校验其结果。业务服务继承 [`dsh-type-meta`](../../typert/type-meta/README.md) 的 `GatewayService`,并用 `@Remote` 或 `@RemoteContext` 标记方法;已有其他基类时仍可改用 `bindTypeRTGateway()`。
|
||||
每次调用时,`ctx.typertGateway.invoke()` 都会解析当前的描述符和 Cordis 服务,校验具名参数是否完全匹配,解析已注册的对象或 Context 身份标识,调用公开的业务方法,并校验其结果。业务服务继承 [`dsh-type-meta`](../../typert/type-meta/README.md) 的 `GatewayService`,并用 `@Remote` 或 `@RemoteScope` 标记方法;已有其他基类时仍可改用 `bindTypeRTGateway()`。
|
||||
|
||||
严格模式从 `ctx.typert.local` 读取生成的调用描述符。查找参数使用 `ctx.typert.lookups` 中当前有效的 resolver:业务包注册稳定声明与默认策略,Host 组合可用 effect-scoped `configure()` 覆盖解析行为;`@RemoteContext` 则通过已注册的 Host Context 提供方解析其接收者。SRC 模式是开发阶段的回退路径,适用于从未具备严格定义的端点;它解析简单参数名,并且只允许非查找参数使用可安全表示为 JSON 的值。已观测到的严格定义一旦撤回,系统会直接报错,而不会降低校验强度。
|
||||
严格模式从 `ctx.typert.local` 读取生成的调用描述符。查找参数使用 `ctx.typert.lookups` 中当前有效的 resolver:业务包注册稳定声明与默认策略,Host 组合可用 effect-scoped `configure()` 覆盖解析行为;`@RemoteScope` 则通过已注册的 Host Context 提供方解析其接收者。SRC 模式是开发阶段的回退路径,适用于从未具备严格定义的端点;它解析简单参数名,并且只允许非查找参数使用可安全表示为 JSON 的值。已观测到的严格定义一旦撤回,系统会直接报错,而不会降低校验强度。
|
||||
|
||||
Connection 可用时,Host 入口会在 Connection 共享的 `/api` FetchHandler 上注册 trusted-host interceptor。Connection 把这个复合 handler 交给 HTTP bridge;handler 将已认领 endpoint 分发给 Gateway,未认领 endpoint 则交给 API Proxy。直接调用 `invoke()` 会保留业务错误;`TypertGatewayError` 可区分分发、绑定、提供方、查找、Context、参数和编解码器各自负责的故障。resolver 可以用 `TypeRTLookupFailure` 携带既有 RPC error,使冷恢复失败或 ownership fence 等策略拒绝保持原错误码。
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { ConnectionHandle } from '@deepseek-ai/dsh-client-connection/client
|
||||
import type {
|
||||
InvocationDescriptor,
|
||||
TypeRTContext,
|
||||
TypeRTRemoteContextApi,
|
||||
TypeRTRemoteScopeApi,
|
||||
TypeRTRemoteNamespace,
|
||||
} from '@deepseek-ai/dsh-type-meta'
|
||||
import TypertRegistry from '@deepseek-ai/dsh-typert-registry'
|
||||
@@ -24,7 +24,7 @@ declare module '@deepseek-ai/dsh-type-meta' {
|
||||
) => Promise<{ readonly ref: string }>
|
||||
}
|
||||
|
||||
interface TypeRTRemoteContextMap {
|
||||
interface TypeRTRemoteScopeMap {
|
||||
'fixture:goals/create': (
|
||||
request: { readonly objective: string },
|
||||
signal?: AbortSignal,
|
||||
@@ -38,7 +38,7 @@ declare module '@deepseek-ai/dsh-type-meta' {
|
||||
|
||||
}
|
||||
|
||||
type FixtureContext = Omit<Context, 'remote'> & { readonly remote: TypeRTRemoteContextApi<'fixture'> }
|
||||
type FixtureContext = Omit<Context, 'remote'> & { readonly remote: TypeRTRemoteScopeApi<'fixture'> }
|
||||
|
||||
const idSchema = z.string().min(1)
|
||||
const requestSchema = z.object({ objective: z.string().min(1) })
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { HttpServerService, WebRoute } from '@deepseek-ai/dsh-host-webserve
|
||||
import {
|
||||
bindTypeRTGateway,
|
||||
Remote,
|
||||
RemoteContext,
|
||||
RemoteScope,
|
||||
TypeRTLookupFailure,
|
||||
type InvocationDescriptor,
|
||||
type TypeRTContext,
|
||||
@@ -65,7 +65,7 @@ class GoalService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
@RemoteContext('gatewayFixture')
|
||||
@RemoteScope('gatewayFixture')
|
||||
rename(request: { readonly title: string }): unknown {
|
||||
this.calls.push('rename')
|
||||
return { title: request.title, scope: (this.ctx as MarkedContext).fixtureScope ?? 'root' }
|
||||
@@ -299,7 +299,7 @@ class ContextWireService extends Service {
|
||||
super(ctx, 'contextWire')
|
||||
}
|
||||
|
||||
@RemoteContext('gatewayFixture')
|
||||
@RemoteScope('gatewayFixture')
|
||||
run(agentId: string): string {
|
||||
return agentId
|
||||
}
|
||||
@@ -389,7 +389,7 @@ describe('TypertGatewayService', () => {
|
||||
expect(service.lastSignal?.aborted).toBe(false)
|
||||
})
|
||||
|
||||
it('resolves strict Remote Context identity without adding a business argument', async () => {
|
||||
it('resolves strict Remote Scope identity without adding a business argument', async () => {
|
||||
const { ctx, service } = await setup()
|
||||
const scoped = ctx.extend({ fixtureScope: 'agent-scope' })
|
||||
ctx.typert.contexts.registerHost('gatewayFixture', contextProvider(scoped))
|
||||
@@ -432,7 +432,7 @@ describe('TypertGatewayService', () => {
|
||||
expect(service.calls).toEqual([])
|
||||
})
|
||||
|
||||
it('derives SRC Remote Context identity and preserves the scoped Proxy receiver', async () => {
|
||||
it('derives SRC Remote Scope identity and preserves the scoped Proxy receiver', async () => {
|
||||
const { ctx } = await setup()
|
||||
const scoped = ctx.extend({ fixtureScope: 'agent-src' })
|
||||
ctx.typert.contexts.registerHost('gatewayFixture', contextProvider(scoped))
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
import { Context as CordisContext } from 'cordis'
|
||||
import type { Context, Fiber } from 'cordis'
|
||||
import type { SessionId } from '@deepseek-ai/dsh-client-connection/client'
|
||||
import type { TypeRTClientRemote, TypeRTRemoteContextApi } from '@deepseek-ai/dsh-type-meta'
|
||||
import type { TypeRTClientRemote, TypeRTRemoteScopeApi } from '@deepseek-ai/dsh-type-meta'
|
||||
|
||||
/** Client Cordis Context carrying one Agent identity and its scoped Remote namespaces. */
|
||||
export type AgentContext = Omit<Context, 'remote'> & {
|
||||
readonly remote: TypeRTClientRemote & TypeRTRemoteContextApi<'agent'>
|
||||
readonly remote: TypeRTClientRemote & TypeRTRemoteScopeApi<'agent'>
|
||||
}
|
||||
|
||||
/** Context tag written by {@link createScope}. */
|
||||
|
||||
@@ -1032,10 +1032,10 @@ class FaceAnalyzer {
|
||||
if (invocation.kind === 'context') {
|
||||
const context = this.contextDeclarations().get(invocation.context)
|
||||
if (context === undefined) {
|
||||
this.fail(method, `Remote Context ${invocation.context} has no TypeRTContextMap entry`)
|
||||
this.fail(method, `Remote Scope ${invocation.context} has no TypeRTContextMap entry`)
|
||||
}
|
||||
const wire = `${invocation.context}Id`
|
||||
if (wires.has(wire)) this.fail(method, `Remote Context wire field ${wire} conflicts with a method parameter`)
|
||||
if (wires.has(wire)) this.fail(method, `Remote Scope wire field ${wire} conflicts with a method parameter`)
|
||||
receiver = {
|
||||
kind: 'context',
|
||||
context: invocation.context,
|
||||
@@ -1200,18 +1200,18 @@ class FaceAnalyzer {
|
||||
}
|
||||
marker = { kind: 'direct', exportName }
|
||||
} else if (ts.isCallExpression(expression)
|
||||
&& this.isTypeMetaSymbol(expression.expression, 'RemoteContext')) {
|
||||
&& this.isTypeMetaSymbol(expression.expression, 'RemoteScope')) {
|
||||
if (expression.arguments.length < 1 || expression.arguments.length > 2) {
|
||||
this.fail(expression, 'RemoteContext() requires a Context key and optional exported method name')
|
||||
this.fail(expression, 'RemoteScope() requires a Context key and optional exported method name')
|
||||
}
|
||||
const context = stringLiteralValue(expression.arguments[0])
|
||||
if (context === undefined || !isRemoteSegment(context)) {
|
||||
this.fail(expression.arguments[0] ?? expression, 'RemoteContext() key must be a string literal containing only RPC endpoint segment characters')
|
||||
this.fail(expression.arguments[0] ?? expression, 'RemoteScope() key must be a string literal containing only RPC endpoint segment characters')
|
||||
}
|
||||
const exportArgument = expression.arguments[1]
|
||||
const exportName = exportArgument === undefined ? undefined : stringLiteralValue(exportArgument)
|
||||
if (exportArgument !== undefined && (exportName === undefined || !isRemoteSegment(exportName))) {
|
||||
this.fail(exportArgument, 'RemoteContext() name must be a string literal containing only RPC endpoint segment characters')
|
||||
this.fail(exportArgument, 'RemoteScope() name must be a string literal containing only RPC endpoint segment characters')
|
||||
}
|
||||
marker = { kind: 'context', context, ...exportName === undefined ? {} : { exportName } }
|
||||
} else {
|
||||
@@ -2529,7 +2529,7 @@ function sourceFileHasSurface(sourceFile: ts.SourceFile): boolean {
|
||||
? decorator.expression.expression
|
||||
: decorator.expression
|
||||
const name = expressionName(expression)
|
||||
if (name === 'Remote' || name === 'RemoteContext') return true
|
||||
if (name === 'Remote' || name === 'RemoteScope') return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,7 +376,7 @@ export class FaceModelEmitter {
|
||||
lines.push(' }')
|
||||
}
|
||||
if (scoped.length > 0) {
|
||||
lines.push(' interface TypeRTRemoteContextMap {')
|
||||
lines.push(' interface TypeRTRemoteScopeMap {')
|
||||
for (const invocation of scoped) {
|
||||
this.pushRemoteSignature(lines, sourceMap, packageModel, invocation, referenceNames, true)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { GatewayService, Remote, RemoteContext } from '@deepseek-ai/dsh-type-meta'
|
||||
import { GatewayService, Remote, RemoteScope } from '@deepseek-ai/dsh-type-meta'
|
||||
import type { Agent } from '@fixture/domain'
|
||||
import type {
|
||||
CreateGoalRequest,
|
||||
@@ -19,7 +19,7 @@ export class GoalService extends GatewayService {
|
||||
return { ref: `${agent.id}:${request.title}` }
|
||||
}
|
||||
|
||||
@RemoteContext('agent')
|
||||
@RemoteScope('agent')
|
||||
rename(request: RenameGoalRequest): RenameGoalResult {
|
||||
return { renamed: request.title.length > 0 }
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ declare module '@deepseek-ai/dsh-type-meta' {
|
||||
export interface TypeRTLookupMap {}
|
||||
export interface TypeRTContextMap {}
|
||||
export interface TypeRTRemoteMap {}
|
||||
export interface TypeRTRemoteContextMap {}
|
||||
export interface TypeRTRemoteScopeMap {}
|
||||
|
||||
export type TypeRTRemoteNamespace<Namespace extends string> = {
|
||||
[Endpoint in keyof TypeRTRemoteMap as Endpoint extends `${Namespace}/${infer Method}`
|
||||
@@ -56,7 +56,7 @@ declare module '@deepseek-ai/dsh-type-meta' {
|
||||
context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Result>,
|
||||
) => void
|
||||
|
||||
export function RemoteContext(key: Extract<keyof TypeRTContextMap, string>, exportName?: string):
|
||||
export function RemoteScope(key: Extract<keyof TypeRTContextMap, string>, exportName?: string):
|
||||
<This extends object, Args extends unknown[], Result>(
|
||||
method: (this: This, ...args: Args) => Result,
|
||||
context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Result>,
|
||||
|
||||
@@ -288,7 +288,7 @@ export interface BoxPayload {
|
||||
const root = copyFixture()
|
||||
editFile(root, 'packages/remote/src/index.ts', source => source
|
||||
.replace(' @Remote\n', '')
|
||||
.replace(" @RemoteContext('agent')\n", ''))
|
||||
.replace(" @RemoteScope('agent')\n", ''))
|
||||
editFile(root, 'packages/remote/src/types.ts', source => `${source}
|
||||
|
||||
/** @typert schema */
|
||||
@@ -377,8 +377,8 @@ export interface ClientMarker {
|
||||
name: 'duplicate GatewayService field binding',
|
||||
edit: (source: string) => source
|
||||
.replace(
|
||||
'import { GatewayService, Remote, RemoteContext }',
|
||||
'import { GatewayService, Remote, RemoteContext, bindTypeRTGateway }',
|
||||
'import { GatewayService, Remote, RemoteScope }',
|
||||
'import { GatewayService, Remote, RemoteScope, bindTypeRTGateway }',
|
||||
)
|
||||
.replace(
|
||||
'export class GoalService extends GatewayService {',
|
||||
@@ -495,11 +495,11 @@ export interface ClientMarker {
|
||||
expect(() => analyzeRemote(root)).not.toThrow()
|
||||
})
|
||||
|
||||
it('rejects a Remote Context without a static Context declaration', () => {
|
||||
it('rejects a Remote Scope without a static Context declaration', () => {
|
||||
const root = copyFixture()
|
||||
editFile(root, 'packages/remote/src/index.ts', source => source.replace("@RemoteContext('agent')", "@RemoteContext('missing')"))
|
||||
editFile(root, 'packages/remote/src/index.ts', source => source.replace("@RemoteScope('agent')", "@RemoteScope('missing')"))
|
||||
|
||||
expect(() => analyzeRemote(root, false)).toThrow(/Remote Context missing has no TypeRTContextMap entry/)
|
||||
expect(() => analyzeRemote(root, false)).toThrow(/Remote Scope missing has no TypeRTContextMap entry/)
|
||||
})
|
||||
|
||||
it('rejects a direct scoped projection whose Context and lookup wire symbols differ', () => {
|
||||
@@ -579,7 +579,7 @@ function assertRemoteConsumerTypechecks(
|
||||
import remote from '@fixture/remote/remote'
|
||||
import type {
|
||||
TypeRTRemoteContribution,
|
||||
TypeRTRemoteContextMap,
|
||||
TypeRTRemoteScopeMap,
|
||||
TypeRTRemoteMap,
|
||||
TypeRTRemoteNamespaceMap,
|
||||
} from '@deepseek-ai/dsh-type-meta'
|
||||
@@ -587,8 +587,8 @@ import type { CreateGoalResult, RenameGoalResult } from '@fixture/remote/types'
|
||||
|
||||
const contribution: TypeRTRemoteContribution = remote
|
||||
declare const create: TypeRTRemoteMap['goals/create']
|
||||
declare const createScoped: TypeRTRemoteContextMap['agent:goals/create']
|
||||
declare const rename: TypeRTRemoteContextMap['agent:goals/rename']
|
||||
declare const createScoped: TypeRTRemoteScopeMap['agent:goals/create']
|
||||
declare const rename: TypeRTRemoteScopeMap['agent:goals/rename']
|
||||
const created: Promise<CreateGoalResult> = create('agent-1', { title: 'ship' })
|
||||
const cancellable: Promise<CreateGoalResult> = create('agent-1', { title: 'ship' }, new AbortController().signal)
|
||||
const createdScoped: Promise<CreateGoalResult> = createScoped({ title: 'ship' })
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/typert/type-meta/README.md
|
||||
README.md: a76169742cb78d0d19814bcd0f978c71036a5a1c
|
||||
README.zh.md: 6f2d2fd6e241441fae8102c0639608e9b27b9bec
|
||||
README.md: 9bd475f8973ec54756fe0e63d5b7fa485381697d
|
||||
README.zh.md: 10a6309bc47001d572abb3dd3f794ebfcf6252e8
|
||||
|
||||
@@ -7,7 +7,7 @@ Compiler-independent declarations shared by business packages, generated TypeRT
|
||||
## Remote declarations
|
||||
|
||||
- `@Remote` marks a public instance method for direct invocation on its registered Cordis Service.
|
||||
- `@RemoteContext(key)` marks a method whose receiver is selected from a merge-declared scoped Context kind.
|
||||
- `@RemoteScope(key)` marks a method whose receiver is selected from a merge-declared scoped Context kind.
|
||||
- `GatewayService` binds the Cordis key passed to `super(ctx, serviceKey, options?)` to the same default wire namespace.
|
||||
- `bindTypeRTGateway(this, serviceKey, options?)` provides the same visible, frozen binding for a Service that cannot inherit from `GatewayService`.
|
||||
- `remoteMethods(service)` returns a detached declaration-order snapshot used by the Gateway's SRC fallback.
|
||||
@@ -18,7 +18,7 @@ Decorator initializers retain markers in a module-private `WeakMap` keyed by the
|
||||
|
||||
## TypeRT protocol
|
||||
|
||||
Business packages extend `TypeRTLookupMap` and `TypeRTContextMap` to associate Host objects or scoped Contexts with their wire identities. Generated artifacts extend `TypeRTRemoteMap`, `TypeRTRemoteContextMap`, and `TypeRTRemoteNamespaceMap` so Client imports expose only selected Remote methods. `InvocationDescriptor` is the shared runtime form consumed by the registry, Gateway, and Client API.
|
||||
Business packages extend `TypeRTLookupMap` and `TypeRTContextMap` to associate Host objects or scoped Contexts with their wire identities. Generated artifacts extend `TypeRTRemoteMap`, `TypeRTRemoteScopeMap`, and `TypeRTRemoteNamespaceMap` so Client imports expose only selected Remote methods. `InvocationDescriptor` is the shared runtime form consumed by the registry, Gateway, and Client Remote.
|
||||
|
||||
Lookup and Context packages own both sides of their contract: declaration merging supplies the static association, while runtime providers register identity resolution with `ctx.typert`. A lookup or Host Context provider supplies the stable declaration and default resolver, while Host composition may separately configure a synchronous or asynchronous resolver; policy rejections may use `TypeRTLookupFailure` to carry a failure value owned by the boundary adapter. Strict codecs carry generated schemas; `src-json` codecs identify the weaker source-launch path.
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
## Remote 声明
|
||||
|
||||
- `@Remote` 将公开实例方法标记为可在其注册的 Cordis 服务上直接调用。
|
||||
- `@RemoteContext(key)` 标记接收者选自合并声明的作用域 Context 类型的方法。
|
||||
- `@RemoteScope(key)` 标记接收者选自合并声明的作用域 Context 类型的方法。
|
||||
- `GatewayService` 将 `super(ctx, serviceKey, options?)` 接收的 Cordis key 同时绑定为默认 wire namespace。
|
||||
- `bindTypeRTGateway(this, serviceKey, options?)` 为无法继承 `GatewayService` 的 Service 提供同样可见且冻结的绑定。
|
||||
- `remoteMethods(service)` 返回按声明顺序排列、与内部状态分离的快照,供 Gateway 的 SRC 回退路径使用。
|
||||
@@ -18,7 +18,7 @@ Host 方法通过将 `signal: AbortSignal` 声明为最后一个参数来启用
|
||||
|
||||
## TypeRT 协议
|
||||
|
||||
业务包扩展 `TypeRTLookupMap` 和 `TypeRTContextMap`,以关联 Host 对象或作用域 Context 与其协议身份。生成的产物扩展 `TypeRTRemoteMap`、`TypeRTRemoteContextMap` 和 `TypeRTRemoteNamespaceMap`,使 Client 导入后仅暴露选定的 Remote 方法。`InvocationDescriptor` 是供注册表、Gateway 和 Client API 使用的共享运行时形式。
|
||||
业务包扩展 `TypeRTLookupMap` 和 `TypeRTContextMap`,以关联 Host 对象或作用域 Context 与其协议身份。生成的产物扩展 `TypeRTRemoteMap`、`TypeRTRemoteScopeMap` 和 `TypeRTRemoteNamespaceMap`,使 Client 导入后仅暴露选定的 Remote 方法。`InvocationDescriptor` 是供注册表、Gateway 和 Client Remote 使用的共享运行时形式。
|
||||
|
||||
查找包与 Context 包同时负责其契约的两侧:声明合并提供静态关联,运行时提供方则向 `ctx.typert` 注册身份解析。lookup 或 Host Context provider 提供稳定声明与默认 resolver,Host 组合可以另行配置同步或异步 resolver;策略拒绝可用 `TypeRTLookupFailure` 携带由边界适配器拥有的失败值。严格编解码器携带生成的 schema;`src-json` 编解码器标识约束更弱的源码启动路径。
|
||||
|
||||
|
||||
@@ -60,9 +60,9 @@ export type {
|
||||
TypeRTLookupResolver,
|
||||
TypeRTLookupRegistry,
|
||||
TypeRTLookupWire,
|
||||
TypeRTRemoteContextApi,
|
||||
TypeRTRemoteContextMap,
|
||||
TypeRTRemoteContextNamespace,
|
||||
TypeRTRemoteScopeApi,
|
||||
TypeRTRemoteScopeMap,
|
||||
TypeRTRemoteScopeNamespace,
|
||||
TypeRTRemoteContribution,
|
||||
TypeRTRemoteMap,
|
||||
TypeRTRemoteNamespace,
|
||||
@@ -191,16 +191,16 @@ export function Remote<This extends object, Args extends unknown[], Result>(
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a decorator for a method resolved from one scoped Remote Context.
|
||||
* @param key - merge-declared Context key.
|
||||
* Create a decorator for a method resolved from one Remote Scope.
|
||||
* @param key - scope key declared through the Context map.
|
||||
* @param exportName - optional Remote export name; defaults to the method name.
|
||||
* @returns a standard method decorator that records only private module state.
|
||||
*/
|
||||
export function RemoteContext(
|
||||
export function RemoteScope(
|
||||
key: Extract<keyof TypeRTContextMap, string>,
|
||||
exportName?: string,
|
||||
): RemoteMethodDecorator {
|
||||
validateName('Context key', key)
|
||||
validateName('Scope key', key)
|
||||
if (exportName !== undefined) validateName('Remote export name', exportName)
|
||||
return function <This extends object, Args extends unknown[], Result>(
|
||||
_method: (this: This, ...args: Args) => Result,
|
||||
|
||||
@@ -40,7 +40,7 @@ export interface TypeRTContextMap {}
|
||||
export interface TypeRTRemoteMap {}
|
||||
|
||||
/** Merge-extensible scoped Remote method signatures generated for consumers. */
|
||||
export interface TypeRTRemoteContextMap {}
|
||||
export interface TypeRTRemoteScopeMap {}
|
||||
|
||||
/**
|
||||
* Resolve one direct Remote namespace from the generated flat endpoint map.
|
||||
@@ -57,24 +57,24 @@ export type TypeRTRemoteNamespace<Namespace extends string> = {
|
||||
* The calling Cordis Context supplies the concrete identity at runtime.
|
||||
* @template Namespace - wire namespace between the Context prefix and method.
|
||||
*/
|
||||
export type TypeRTRemoteContextNamespace<
|
||||
export type TypeRTRemoteScopeNamespace<
|
||||
Namespace extends string,
|
||||
ContextKey extends string = string,
|
||||
> = {
|
||||
[Endpoint in keyof TypeRTRemoteContextMap as Endpoint extends `${ContextKey}:${Namespace}/${infer Method}`
|
||||
[Endpoint in keyof TypeRTRemoteScopeMap as Endpoint extends `${ContextKey}:${Namespace}/${infer Method}`
|
||||
? Method
|
||||
: never]: TypeRTRemoteContextMap[Endpoint]
|
||||
: never]: TypeRTRemoteScopeMap[Endpoint]
|
||||
}
|
||||
|
||||
type TypeRTRemoteContextNamespaceKey<
|
||||
type TypeRTRemoteScopeNamespaceKey<
|
||||
ContextKey extends string,
|
||||
Endpoint = keyof TypeRTRemoteContextMap,
|
||||
Endpoint = keyof TypeRTRemoteScopeMap,
|
||||
> = Endpoint extends `${ContextKey}:${infer Namespace}/${string}` ? Namespace : never
|
||||
|
||||
/** Generated scoped Remote namespaces available to one Context kind. */
|
||||
export type TypeRTRemoteContextApi<ContextKey extends string> = {
|
||||
[Namespace in TypeRTRemoteContextNamespaceKey<ContextKey>]:
|
||||
TypeRTRemoteContextNamespace<Namespace, ContextKey>
|
||||
export type TypeRTRemoteScopeApi<ContextKey extends string> = {
|
||||
[Namespace in TypeRTRemoteScopeNamespaceKey<ContextKey>]:
|
||||
TypeRTRemoteScopeNamespace<Namespace, ContextKey>
|
||||
}
|
||||
|
||||
/** Merge-extensible direct namespace surface generated for Client Remote services. */
|
||||
@@ -227,7 +227,7 @@ export interface TypeRTLookupDefinition {
|
||||
readonly wireTypeSymbol: string
|
||||
}
|
||||
|
||||
/** Host resolver for one scoped Remote Context kind. */
|
||||
/** Host resolver for one scoped Remote kind. */
|
||||
export interface TypeRTHostContextProvider<Wire = unknown> {
|
||||
/** Wire field carrying the Context identity. */
|
||||
readonly wire: string
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Context } from 'cordis'
|
||||
import {
|
||||
GatewayService,
|
||||
Remote,
|
||||
RemoteContext,
|
||||
RemoteScope,
|
||||
remoteMethods,
|
||||
} from '@deepseek-ai/dsh-type-meta'
|
||||
|
||||
@@ -16,7 +16,7 @@ class Goals extends GatewayService {
|
||||
return value
|
||||
}
|
||||
|
||||
@RemoteContext('agent')
|
||||
@RemoteScope('agent')
|
||||
scoped(value: string): string {
|
||||
return value
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
bindTypeRTGateway,
|
||||
GatewayService,
|
||||
Remote,
|
||||
RemoteContext,
|
||||
RemoteScope,
|
||||
remoteMethods,
|
||||
type TypeRTContext,
|
||||
} from '@deepseek-ai/dsh-type-meta'
|
||||
@@ -29,7 +29,7 @@ describe('type-meta Remote declarations', () => {
|
||||
return value
|
||||
}
|
||||
|
||||
@RemoteContext('metaFixture')
|
||||
@RemoteScope('metaFixture')
|
||||
scoped(value: string): string {
|
||||
return value
|
||||
}
|
||||
@@ -84,7 +84,7 @@ describe('type-meta Remote declarations', () => {
|
||||
Reflect.get(Goals.prototype, 'create') as (this: Goals, ...args: unknown[]) => unknown,
|
||||
methodContext('create', initializers),
|
||||
)
|
||||
RemoteContext('metaFixture')(
|
||||
RemoteScope('metaFixture')(
|
||||
Reflect.get(Goals.prototype, 'scoped') as (this: Goals, ...args: unknown[]) => unknown,
|
||||
methodContext('scoped', initializers),
|
||||
)
|
||||
@@ -141,7 +141,7 @@ describe('type-meta Remote declarations', () => {
|
||||
Reflect.get(Service.prototype, 'run') as (this: Service, ...args: unknown[]) => unknown,
|
||||
methodContext('run', initializers),
|
||||
)
|
||||
RemoteContext('metaFixture', 'inspect')(
|
||||
RemoteScope('metaFixture', 'inspect')(
|
||||
Reflect.get(Service.prototype, 'scoped') as (this: Service, ...args: unknown[]) => unknown,
|
||||
methodContext('scoped', initializers),
|
||||
)
|
||||
@@ -166,8 +166,8 @@ describe('type-meta Remote declarations', () => {
|
||||
expect(() => Remote('bad name')).toThrow('export name')
|
||||
expect(() => Remote('.')).toThrow('export name')
|
||||
expect(() => Remote('..')).toThrow('export name')
|
||||
expect(() => RemoteContext('' as 'metaFixture')).toThrow('Context key')
|
||||
expect(() => RemoteContext('metaFixture', 'bad/name')).toThrow('export name')
|
||||
expect(() => RemoteScope('' as 'metaFixture')).toThrow('Scope key')
|
||||
expect(() => RemoteScope('metaFixture', 'bad/name')).toThrow('export name')
|
||||
|
||||
for (const context of [
|
||||
{ ...methodContext('run', []), private: true },
|
||||
@@ -195,7 +195,7 @@ describe('type-meta Remote declarations', () => {
|
||||
Reflect.get(Service.prototype, 'run'),
|
||||
methodContext('run', conflicting),
|
||||
)
|
||||
RemoteContext('metaFixture')(
|
||||
RemoteScope('metaFixture')(
|
||||
Reflect.get(Service.prototype, 'run'),
|
||||
methodContext('run', conflicting),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user