@deepseek-ai/dsh-typert-protocol 0.1.1-rc.2 → 0.1.2-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +128 -17
- package/README.zh.md +129 -18
- package/lib/index.js +98 -54
- package/lib/types/index.d.ts +18 -22
- package/lib/types/index.js +71 -48
- package/lib/types/remote-error.d.ts +31 -0
- package/lib/types/remote-error.js +44 -0
- package/lib/types/types.d.ts +126 -64
- package/package.json +5 -5
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/typert/protocol/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 6400cadad08c7cea634002daa15970fa5f7f6636
|
|
6
|
+
README.zh.md: 8efe1529e118ca2f740769b4113e7758923243a3
|
package/README.md
CHANGED
|
@@ -1,38 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "The shared Typert Remote protocol: decorators, wire descriptors, codecs, and provider contracts used by business packages, generated artifacts, the Host Gateway, and the Client API."
|
|
3
|
+
kind: "package-library"
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# @deepseek-ai/dsh-typert-protocol
|
|
2
7
|
|
|
3
8
|
English | [中文](README.zh.md)
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
With `dsh-typert-protocol`, business packages can expose Host methods to Remote clients: mark a method with `@Remote` (or `@RemoteScope` for scoped receivers), bind the service to a wire namespace, and associate Host objects and scoped Contexts with wire identities through the merge-extensible protocol maps. Generated artifacts, the Host Gateway, and the Client API consume the same invocation descriptors, codecs, and provider contracts, so one declaration set stays in sync across every face. The package registers no Cordis service and runs no TypeScript analysis; it declares types and decorator markers only.
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Use this package](#use-this-package)
|
|
17
|
+
- [Understand the implementation](#understand-the-implementation)
|
|
18
|
+
- [Further Exploration](#further-exploration)
|
|
19
|
+
- [Model Experience](#model-experience)
|
|
20
|
+
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
|
21
|
+
- [Dev Note](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## Use this package
|
|
27
|
+
|
|
28
|
+
This package is for business-package and assembly maintainers who expose Host capabilities to Remote clients. It is a declarations library: mark methods, bind services, and let the generated pipeline and the Gateway do the rest.
|
|
29
|
+
|
|
30
|
+
### Exposing a Host method
|
|
31
|
+
|
|
32
|
+
A business package marks a public instance method with `@Remote` (or `@RemoteScope(key)` when the receiver comes from a scoped Context), and the owning service either extends `TypertRemoteService` or declares a `typertRemote` binding through `bindTypertRemote()`:
|
|
33
|
+
|
|
34
|
+
```text
|
|
35
|
+
import { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'
|
|
36
|
+
|
|
37
|
+
export class GoalService extends TypertRemoteService {
|
|
38
|
+
@Remote
|
|
39
|
+
async create(agentId: string, objective: string): Promise<GoalResult> {
|
|
40
|
+
...
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Generation turns the method into a wire endpoint under the service's namespace; Clients call it as a typed method through `ctx.remote` (see the [API Gateway reference](../../../docs/api-gateway.md)). A method opts into cooperative cancellation by declaring `signal: AbortSignal` as its final parameter — the signal is injected, never a JSON parameter or lookup field.
|
|
46
|
+
|
|
47
|
+
### Associating Host objects and Contexts with wire identities
|
|
48
|
+
|
|
49
|
+
Complex Host objects cannot cross the wire directly. A business package declares the association through the merge-extensible `TypertLookupMap` and `TypertContextMap`. Host and Client Context adapters both map `Context` to a wire identity and that identity back to `Context`; the Host adapter also owns the stable wire declaration. Host composition may override its synchronous or asynchronous resolver. A resolver that refuses on policy grounds throws `RemoteError` with its own code, which reaches the caller unchanged.
|
|
50
|
+
|
|
51
|
+
### Reporting and reading a Remote failure
|
|
52
|
+
|
|
53
|
+
One class carries every Remote failure: `RemoteError`, holding a stable `<domain>/<reason>` code and the details typed for that code. This package declares the universal carrier codes (`gateway/bad-request`, `gateway/cancelled`, `gateway/internal`) and owns `RemoteErrorDetailsMap`, the merge-extensible table every other package extends beside its own throwing code:
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
declare module '@deepseek-ai/dsh-typert-protocol' {
|
|
57
|
+
interface RemoteErrorDetailsMap {
|
|
58
|
+
'goal/not-found': { readonly goalId: string }
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
throw new RemoteError('goal/not-found', `goal "${id}" does not exist`, { goalId: id })
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
An owner throws at the failure point; no package writes an error-class family or an exit-mapping function. A caller discriminates by `code` — never by `instanceof` — and a `code` branch narrows `details` with no cast, because `RemoteFailure` is the code-discriminated union of `RemoteError` instances. Infrastructure that must recognize a failure carried across a module or realm copy of the class calls `remoteErrorOf(value)`, which reads a structural marker instead of the prototype chain.
|
|
65
|
+
|
|
66
|
+
### Receiving forwarded Host events on the Client
|
|
67
|
+
|
|
68
|
+
The Host assembly extends `TypertRemoteEventSelection` with the Cordis events it forwards to consumers, which narrows the `ctx.remote.$on` key set. `TypertForwardableEvent` accepts unscoped `void` notifications and scoped async waterfalls whose final `next()` callback returns the event's result type. `TypertClientEventListener` derives the Client listener from that same `Events` member while preserving signals, optional and readonly fields, arrays, callbacks, and result types. `TypertClientRemote` exposes only `$mount()` and `$on()`; event transport remains private to Gateway.
|
|
6
69
|
|
|
7
|
-
|
|
70
|
+
-----
|
|
8
71
|
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
- `TypertRemoteService` binds the Cordis key passed to `super(ctx, serviceKey, options?)` to the same default wire namespace.
|
|
12
|
-
- `bindTypertRemote(this, serviceKey, options?)` provides the same visible, frozen binding for a Service that cannot inherit from `TypertRemoteService`.
|
|
13
|
-
- `remoteMethods(service)` returns a detached declaration-order snapshot used by the Gateway's SRC fallback.
|
|
72
|
+
<a id="understand-the-implementation"></a>
|
|
73
|
+
## Understand the implementation
|
|
14
74
|
|
|
15
|
-
|
|
75
|
+
<details>
|
|
76
|
+
<summary>Implementation internals — click to expand</summary>
|
|
16
77
|
|
|
17
|
-
|
|
78
|
+
This section explains how the declarations stay compiler-independent and where each contract is enforced; the programming model is covered in [Use this package](#use-this-package).
|
|
18
79
|
|
|
19
|
-
|
|
80
|
+
### Design concept
|
|
20
81
|
|
|
21
|
-
|
|
82
|
+
The package keeps strict reflection in the compiler: decorator initializers retain minimal markers in a versioned descriptor on the Service prototype. The descriptor uses a stable string property name, so another installed copy of the protocol package can read the same markers. Full parameter, result, lookup, and schema reflection is the Typert build pipeline's job, delivered through `InvocationDescriptor`.
|
|
22
83
|
|
|
23
|
-
|
|
84
|
+
### Remote markers
|
|
24
85
|
|
|
25
|
-
|
|
86
|
+
`@Remote` and `@RemoteScope` schedule an initializer that appends the method name, an optional export name, and the invocation mode to the prototype descriptor; `remoteMethods(service)` validates its version and returns a detached declaration-order snapshot that the Gateway's source-mode fallback reads. Markers require public, non-static instance methods with string names, and conflicting markers on one method are rejected.
|
|
26
87
|
|
|
88
|
+
### Protocol maps and descriptors
|
|
89
|
+
|
|
90
|
+
The merge-extensible protocol maps keep static associations in the type system, while runtime providers register resolution with `ctx.typert`; the map names and shapes live in [`src/types.ts`](src/types.ts). `InvocationDescriptor` is the shared runtime form consumed by the registry, the Gateway, and the Client Remote, covering direct and Context receivers, JSON and lookup parameters, scope projections, cancellation, and result codecs.
|
|
91
|
+
|
|
92
|
+
### Wire identity grammar
|
|
93
|
+
|
|
94
|
+
Every namespace, method, lookup, and Context segment must satisfy `isTypertRemoteSegment()`, so generated names cross the shared RPC carrier unchanged. Strict codecs carry generated schemas; `src-json` codecs identify the weaker source-launch path.
|
|
95
|
+
|
|
96
|
+
### Source map
|
|
97
|
+
|
|
98
|
+
| File | Role |
|
|
99
|
+
|---|---|
|
|
100
|
+
| [`src/index.ts`](src/index.ts) | Decorators, Gateway bindings, `remoteMethods`, segment validation |
|
|
101
|
+
| [`src/remote-error.ts`](src/remote-error.ts) | `RemoteError` and the structural `remoteErrorOf` recognizer |
|
|
102
|
+
| [`src/types.ts`](src/types.ts) | Protocol maps, `RemoteErrorDetailsMap`, `RemoteResult`, `InvocationDescriptor`, codecs, provider contracts, registry interfaces, `TypertClientRemote` |
|
|
103
|
+
| [`src/invariant.ts`](src/invariant.ts) | Invariant companion |
|
|
104
|
+
|
|
105
|
+
</details>
|
|
106
|
+
|
|
107
|
+
-----
|
|
108
|
+
|
|
109
|
+
<a id="further-exploration"></a>
|
|
110
|
+
## Further Exploration
|
|
111
|
+
|
|
112
|
+
Read these pages when the package-level contract is not enough; they move from the declarations to the runtime and the call path.
|
|
113
|
+
|
|
114
|
+
- [API Gateway reference](../../../docs/api-gateway.md) — how the declarations become running Host-to-Client calls.
|
|
115
|
+
- [Typert subsystem reference](../../../docs/subsystems/typert.md) — the literal public contracts recorded from protocol and Gateway types.
|
|
116
|
+
- [Typert registry](../registry/README.md) — where descriptors and providers are stored at runtime.
|
|
117
|
+
- [Typert generator](../generator/README.md) — what generates the consumer-side declarations and descriptors.
|
|
118
|
+
- [Remote-call Agent Note](../../../.agents/notes/implemented/architecture/2026-08-02-typert-remote-method-calls.md) — the architecture and transport decisions behind Remote calls.
|
|
119
|
+
|
|
120
|
+
-----
|
|
121
|
+
|
|
122
|
+
<a id="model-experience"></a>
|
|
27
123
|
## Model Experience
|
|
28
124
|
|
|
29
|
-
None, as
|
|
125
|
+
None, as compiler-independent Remote protocol declarations register nothing model-facing.
|
|
30
126
|
|
|
31
127
|
#### KV Cache effect
|
|
32
128
|
|
|
33
|
-
No direct effect.
|
|
129
|
+
No direct effect; the declared contracts reach a request only when an assembly places them in one.
|
|
34
130
|
|
|
35
131
|
## Known Limitations and Deferred Work
|
|
36
132
|
|
|
37
|
-
-
|
|
38
|
-
|
|
133
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
These limits define what the declarations can represent; they are current package constraints, not a task backlog.
|
|
137
|
+
|
|
138
|
+
- **Decorator markers are minimal** — markers contain only the method name and the direct or Context invocation mode; parameter, result, lookup, and schema reflection require the Typert build pipeline.
|
|
139
|
+
- **Remote signatures are restricted** — decorators accept only public, non-static instance methods with string names, and source-mode execution cannot represent overloaded, destructured, defaulted, or rest-parameter signatures.
|
|
140
|
+
|
|
141
|
+
<a id="dev-note"></a>
|
|
142
|
+
### Dev Note
|
|
143
|
+
|
|
144
|
+
<details>
|
|
145
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
146
|
+
|
|
147
|
+
None.
|
|
148
|
+
|
|
149
|
+
</details>
|
package/README.zh.md
CHANGED
|
@@ -1,38 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "共享的 Typert Remote 协议:业务包、生成产物、Host Gateway 与 Client API 使用的装饰器、wire 描述符、编解码器与提供方约定。"
|
|
3
|
+
kind: "package-library"
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# @deepseek-ai/dsh-typert-protocol
|
|
2
7
|
|
|
3
8
|
[English](README.md) | 中文
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## 概述
|
|
11
|
+
|
|
12
|
+
借助 `dsh-typert-protocol`,业务包可以向 Remote 客户端暴露 Host 方法:用 `@Remote`(作用域接收者用 `@RemoteScope`)标记方法,把服务绑定到 wire 命名空间,并通过可合并扩展的协议映射把 Host 对象与作用域 Context 关联到 wire identity。生成产物、Host Gateway 与 Client API 消费同一套调用描述符、编解码器与提供方约定,因此一套声明在每个 face 上保持一致。本包不注册任何 Cordis 服务,也不运行 TypeScript 分析;它只声明类型与装饰器标记。
|
|
13
|
+
|
|
14
|
+
## 目录
|
|
15
|
+
|
|
16
|
+
- [使用本包](#use-this-package)
|
|
17
|
+
- [理解实现](#understand-the-implementation)
|
|
18
|
+
- [进一步探索](#further-exploration)
|
|
19
|
+
- [模型体验](#model-experience)
|
|
20
|
+
- [已知限制与延期工作](#known-limitations-and-deferred-work)
|
|
21
|
+
- [开发备注](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## 使用本包
|
|
27
|
+
|
|
28
|
+
本包供向 Remote 客户端暴露 Host 能力的业务包与装配维护者使用。它是一个声明库:标记方法、绑定服务,其余交给生成的流水线与 Gateway。
|
|
29
|
+
|
|
30
|
+
### 暴露 Host 方法
|
|
31
|
+
|
|
32
|
+
业务包用 `@Remote`(当接收者来自作用域 Context 时用 `@RemoteScope(key)`)标记一个公开实例方法,所属服务要么继承 `TypertRemoteService`,要么通过 `bindTypertRemote()` 声明 `typertRemote` 绑定:
|
|
33
|
+
|
|
34
|
+
```text
|
|
35
|
+
import { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'
|
|
36
|
+
|
|
37
|
+
export class GoalService extends TypertRemoteService {
|
|
38
|
+
@Remote
|
|
39
|
+
async create(agentId: string, objective: string): Promise<GoalResult> {
|
|
40
|
+
...
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
生成会把方法变为服务命名空间下的 wire 端点;Client 通过 `ctx.remote` 以类型化方法调用它(见 [API Gateway 参考](../../../docs/api-gateway.zh.md))。方法把 `signal: AbortSignal` 声明为最后一个参数即可选择协作式取消——该信号是注入的,绝不会成为 JSON 参数或查找字段。
|
|
46
|
+
|
|
47
|
+
### 把 Host 对象与 Context 关联到 wire identity
|
|
48
|
+
|
|
49
|
+
复杂的 Host 对象不能直接跨 wire 传输。业务包通过可合并扩展的 `TypertLookupMap` 与 `TypertContextMap` 声明关联。Host 与 Client Context adapter 都把 `Context` 映射为 wire identity,也把该 identity 映射回 `Context`;Host adapter 还拥有稳定 wire 声明。Host 组合可以覆盖其同步或异步 resolver。因策略而拒绝的 resolver 抛出带自有码的 `RemoteError`,该码原样到达调用方。
|
|
50
|
+
|
|
51
|
+
### 报告与读取 Remote 失败
|
|
52
|
+
|
|
53
|
+
所有 Remote 失败都由一个类承载:`RemoteError`,携带稳定的 `<domain>/<reason>` 码,以及按该码定型的 details。本包声明通用载体码(`gateway/bad-request`、`gateway/cancelled`、`gateway/internal`),并拥有 `RemoteErrorDetailsMap`——可合并扩展的码表,其他每个包都在自己的抛出点旁扩展它:
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
declare module '@deepseek-ai/dsh-typert-protocol' {
|
|
57
|
+
interface RemoteErrorDetailsMap {
|
|
58
|
+
'goal/not-found': { readonly goalId: string }
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
throw new RemoteError('goal/not-found', `goal "${id}" does not exist`, { goalId: id })
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
拥有方在失败点直接抛出;没有任何包再写错误类家族或出口映射函数。调用方按 `code` 判别——绝不用 `instanceof`——且 `code` 分支无需 cast 即收窄 `details`,因为 `RemoteFailure` 就是 `RemoteError` 实例按码判别的 union。需要识别跨模块或跨 realm 类副本传来的失败时,基础设施调用 `remoteErrorOf(value)`,它读结构标记而不是原型链。
|
|
65
|
+
|
|
66
|
+
### 在 Client 侧接收转发的 Host 事件
|
|
67
|
+
|
|
68
|
+
Host 装配以转发给消费端的 Cordis 事件扩展 `TypertRemoteEventSelection`,从而收窄 `ctx.remote.$on` 的键集。`TypertForwardableEvent` 接受无作用域且返回 `void` 的通知,以及最后一个 `next()` 回调返回事件结果类型的异步作用域 waterfall。`TypertClientEventListener` 从同一条 `Events` 成员派生 Client listener,并保留 signal、可选和只读字段、数组、回调与结果类型。`TypertClientRemote` 只公开 `$mount()` 与 `$on()`;事件传输仍由 Gateway 私有持有。
|
|
6
69
|
|
|
7
|
-
|
|
70
|
+
-----
|
|
8
71
|
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
- `TypertRemoteService` 将传给 `super(ctx, serviceKey, options?)` 的 Cordis 键绑定到同一默认协议命名空间。
|
|
12
|
-
- `bindTypertRemote(this, serviceKey, options?)` 为无法继承 `TypertRemoteService` 的服务提供同样可见且冻结的绑定。
|
|
13
|
-
- `remoteMethods(service)` 返回按声明顺序排列、与内部状态分离的快照,供 Gateway 的 SRC 回退路径使用。
|
|
72
|
+
<a id="understand-the-implementation"></a>
|
|
73
|
+
## 理解实现
|
|
14
74
|
|
|
15
|
-
|
|
75
|
+
<details>
|
|
76
|
+
<summary>实现细节——点击展开</summary>
|
|
16
77
|
|
|
17
|
-
|
|
78
|
+
本节解释声明如何保持与编译器无关,以及每个约定在哪里执行;编程模型已在[使用本包](#use-this-package)中说明。
|
|
18
79
|
|
|
19
|
-
|
|
80
|
+
### 设计理念
|
|
20
81
|
|
|
21
|
-
|
|
82
|
+
本包把严格反射留在编译器中:装饰器初始化器把最小标记保存在 Service 原型上的带版本描述符中。描述符使用稳定的字符串属性名,因此协议包的另一个已安装副本也能读取同一组标记。完整的参数、结果、查找与 schema 反射是 Typert 构建流水线的职责,通过 `InvocationDescriptor` 交付。
|
|
22
83
|
|
|
23
|
-
|
|
84
|
+
### Remote 标记
|
|
24
85
|
|
|
25
|
-
|
|
86
|
+
`@Remote` 与 `@RemoteScope` 调度一个初始化器,把方法名、可选导出名与调用模式追加到原型描述符;`remoteMethods(service)` 校验其版本,并返回与已存描述符分离、按声明顺序排列的快照,供 Gateway 的源码模式回退读取。标记要求公开、非静态、具名字符串的实例方法,同一方法上的冲突标记会被拒绝。
|
|
26
87
|
|
|
88
|
+
### 协议映射与描述符
|
|
89
|
+
|
|
90
|
+
可合并扩展的协议映射在类型系统中保留静态关联,运行时提供方则向 `ctx.typert` 注册解析;映射的名称与形状见 [`src/types.ts`](src/types.ts)。`InvocationDescriptor` 是注册表、Gateway 与 Client Remote 共同消费的共享运行时形式,涵盖直接与 Context 接收者、JSON 与查找参数、作用域投影、取消与结果编解码器。
|
|
91
|
+
|
|
92
|
+
### Wire 标识文法
|
|
93
|
+
|
|
94
|
+
每个命名空间、方法、查找与 Context 段都必须满足 `isTypertRemoteSegment()`,生成的名字才能原样跨共享 RPC 载体传输。严格编解码器携带生成的 schema;`src-json` 编解码器标识约束更弱的源码启动路径。
|
|
95
|
+
|
|
96
|
+
### 源码地图
|
|
97
|
+
|
|
98
|
+
| 文件 | 职责 |
|
|
99
|
+
|---|---|
|
|
100
|
+
| [`src/index.ts`](src/index.ts) | 装饰器、Gateway 绑定、`remoteMethods`、段校验 |
|
|
101
|
+
| [`src/remote-error.ts`](src/remote-error.ts) | `RemoteError` 与结构式识别函数 `remoteErrorOf` |
|
|
102
|
+
| [`src/types.ts`](src/types.ts) | 协议映射、`RemoteErrorDetailsMap`、`RemoteResult`、`InvocationDescriptor`、编解码器、提供方约定、注册表接口、`TypertClientRemote` |
|
|
103
|
+
| [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件 |
|
|
104
|
+
|
|
105
|
+
</details>
|
|
106
|
+
|
|
107
|
+
-----
|
|
108
|
+
|
|
109
|
+
<a id="further-exploration"></a>
|
|
110
|
+
## 进一步探索
|
|
111
|
+
|
|
112
|
+
当包级约定不够用时阅读以下页面;它们从声明逐步进入运行时与调用路径。
|
|
113
|
+
|
|
114
|
+
- [API Gateway 参考](../../../docs/api-gateway.zh.md)——声明如何成为实际的 Host 到 Client 调用。
|
|
115
|
+
- [Typert 子系统参考](../../../docs/subsystems/typert.zh.md)——从协议与 Gateway 类型记录的字面公共约定。
|
|
116
|
+
- [Typert 注册表](../registry/README.zh.md)——描述符与提供方在运行时存放的位置。
|
|
117
|
+
- [Typert 生成器](../generator/README.zh.md)——生成消费方声明与描述符的包。
|
|
118
|
+
- [Remote 调用 Agent Note](../../../.agents/notes/implemented/architecture/2026-08-02-typert-remote-method-calls.zh.md)——Remote 调用背后的架构与传输决策。
|
|
119
|
+
|
|
120
|
+
-----
|
|
121
|
+
|
|
122
|
+
<a id="model-experience"></a>
|
|
27
123
|
## 模型体验
|
|
28
124
|
|
|
29
|
-
|
|
125
|
+
无,因为与编译器无关的 Remote 协议声明不注册任何面向模型的内容。
|
|
30
126
|
|
|
31
127
|
#### KV Cache 影响
|
|
32
128
|
|
|
33
|
-
|
|
129
|
+
无直接影响;声明的约定只有在装配将其放入请求时才会触及请求。
|
|
130
|
+
|
|
131
|
+
## 已知限制与延期工作
|
|
132
|
+
|
|
133
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
这些限制说明声明能表示什么;它们是当前包约束,不是任务积压。
|
|
137
|
+
|
|
138
|
+
- **装饰器标记是最小化的**——标记只包含方法名与直接调用或 Context 调用模式;参数、结果、查找与 schema 反射需要 Typert 构建流水线。
|
|
139
|
+
- **Remote 签名受限**——装饰器只接受具有字符串名称的公开、非静态实例方法,源码模式执行无法表示重载、解构、默认参数或剩余参数签名。
|
|
140
|
+
|
|
141
|
+
<a id="dev-note"></a>
|
|
142
|
+
### 开发备注
|
|
143
|
+
|
|
144
|
+
<details>
|
|
145
|
+
<summary>维护者的工作上下文——点击展开</summary>
|
|
34
146
|
|
|
35
|
-
|
|
147
|
+
无。
|
|
36
148
|
|
|
37
|
-
|
|
38
|
-
- Remote 装饰器只接受具有字符串名称的公开、非静态实例方法。SRC 执行无法表示重载签名,以及包含解构参数、默认参数或剩余参数的方法签名。
|
|
149
|
+
</details>
|
package/lib/index.js
CHANGED
|
@@ -1,8 +1,47 @@
|
|
|
1
1
|
import { Service } from "@deepseek-ai/cordis";
|
|
2
|
+
//#region lib/types/remote-error.js
|
|
3
|
+
/** The one Remote failure class shared by owners, the Gateway, and consumers. */
|
|
4
|
+
/**
|
|
5
|
+
* One Remote call failure: a real Error carrying its stable code and typed
|
|
6
|
+
* details. Owners throw it at the failure point; the Host Gateway encodes it
|
|
7
|
+
* onto the wire unchanged; the Client face rebuilds an instance for the
|
|
8
|
+
* `RemoteResult` error branch, so `throw result.error` keeps throw semantics.
|
|
9
|
+
* Discrimination is always by `code`, never by instanceof.
|
|
10
|
+
*/
|
|
11
|
+
var RemoteError = class extends Error {
|
|
12
|
+
code;
|
|
13
|
+
details;
|
|
14
|
+
/** Structural marker: cross-realm/bundle identification never uses instanceof. */
|
|
15
|
+
isDSHRemoteError = true;
|
|
16
|
+
/**
|
|
17
|
+
* @param code - stable failure code declared in {@link RemoteErrorDetailsMap}.
|
|
18
|
+
* @param message - human diagnostic carried across the wire.
|
|
19
|
+
* @param details - structured payload typed by the code.
|
|
20
|
+
* @param options - standard Error options (`cause` survives in-process only).
|
|
21
|
+
*/
|
|
22
|
+
constructor(code, message, details, options) {
|
|
23
|
+
super(message, options);
|
|
24
|
+
this.code = code;
|
|
25
|
+
this.details = details;
|
|
26
|
+
this.name = "RemoteError";
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Structurally identify a RemoteError thrown across module or realm copies of
|
|
31
|
+
* this class. Mechanism-internal: the Gateway and test assertions use it;
|
|
32
|
+
* business code receives typed failures and never needs it.
|
|
33
|
+
* @param value - a caught value.
|
|
34
|
+
* @returns the failure when the marker matches, otherwise undefined.
|
|
35
|
+
*/
|
|
36
|
+
function remoteErrorOf(value) {
|
|
37
|
+
if (typeof value === "object" && value !== null && value.isDSHRemoteError === true && typeof value.code === "string") return value;
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
2
40
|
//#region lib/types/index.js
|
|
3
41
|
/**
|
|
4
|
-
* Remote decorators and explicit Gateway bindings backed
|
|
5
|
-
*
|
|
42
|
+
* Remote decorators and explicit Gateway bindings backed by versioned
|
|
43
|
+
* descriptors carried on decorated class prototypes. Strict reflection
|
|
44
|
+
* remains a Typert compiler responsibility.
|
|
6
45
|
* @module @deepseek-ai/dsh-typert-protocol
|
|
7
46
|
*/
|
|
8
47
|
const TYPERT_REMOTE_SEGMENT_PATTERN = /^[A-Za-z0-9_$.-]+$/;
|
|
@@ -14,24 +53,7 @@ const TYPERT_REMOTE_SEGMENT_PATTERN = /^[A-Za-z0-9_$.-]+$/;
|
|
|
14
53
|
function isTypertRemoteSegment(value) {
|
|
15
54
|
return value !== "." && value !== ".." && TYPERT_REMOTE_SEGMENT_PATTERN.test(value);
|
|
16
55
|
}
|
|
17
|
-
|
|
18
|
-
* A lookup policy rejection whose typed payload belongs to the active boundary adapter.
|
|
19
|
-
* Gateway adapters preserve this payload instead of collapsing it into an infrastructure failure.
|
|
20
|
-
*/
|
|
21
|
-
var TypertLookupFailure = class extends Error {
|
|
22
|
-
/** Adapter-owned failure returned to the caller. */
|
|
23
|
-
failure;
|
|
24
|
-
/**
|
|
25
|
-
* Wrap one adapter failure without exposing the rejected identity.
|
|
26
|
-
* @param failure - typed failure owned by the active boundary adapter.
|
|
27
|
-
*/
|
|
28
|
-
constructor(failure) {
|
|
29
|
-
super("Typert lookup policy rejected the requested identity");
|
|
30
|
-
this.name = "TypertLookupFailure";
|
|
31
|
-
this.failure = failure;
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
const markers = /* @__PURE__ */ new WeakMap();
|
|
56
|
+
const REMOTE_METHOD_DESCRIPTOR = "@deepseek-ai/dsh-typert-protocol/remote-methods";
|
|
35
57
|
/**
|
|
36
58
|
* Bind one visible Service field to a Cordis key and Remote namespace.
|
|
37
59
|
* @param service - owning Service instance, normally `this`.
|
|
@@ -64,77 +86,99 @@ var TypertRemoteService = class extends Service {
|
|
|
64
86
|
this.typertRemote = bindTypertRemote(this, this.name, options);
|
|
65
87
|
}
|
|
66
88
|
};
|
|
67
|
-
function Remote(
|
|
68
|
-
if (typeof
|
|
69
|
-
validateName("Remote export name",
|
|
70
|
-
return
|
|
71
|
-
|
|
72
|
-
|
|
89
|
+
function Remote(methodExportOrOptions, context) {
|
|
90
|
+
if (typeof methodExportOrOptions === "string") {
|
|
91
|
+
validateName("Remote export name", methodExportOrOptions);
|
|
92
|
+
return remoteDecorator({ kind: "direct" }, void 0, methodExportOrOptions);
|
|
93
|
+
}
|
|
94
|
+
if (typeof methodExportOrOptions === "object") {
|
|
95
|
+
if (remoteOptionMode(methodExportOrOptions) !== "stream" || Reflect.ownKeys(methodExportOrOptions).length !== 1) throw new TypeError("typert-protocol: Remote options must contain exactly mode: \"stream\"");
|
|
96
|
+
return remoteDecorator({ kind: "direct" }, "stream");
|
|
73
97
|
}
|
|
74
98
|
if (context === void 0) throw new TypeError("typert-protocol: Remote decorator context is missing");
|
|
75
99
|
addMarkerInitializer(context, { kind: "direct" });
|
|
76
100
|
}
|
|
101
|
+
function remoteOptionMode(options) {
|
|
102
|
+
return Reflect.get(options, "mode");
|
|
103
|
+
}
|
|
104
|
+
function remoteDecorator(invocation, mode, exportName) {
|
|
105
|
+
return function(_method, context) {
|
|
106
|
+
addMarkerInitializer(context, invocation, mode, exportName);
|
|
107
|
+
};
|
|
108
|
+
}
|
|
77
109
|
/**
|
|
78
110
|
* Create a decorator for a method resolved from one Remote Scope.
|
|
79
111
|
* @param key - scope key declared through the Context map.
|
|
80
112
|
* @param exportName - optional Remote export name; defaults to the method name.
|
|
81
|
-
* @returns a standard method decorator that records
|
|
113
|
+
* @returns a standard method decorator that records a versioned prototype descriptor.
|
|
82
114
|
*/
|
|
83
115
|
function RemoteScope(key, exportName) {
|
|
84
116
|
validateName("Scope key", key);
|
|
85
117
|
if (exportName !== void 0) validateName("Remote export name", exportName);
|
|
86
|
-
return
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}, exportName);
|
|
91
|
-
};
|
|
118
|
+
return remoteDecorator({
|
|
119
|
+
kind: "context",
|
|
120
|
+
context: key
|
|
121
|
+
}, void 0, exportName);
|
|
92
122
|
}
|
|
93
123
|
/**
|
|
94
|
-
* Read Remote markers attached to a live Service
|
|
95
|
-
* The returned snapshot cannot mutate the
|
|
124
|
+
* Read Remote markers attached to a live Service's class prototype.
|
|
125
|
+
* The returned snapshot cannot mutate the stored descriptor.
|
|
96
126
|
* @param service - live Service instance.
|
|
97
127
|
* @returns markers in class declaration order.
|
|
98
128
|
*/
|
|
99
129
|
function remoteMethods(service) {
|
|
100
130
|
const prototype = Object.getPrototypeOf(service);
|
|
101
131
|
if (prototype === null) return [];
|
|
102
|
-
return
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
132
|
+
return (readRemoteMethodDescriptor(prototype)?.methods ?? []).map((marker) => ({ ...marker }));
|
|
133
|
+
}
|
|
134
|
+
function readRemoteMethodDescriptor(prototype) {
|
|
135
|
+
const property = Object.getOwnPropertyDescriptor(prototype, REMOTE_METHOD_DESCRIPTOR);
|
|
136
|
+
if (property === void 0) return void 0;
|
|
137
|
+
const descriptor = property.value;
|
|
138
|
+
if (descriptor === null || typeof descriptor !== "object") throw new TypeError("typert-protocol: Remote method descriptor must be an object");
|
|
139
|
+
const version = Reflect.get(descriptor, "version");
|
|
140
|
+
if (version !== 1) throw new TypeError(`typert-protocol: unsupported Remote method descriptor version ${String(version)}`);
|
|
141
|
+
const methods = Reflect.get(descriptor, "methods");
|
|
142
|
+
if (!Array.isArray(methods)) throw new TypeError("typert-protocol: Remote method descriptor methods must be an array");
|
|
143
|
+
return descriptor;
|
|
106
144
|
}
|
|
107
|
-
function addMarkerInitializer(context, invocation, exportName) {
|
|
145
|
+
function addMarkerInitializer(context, invocation, mode, exportName) {
|
|
108
146
|
if (context.private || context.static || typeof context.name !== "string") throw new TypeError("typert-protocol: Remote decorators require a public instance method with a string name");
|
|
109
147
|
const method = context.name;
|
|
110
148
|
context.addInitializer(function() {
|
|
111
149
|
const prototype = Object.getPrototypeOf(this);
|
|
112
150
|
if (prototype === null) throw new TypeError(`typert-protocol: cannot mark Remote method "${method}" on an object without a prototype`);
|
|
113
|
-
mark(prototype, method, invocation, exportName);
|
|
151
|
+
mark(prototype, method, invocation, mode, exportName);
|
|
114
152
|
});
|
|
115
153
|
}
|
|
116
|
-
function mark(prototype, method, invocation, exportName) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
markers.set(prototype, table);
|
|
121
|
-
}
|
|
122
|
-
const marker = {
|
|
154
|
+
function mark(prototype, method, invocation, mode, exportName) {
|
|
155
|
+
const descriptor = readRemoteMethodDescriptor(prototype);
|
|
156
|
+
const marker = Object.freeze({
|
|
157
|
+
method,
|
|
123
158
|
...exportName === void 0 || exportName === method ? {} : { exportName },
|
|
159
|
+
...mode === void 0 ? {} : { mode },
|
|
124
160
|
invocation: Object.freeze(invocation)
|
|
125
|
-
};
|
|
126
|
-
const current =
|
|
161
|
+
});
|
|
162
|
+
const current = descriptor?.methods.find((candidate) => candidate.method === method);
|
|
127
163
|
if (current !== void 0) {
|
|
128
|
-
if (current.exportName === marker.exportName && sameInvocation(current.invocation, invocation)) return;
|
|
164
|
+
if (current.exportName === marker.exportName && current.mode === marker.mode && sameInvocation(current.invocation, invocation)) return;
|
|
129
165
|
throw new Error(`typert-protocol: Remote method "${method}" has conflicting invocation markers`);
|
|
130
166
|
}
|
|
131
|
-
|
|
167
|
+
Object.defineProperty(prototype, REMOTE_METHOD_DESCRIPTOR, {
|
|
168
|
+
configurable: true,
|
|
169
|
+
value: Object.freeze({
|
|
170
|
+
version: 1,
|
|
171
|
+
methods: Object.freeze([...descriptor?.methods ?? [], marker])
|
|
172
|
+
})
|
|
173
|
+
});
|
|
132
174
|
}
|
|
133
175
|
function sameInvocation(left, right) {
|
|
134
|
-
|
|
176
|
+
if (left.kind === "direct") return right.kind === "direct";
|
|
177
|
+
if (right.kind === "direct") return false;
|
|
178
|
+
return left.context === right.context;
|
|
135
179
|
}
|
|
136
180
|
function validateName(subject, value) {
|
|
137
181
|
if (!isTypertRemoteSegment(value)) throw new TypeError(`typert-protocol: ${subject} must contain only RPC endpoint segment characters`);
|
|
138
182
|
}
|
|
139
183
|
//#endregion
|
|
140
|
-
export { Remote,
|
|
184
|
+
export { Remote, RemoteError, RemoteScope, TypertRemoteService, bindTypertRemote, isTypertRemoteSegment, remoteErrorOf, remoteMethods };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,30 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Remote decorators and explicit Gateway bindings backed
|
|
3
|
-
*
|
|
2
|
+
* Remote decorators and explicit Gateway bindings backed by versioned
|
|
3
|
+
* descriptors carried on decorated class prototypes. Strict reflection
|
|
4
|
+
* remains a Typert compiler responsibility.
|
|
4
5
|
* @module @deepseek-ai/dsh-typert-protocol
|
|
5
6
|
*/
|
|
6
7
|
import { Service, type Context } from '@deepseek-ai/cordis';
|
|
7
8
|
import type { TypertContextMap } from './types.ts';
|
|
9
|
+
export { RemoteError, remoteErrorOf } from './remote-error.ts';
|
|
8
10
|
/**
|
|
9
11
|
* Test one generated Remote name against the Connection endpoint grammar.
|
|
10
12
|
* @param value - namespace, method, lookup, or Context segment.
|
|
11
13
|
* @returns whether the value can cross the shared RPC carrier unchanged.
|
|
12
14
|
*/
|
|
13
15
|
export declare function isTypertRemoteSegment(value: string): boolean;
|
|
14
|
-
|
|
15
|
-
* A lookup policy rejection whose typed payload belongs to the active boundary adapter.
|
|
16
|
-
* Gateway adapters preserve this payload instead of collapsing it into an infrastructure failure.
|
|
17
|
-
*/
|
|
18
|
-
export declare class TypertLookupFailure<Failure = unknown> extends Error {
|
|
19
|
-
/** Adapter-owned failure returned to the caller. */
|
|
20
|
-
readonly failure: Failure;
|
|
21
|
-
/**
|
|
22
|
-
* Wrap one adapter failure without exposing the rejected identity.
|
|
23
|
-
* @param failure - typed failure owned by the active boundary adapter.
|
|
24
|
-
*/
|
|
25
|
-
constructor(failure: Failure);
|
|
26
|
-
}
|
|
27
|
-
export type { InvocationDescriptor, InvocationParameterDescriptor, InvocationSourceLocation, RemoteFailure, RemoteResult, TypertClientRemote, TypertClientContextBinder, TypertCodec, TypertContext, TypertContextMap, TypertContextRegistry, TypertContextWire, TypertDisposer, TypertForwardableEvent, TypertHostContextProvider, TypertHostContextResolver, TypertLocalRegistry, TypertLookup, TypertLookupDefinition, TypertLookupHost, TypertLookupMap, TypertLookupProvider, TypertLookupResolver, TypertLookupRegistry, TypertLookupWire, TypertRemoteScopeApi, TypertRemoteScopeMap, TypertRemoteScopeNamespace, TypertRemoteContribution, TypertRemoteEvent, TypertRemoteEventSelection, TypertRemoteMap, TypertRemoteNamespace, TypertRemoteNamespaceMap, TypertRemoteRegistry, TypertRegistryChange, TypertRegistryListener, TypertSchema, TypertRegistryContract, } from './types.ts';
|
|
16
|
+
export type { InvocationDescriptor, InvocationParameterDescriptor, InvocationSourceLocation, RemoteErrorCode, RemoteErrorDetailsMap, RemoteFailure, RemoteResult, TypertClientEventListener, TypertClientRemote, TypertClientContextAdapter, TypertCodec, TypertContext, TypertContextAdapter, TypertContextMap, TypertContextRegistry, TypertContextWire, TypertDisposer, TypertForwardableEvent, TypertForwardableEventEntry, TypertHostContextAdapter, TypertHostContextIdentity, TypertHostContextResolver, TypertLocalRegistry, TypertLookup, TypertLookupDefinition, TypertLookupHost, TypertLookupMap, TypertLookupProvider, TypertLookupResolver, TypertLookupRegistry, TypertLookupWire, TypertRemoteScopeApi, TypertRemoteScopeMap, TypertRemoteScopeNamespace, TypertRemoteContribution, TypertRemoteEvent, TypertRemoteEventSelection, TypertRemoteMap, TypertRemoteNamespace, TypertRemoteNamespaceMap, TypertRemoteRegistry, TypertRegistryChange, TypertRegistryListener, TypertSchema, TypertRegistryContract, } from './types.ts';
|
|
28
17
|
/** Options for an explicit Service-to-Gateway binding. */
|
|
29
18
|
export interface TypertGatewayBindingOptions {
|
|
30
19
|
/** Wire namespace; defaults to the Cordis service key. */
|
|
@@ -49,8 +38,15 @@ export interface RemoteMethodMarker {
|
|
|
49
38
|
readonly method: string;
|
|
50
39
|
/** Endpoint method when it differs from the implementation member. */
|
|
51
40
|
readonly exportName?: string;
|
|
41
|
+
/** Stream methods yield many independently validated result items. */
|
|
42
|
+
readonly mode?: 'stream';
|
|
52
43
|
readonly invocation: RemoteInvocationMarker;
|
|
53
44
|
}
|
|
45
|
+
/** Options for a non-unary Remote method. */
|
|
46
|
+
export interface RemoteMethodOptions {
|
|
47
|
+
/** Deliver each Iterable item over the shared logical-stream carrier. */
|
|
48
|
+
readonly mode: 'stream';
|
|
49
|
+
}
|
|
54
50
|
type RemoteMethodDecorator = <This extends object, Args extends unknown[], Result>(method: (this: This, ...args: Args) => Result, context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Result>) => void;
|
|
55
51
|
/**
|
|
56
52
|
* Bind one visible Service field to a Cordis key and Remote namespace.
|
|
@@ -79,21 +75,21 @@ export declare abstract class TypertRemoteService<out T = never> extends Service
|
|
|
79
75
|
*/
|
|
80
76
|
export declare function Remote<This extends object, Args extends unknown[], Result>(_method: (this: This, ...args: Args) => Result, context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Result>): void;
|
|
81
77
|
/**
|
|
82
|
-
* Mark one public instance method under
|
|
83
|
-
* @param
|
|
78
|
+
* Mark one public instance method under an exported name or as a logical stream.
|
|
79
|
+
* @param option - endpoint method name or stream delivery mode.
|
|
84
80
|
* @returns a standard method decorator.
|
|
85
81
|
*/
|
|
86
|
-
export declare function Remote(
|
|
82
|
+
export declare function Remote(option: string | RemoteMethodOptions): RemoteMethodDecorator;
|
|
87
83
|
/**
|
|
88
84
|
* Create a decorator for a method resolved from one Remote Scope.
|
|
89
85
|
* @param key - scope key declared through the Context map.
|
|
90
86
|
* @param exportName - optional Remote export name; defaults to the method name.
|
|
91
|
-
* @returns a standard method decorator that records
|
|
87
|
+
* @returns a standard method decorator that records a versioned prototype descriptor.
|
|
92
88
|
*/
|
|
93
89
|
export declare function RemoteScope(key: Extract<keyof TypertContextMap, string>, exportName?: string): RemoteMethodDecorator;
|
|
94
90
|
/**
|
|
95
|
-
* Read Remote markers attached to a live Service
|
|
96
|
-
* The returned snapshot cannot mutate the
|
|
91
|
+
* Read Remote markers attached to a live Service's class prototype.
|
|
92
|
+
* The returned snapshot cannot mutate the stored descriptor.
|
|
97
93
|
* @param service - live Service instance.
|
|
98
94
|
* @returns markers in class declaration order.
|
|
99
95
|
*/
|
package/lib/types/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Remote decorators and explicit Gateway bindings backed
|
|
3
|
-
*
|
|
2
|
+
* Remote decorators and explicit Gateway bindings backed by versioned
|
|
3
|
+
* descriptors carried on decorated class prototypes. Strict reflection
|
|
4
|
+
* remains a Typert compiler responsibility.
|
|
4
5
|
* @module @deepseek-ai/dsh-typert-protocol
|
|
5
6
|
*/
|
|
6
7
|
import { Service } from '@deepseek-ai/cordis';
|
|
8
|
+
export { RemoteError, remoteErrorOf } from "./remote-error.js";
|
|
7
9
|
const TYPERT_REMOTE_SEGMENT_PATTERN = /^[A-Za-z0-9_$.-]+$/;
|
|
8
10
|
/**
|
|
9
11
|
* Test one generated Remote name against the Connection endpoint grammar.
|
|
@@ -13,24 +15,7 @@ const TYPERT_REMOTE_SEGMENT_PATTERN = /^[A-Za-z0-9_$.-]+$/;
|
|
|
13
15
|
export function isTypertRemoteSegment(value) {
|
|
14
16
|
return value !== '.' && value !== '..' && TYPERT_REMOTE_SEGMENT_PATTERN.test(value);
|
|
15
17
|
}
|
|
16
|
-
|
|
17
|
-
* A lookup policy rejection whose typed payload belongs to the active boundary adapter.
|
|
18
|
-
* Gateway adapters preserve this payload instead of collapsing it into an infrastructure failure.
|
|
19
|
-
*/
|
|
20
|
-
export class TypertLookupFailure extends Error {
|
|
21
|
-
/** Adapter-owned failure returned to the caller. */
|
|
22
|
-
failure;
|
|
23
|
-
/**
|
|
24
|
-
* Wrap one adapter failure without exposing the rejected identity.
|
|
25
|
-
* @param failure - typed failure owned by the active boundary adapter.
|
|
26
|
-
*/
|
|
27
|
-
constructor(failure) {
|
|
28
|
-
super('Typert lookup policy rejected the requested identity');
|
|
29
|
-
this.name = 'TypertLookupFailure';
|
|
30
|
-
this.failure = failure;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
const markers = new WeakMap();
|
|
18
|
+
const REMOTE_METHOD_DESCRIPTOR = '@deepseek-ai/dsh-typert-protocol/remote-methods';
|
|
34
19
|
/**
|
|
35
20
|
* Bind one visible Service field to a Cordis key and Remote namespace.
|
|
36
21
|
* @param service - owning Service instance, normally `this`.
|
|
@@ -59,34 +44,45 @@ export class TypertRemoteService extends Service {
|
|
|
59
44
|
this.typertRemote = bindTypertRemote(this, this.name, options);
|
|
60
45
|
}
|
|
61
46
|
}
|
|
62
|
-
export function Remote(
|
|
63
|
-
if (typeof
|
|
64
|
-
validateName('Remote export name',
|
|
65
|
-
return
|
|
66
|
-
|
|
67
|
-
|
|
47
|
+
export function Remote(methodExportOrOptions, context) {
|
|
48
|
+
if (typeof methodExportOrOptions === 'string') {
|
|
49
|
+
validateName('Remote export name', methodExportOrOptions);
|
|
50
|
+
return remoteDecorator({ kind: 'direct' }, undefined, methodExportOrOptions);
|
|
51
|
+
}
|
|
52
|
+
if (typeof methodExportOrOptions === 'object') {
|
|
53
|
+
if (remoteOptionMode(methodExportOrOptions) !== 'stream'
|
|
54
|
+
|| Reflect.ownKeys(methodExportOrOptions).length !== 1) {
|
|
55
|
+
throw new TypeError('typert-protocol: Remote options must contain exactly mode: "stream"');
|
|
56
|
+
}
|
|
57
|
+
return remoteDecorator({ kind: 'direct' }, 'stream');
|
|
68
58
|
}
|
|
69
59
|
if (context === undefined)
|
|
70
60
|
throw new TypeError('typert-protocol: Remote decorator context is missing');
|
|
71
61
|
addMarkerInitializer(context, { kind: 'direct' });
|
|
72
62
|
}
|
|
63
|
+
function remoteOptionMode(options) {
|
|
64
|
+
return Reflect.get(options, 'mode');
|
|
65
|
+
}
|
|
66
|
+
function remoteDecorator(invocation, mode, exportName) {
|
|
67
|
+
return function (_method, context) {
|
|
68
|
+
addMarkerInitializer(context, invocation, mode, exportName);
|
|
69
|
+
};
|
|
70
|
+
}
|
|
73
71
|
/**
|
|
74
72
|
* Create a decorator for a method resolved from one Remote Scope.
|
|
75
73
|
* @param key - scope key declared through the Context map.
|
|
76
74
|
* @param exportName - optional Remote export name; defaults to the method name.
|
|
77
|
-
* @returns a standard method decorator that records
|
|
75
|
+
* @returns a standard method decorator that records a versioned prototype descriptor.
|
|
78
76
|
*/
|
|
79
77
|
export function RemoteScope(key, exportName) {
|
|
80
78
|
validateName('Scope key', key);
|
|
81
79
|
if (exportName !== undefined)
|
|
82
80
|
validateName('Remote export name', exportName);
|
|
83
|
-
return
|
|
84
|
-
addMarkerInitializer(context, { kind: 'context', context: key }, exportName);
|
|
85
|
-
};
|
|
81
|
+
return remoteDecorator({ kind: 'context', context: key }, undefined, exportName);
|
|
86
82
|
}
|
|
87
83
|
/**
|
|
88
|
-
* Read Remote markers attached to a live Service
|
|
89
|
-
* The returned snapshot cannot mutate the
|
|
84
|
+
* Read Remote markers attached to a live Service's class prototype.
|
|
85
|
+
* The returned snapshot cannot mutate the stored descriptor.
|
|
90
86
|
* @param service - live Service instance.
|
|
91
87
|
* @returns markers in class declaration order.
|
|
92
88
|
*/
|
|
@@ -94,9 +90,27 @@ export function remoteMethods(service) {
|
|
|
94
90
|
const prototype = Object.getPrototypeOf(service);
|
|
95
91
|
if (prototype === null)
|
|
96
92
|
return [];
|
|
97
|
-
return
|
|
93
|
+
return (readRemoteMethodDescriptor(prototype)?.methods ?? []).map(marker => ({ ...marker }));
|
|
98
94
|
}
|
|
99
|
-
function
|
|
95
|
+
function readRemoteMethodDescriptor(prototype) {
|
|
96
|
+
const property = Object.getOwnPropertyDescriptor(prototype, REMOTE_METHOD_DESCRIPTOR);
|
|
97
|
+
if (property === undefined)
|
|
98
|
+
return undefined;
|
|
99
|
+
const descriptor = property.value;
|
|
100
|
+
if (descriptor === null || typeof descriptor !== 'object') {
|
|
101
|
+
throw new TypeError('typert-protocol: Remote method descriptor must be an object');
|
|
102
|
+
}
|
|
103
|
+
const version = Reflect.get(descriptor, 'version');
|
|
104
|
+
if (version !== 1) {
|
|
105
|
+
throw new TypeError(`typert-protocol: unsupported Remote method descriptor version ${String(version)}`);
|
|
106
|
+
}
|
|
107
|
+
const methods = Reflect.get(descriptor, 'methods');
|
|
108
|
+
if (!Array.isArray(methods)) {
|
|
109
|
+
throw new TypeError('typert-protocol: Remote method descriptor methods must be an array');
|
|
110
|
+
}
|
|
111
|
+
return descriptor;
|
|
112
|
+
}
|
|
113
|
+
function addMarkerInitializer(context, invocation, mode, exportName) {
|
|
100
114
|
if (context.private || context.static || typeof context.name !== 'string') {
|
|
101
115
|
throw new TypeError('typert-protocol: Remote decorators require a public instance method with a string name');
|
|
102
116
|
}
|
|
@@ -106,30 +120,39 @@ function addMarkerInitializer(context, invocation, exportName) {
|
|
|
106
120
|
if (prototype === null) {
|
|
107
121
|
throw new TypeError(`typert-protocol: cannot mark Remote method "${method}" on an object without a prototype`);
|
|
108
122
|
}
|
|
109
|
-
mark(prototype, method, invocation, exportName);
|
|
123
|
+
mark(prototype, method, invocation, mode, exportName);
|
|
110
124
|
});
|
|
111
125
|
}
|
|
112
|
-
function mark(prototype, method, invocation, exportName) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
markers.set(prototype, table);
|
|
117
|
-
}
|
|
118
|
-
const marker = {
|
|
126
|
+
function mark(prototype, method, invocation, mode, exportName) {
|
|
127
|
+
const descriptor = readRemoteMethodDescriptor(prototype);
|
|
128
|
+
const marker = Object.freeze({
|
|
129
|
+
method,
|
|
119
130
|
...(exportName === undefined || exportName === method ? {} : { exportName }),
|
|
131
|
+
...(mode === undefined ? {} : { mode }),
|
|
120
132
|
invocation: Object.freeze(invocation),
|
|
121
|
-
};
|
|
122
|
-
const current =
|
|
133
|
+
});
|
|
134
|
+
const current = descriptor?.methods.find(candidate => candidate.method === method);
|
|
123
135
|
if (current !== undefined) {
|
|
124
|
-
if (current.exportName === marker.exportName
|
|
136
|
+
if (current.exportName === marker.exportName
|
|
137
|
+
&& current.mode === marker.mode
|
|
138
|
+
&& sameInvocation(current.invocation, invocation))
|
|
125
139
|
return;
|
|
126
140
|
throw new Error(`typert-protocol: Remote method "${method}" has conflicting invocation markers`);
|
|
127
141
|
}
|
|
128
|
-
|
|
142
|
+
Object.defineProperty(prototype, REMOTE_METHOD_DESCRIPTOR, {
|
|
143
|
+
configurable: true,
|
|
144
|
+
value: Object.freeze({
|
|
145
|
+
version: 1,
|
|
146
|
+
methods: Object.freeze([...(descriptor?.methods ?? []), marker]),
|
|
147
|
+
}),
|
|
148
|
+
});
|
|
129
149
|
}
|
|
130
150
|
function sameInvocation(left, right) {
|
|
131
|
-
|
|
132
|
-
|
|
151
|
+
if (left.kind === 'direct')
|
|
152
|
+
return right.kind === 'direct';
|
|
153
|
+
if (right.kind === 'direct')
|
|
154
|
+
return false;
|
|
155
|
+
return left.context === right.context;
|
|
133
156
|
}
|
|
134
157
|
function validateName(subject, value) {
|
|
135
158
|
if (!isTypertRemoteSegment(value)) {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** The one Remote failure class shared by owners, the Gateway, and consumers. */
|
|
2
|
+
import type { RemoteErrorCode, RemoteErrorDetailsMap, RemoteFailure } from './types.ts';
|
|
3
|
+
/**
|
|
4
|
+
* One Remote call failure: a real Error carrying its stable code and typed
|
|
5
|
+
* details. Owners throw it at the failure point; the Host Gateway encodes it
|
|
6
|
+
* onto the wire unchanged; the Client face rebuilds an instance for the
|
|
7
|
+
* `RemoteResult` error branch, so `throw result.error` keeps throw semantics.
|
|
8
|
+
* Discrimination is always by `code`, never by instanceof.
|
|
9
|
+
*/
|
|
10
|
+
export declare class RemoteError<Code extends RemoteErrorCode = RemoteErrorCode> extends Error {
|
|
11
|
+
readonly code: Code;
|
|
12
|
+
readonly details: RemoteErrorDetailsMap[Code];
|
|
13
|
+
/** Structural marker: cross-realm/bundle identification never uses instanceof. */
|
|
14
|
+
readonly isDSHRemoteError: true;
|
|
15
|
+
/**
|
|
16
|
+
* @param code - stable failure code declared in {@link RemoteErrorDetailsMap}.
|
|
17
|
+
* @param message - human diagnostic carried across the wire.
|
|
18
|
+
* @param details - structured payload typed by the code.
|
|
19
|
+
* @param options - standard Error options (`cause` survives in-process only).
|
|
20
|
+
*/
|
|
21
|
+
constructor(code: Code, message: string, details: RemoteErrorDetailsMap[Code], options?: ErrorOptions);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Structurally identify a RemoteError thrown across module or realm copies of
|
|
25
|
+
* this class. Mechanism-internal: the Gateway and test assertions use it;
|
|
26
|
+
* business code receives typed failures and never needs it.
|
|
27
|
+
* @param value - a caught value.
|
|
28
|
+
* @returns the failure when the marker matches, otherwise undefined.
|
|
29
|
+
*/
|
|
30
|
+
export declare function remoteErrorOf(value: unknown): RemoteFailure | undefined;
|
|
31
|
+
//# sourceMappingURL=remote-error.d.ts.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** The one Remote failure class shared by owners, the Gateway, and consumers. */
|
|
2
|
+
/**
|
|
3
|
+
* One Remote call failure: a real Error carrying its stable code and typed
|
|
4
|
+
* details. Owners throw it at the failure point; the Host Gateway encodes it
|
|
5
|
+
* onto the wire unchanged; the Client face rebuilds an instance for the
|
|
6
|
+
* `RemoteResult` error branch, so `throw result.error` keeps throw semantics.
|
|
7
|
+
* Discrimination is always by `code`, never by instanceof.
|
|
8
|
+
*/
|
|
9
|
+
export class RemoteError extends Error {
|
|
10
|
+
code;
|
|
11
|
+
details;
|
|
12
|
+
/** Structural marker: cross-realm/bundle identification never uses instanceof. */
|
|
13
|
+
isDSHRemoteError = true;
|
|
14
|
+
/**
|
|
15
|
+
* @param code - stable failure code declared in {@link RemoteErrorDetailsMap}.
|
|
16
|
+
* @param message - human diagnostic carried across the wire.
|
|
17
|
+
* @param details - structured payload typed by the code.
|
|
18
|
+
* @param options - standard Error options (`cause` survives in-process only).
|
|
19
|
+
*/
|
|
20
|
+
constructor(code, message, details, options) {
|
|
21
|
+
super(message, options);
|
|
22
|
+
this.code = code;
|
|
23
|
+
this.details = details;
|
|
24
|
+
this.name = 'RemoteError';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Structurally identify a RemoteError thrown across module or realm copies of
|
|
29
|
+
* this class. Mechanism-internal: the Gateway and test assertions use it;
|
|
30
|
+
* business code receives typed failures and never needs it.
|
|
31
|
+
* @param value - a caught value.
|
|
32
|
+
* @returns the failure when the marker matches, otherwise undefined.
|
|
33
|
+
*/
|
|
34
|
+
export function remoteErrorOf(value) {
|
|
35
|
+
// Structural, not instanceof: an Error thrown in another realm (iframe, VM)
|
|
36
|
+
// fails instanceof Error here, so the marker plus the code field is the test.
|
|
37
|
+
if (typeof value === 'object' && value !== null
|
|
38
|
+
&& value.isDSHRemoteError === true
|
|
39
|
+
&& typeof value.code === 'string') {
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=remote-error.js.map
|
package/lib/types/types.d.ts
CHANGED
|
@@ -32,20 +32,34 @@ export interface TypertContextMap {
|
|
|
32
32
|
export interface TypertRemoteMap {
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
35
|
+
* Merge-extensible Remote failure vocabulary: this package declares the
|
|
36
|
+
* universal carrier codes once; the Gateway merges its infrastructure codes
|
|
37
|
+
* and every owner merges its domain codes next to the throwing code.
|
|
38
38
|
*/
|
|
39
|
-
export interface
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
export interface RemoteErrorDetailsMap {
|
|
40
|
+
/** Owner-side business validation refused the request; `issues` carries codec output when one produced it. */
|
|
41
|
+
'gateway/bad-request': {
|
|
42
|
+
readonly issues?: readonly object[];
|
|
43
|
+
};
|
|
44
|
+
/** The call was cancelled by the carrier signal or the backend. */
|
|
45
|
+
'gateway/cancelled': {};
|
|
46
|
+
/** Carrier, dispatch, or unclassified Host failure. */
|
|
47
|
+
'gateway/internal': {};
|
|
43
48
|
}
|
|
49
|
+
/** Every declared Remote failure code. */
|
|
50
|
+
export type RemoteErrorCode = keyof RemoteErrorDetailsMap;
|
|
51
|
+
/**
|
|
52
|
+
* One Remote call's failure: the code-discriminated union of RemoteError
|
|
53
|
+
* instances, so a `code` branch narrows `details` with no cast.
|
|
54
|
+
*/
|
|
55
|
+
export type RemoteFailure = {
|
|
56
|
+
[Code in RemoteErrorCode]: import('./remote-error.ts').RemoteError<Code>;
|
|
57
|
+
}[RemoteErrorCode];
|
|
44
58
|
/**
|
|
45
59
|
* What every generated Remote method resolves to. The Remote face itself folds
|
|
46
60
|
* carrier failures into the error branch, so no consumer wraps a call to
|
|
47
61
|
* recover one; only assembly faults (arity, an unmounted method, a missing
|
|
48
|
-
* Context
|
|
62
|
+
* Context adapter) still reject.
|
|
49
63
|
* @template T - the Host method's business result.
|
|
50
64
|
*/
|
|
51
65
|
export type RemoteResult<T> = {
|
|
@@ -58,20 +72,47 @@ export type RemoteResult<T> = {
|
|
|
58
72
|
/** Merge-extensible scoped Remote method signatures generated for consumers. */
|
|
59
73
|
export interface TypertRemoteScopeMap {
|
|
60
74
|
}
|
|
75
|
+
type TypertEventParameters<Event extends keyof Events> = Events[Event] extends (...args: infer Args) => unknown ? Args : never;
|
|
76
|
+
type TypertEventResult<Event extends keyof Events> = Events[Event] extends (...args: never[]) => infer Result ? Result : never;
|
|
77
|
+
type TypertProjectedContextKey = Extract<keyof TypertLookupMap, keyof TypertContextMap>;
|
|
78
|
+
type TypertProjectedContextSubject = {
|
|
79
|
+
[Key in TypertProjectedContextKey]: TypertLookupHost<TypertLookupMap[Key]>;
|
|
80
|
+
}[TypertProjectedContextKey];
|
|
81
|
+
type TypertAgentScopedRequest<Request> = Request extends object ? 'agent' extends keyof Request ? Exclude<Request['agent'], undefined> extends TypertProjectedContextSubject ? Request : never : never : never;
|
|
82
|
+
type TypertWaterfallEvent<Event extends keyof Events> = unknown extends ThisParameterType<Events[Event]> ? never : TypertEventParameters<Event> extends [infer Request, infer Next] ? Next extends () => TypertEventResult<Event> ? TypertEventResult<Event> extends Promise<unknown> ? TypertAgentScopedRequest<Request> extends never ? never : Event : never : never : never;
|
|
83
|
+
type TypertForwardingMode<Event extends keyof Events> = unknown extends ThisParameterType<Events[Event]> ? TypertEventResult<Event> extends void ? 'emit' : never : TypertWaterfallEvent<Event> extends never ? never : 'waterfall';
|
|
61
84
|
/**
|
|
62
|
-
* Cordis event names
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* cannot represent.
|
|
85
|
+
* Cordis event names the Remote Event carrier can preserve without a second
|
|
86
|
+
* signature declaration: unscoped `void` notifications and scoped async
|
|
87
|
+
* waterfalls whose final parameter is their same-result `next()` callback.
|
|
66
88
|
*/
|
|
67
89
|
export type TypertForwardableEvent = {
|
|
68
|
-
[Event in keyof Events]:
|
|
90
|
+
[Event in keyof Events]: TypertForwardingMode<Event> extends never ? never : Event;
|
|
91
|
+
}[keyof Events];
|
|
92
|
+
/** Event and dispatch mode accepted by the Remote Event source. */
|
|
93
|
+
export type TypertForwardableEventEntry = {
|
|
94
|
+
[Event in keyof Events]: TypertForwardingMode<Event> extends infer Mode ? Mode extends 'emit' | 'waterfall' ? {
|
|
95
|
+
readonly event: Event;
|
|
96
|
+
readonly mode: Mode;
|
|
97
|
+
} : never : never;
|
|
69
98
|
}[keyof Events];
|
|
70
99
|
/** Merge-extensible forwarding selection declared once by the Host assembly. */
|
|
71
100
|
export interface TypertRemoteEventSelection {
|
|
72
101
|
}
|
|
73
|
-
/** Legal `$on` keys
|
|
74
|
-
export type TypertRemoteEvent = Extract<
|
|
102
|
+
/** Legal `$on` keys selected from the carrier-compatible Cordis event declarations. */
|
|
103
|
+
export type TypertRemoteEvent = Extract<TypertForwardableEvent, keyof TypertRemoteEventSelection>;
|
|
104
|
+
type TypertClientAgent<Value> = Exclude<Value, undefined> extends TypertProjectedContextSubject ? Context | Extract<Value, undefined> : Value;
|
|
105
|
+
type TypertClientEventRequest<Request> = Request extends object ? {
|
|
106
|
+
[Key in keyof Request]: Key extends 'agent' ? TypertClientAgent<Request[Key]> : Request[Key];
|
|
107
|
+
} : never;
|
|
108
|
+
type TypertScopedClientEventListener<Event extends TypertRemoteEvent> = Events[Event] extends (request: infer Request, next: infer Next) => infer Result ? (this: Context, request: TypertClientEventRequest<Request>, next: Next) => Result : never;
|
|
109
|
+
/**
|
|
110
|
+
* Listener derived from one selected Cordis event declaration. Scoped Host
|
|
111
|
+
* subjects become the resolved Client `Context`; one-way notifications retain
|
|
112
|
+
* their declaration unchanged.
|
|
113
|
+
* @template Event - selected Remote Event name.
|
|
114
|
+
*/
|
|
115
|
+
export type TypertClientEventListener<Event extends TypertRemoteEvent> = unknown extends ThisParameterType<Events[Event]> ? Events[Event] : TypertScopedClientEventListener<Event>;
|
|
75
116
|
/**
|
|
76
117
|
* Resolve one direct Remote namespace from the generated flat endpoint map.
|
|
77
118
|
* @template Namespace - wire namespace before the endpoint slash.
|
|
@@ -148,6 +189,8 @@ export interface InvocationDescriptor {
|
|
|
148
189
|
readonly method: string;
|
|
149
190
|
/** Service member invoked when the exported method name is an alias. */
|
|
150
191
|
readonly implementation?: string;
|
|
192
|
+
/** Absent for unary calls; stream calls validate and deliver every yielded item. */
|
|
193
|
+
readonly mode?: 'stream';
|
|
151
194
|
/** Receiver selection mode. */
|
|
152
195
|
readonly invocation: {
|
|
153
196
|
readonly kind: 'direct';
|
|
@@ -159,7 +202,7 @@ export interface InvocationDescriptor {
|
|
|
159
202
|
};
|
|
160
203
|
/** Optional consuming-Context projection for one direct lookup parameter. */
|
|
161
204
|
readonly scope?: {
|
|
162
|
-
/** Context kind whose Client
|
|
205
|
+
/** Context kind whose Client adapter supplies the identity. */
|
|
163
206
|
readonly context: string;
|
|
164
207
|
/** Lookup parameter wire field replaced by the Context identity. */
|
|
165
208
|
readonly wire: string;
|
|
@@ -171,7 +214,7 @@ export interface InvocationDescriptor {
|
|
|
171
214
|
/** Reserved final Host method parameter. */
|
|
172
215
|
readonly parameter: 'signal';
|
|
173
216
|
};
|
|
174
|
-
/** Codec for the
|
|
217
|
+
/** Codec for the unary result or each yielded stream item. */
|
|
175
218
|
readonly result: TypertCodec;
|
|
176
219
|
/** Source declaration used only for diagnostics. */
|
|
177
220
|
readonly sourceLocation?: InvocationSourceLocation;
|
|
@@ -192,26 +235,15 @@ export interface TypertClientRemote extends TypertRemoteNamespaceMap {
|
|
|
192
235
|
*/
|
|
193
236
|
$mount(contribution: TypertRemoteContribution): Promise<TypertDisposer>;
|
|
194
237
|
/**
|
|
195
|
-
* Subscribe to one forwarded Host event
|
|
196
|
-
* order
|
|
238
|
+
* Subscribe to one forwarded Host event. Notifications run in registration
|
|
239
|
+
* order and isolate failures; scoped waterfalls return, delegate through
|
|
240
|
+
* `next()`, or reject the Host dispatch.
|
|
197
241
|
* @template Event - forwarded event name selected by the Host assembly.
|
|
198
242
|
* @param event - forwarded Host event name, unchanged on the wire.
|
|
199
|
-
* @param listener - receives the
|
|
243
|
+
* @param listener - receives the Client projection of the Cordis `Events` declaration.
|
|
200
244
|
* @returns disposer owned by the calling fiber.
|
|
201
245
|
*/
|
|
202
|
-
$on<Event extends TypertRemoteEvent>(event: Event, listener:
|
|
203
|
-
/**
|
|
204
|
-
* Hand one decoded forwarded frame to the subscription table. The carrier
|
|
205
|
-
* owning the Host frame sink calls this; a consumer subscribes with
|
|
206
|
-
* {@link TypertClientRemote.$on} and never calls it.
|
|
207
|
-
*
|
|
208
|
-
* `event` is a plain string because this is the wire boundary: the name is
|
|
209
|
-
* whatever the Host assembly's allowlist selected, and one nobody subscribed
|
|
210
|
-
* to is dropped silently.
|
|
211
|
-
* @param event - forwarded Host event name, exactly as the Host emitted it.
|
|
212
|
-
* @param args - the Host argument list, already JSON-decoded.
|
|
213
|
-
*/
|
|
214
|
-
$dispatch(event: string, args: readonly unknown[]): void;
|
|
246
|
+
$on<Event extends TypertRemoteEvent>(event: Event, listener: TypertClientEventListener<Event>): () => void;
|
|
215
247
|
}
|
|
216
248
|
/**
|
|
217
249
|
* Resolve one validated wire identity, synchronously or asynchronously.
|
|
@@ -249,29 +281,52 @@ export interface TypertLookupDefinition {
|
|
|
249
281
|
/** Canonical wire type symbol used by strict generation. */
|
|
250
282
|
readonly wireTypeSymbol: string;
|
|
251
283
|
}
|
|
252
|
-
/**
|
|
253
|
-
export interface
|
|
254
|
-
/** Wire field carrying the Context identity. */
|
|
255
|
-
readonly wire: string;
|
|
256
|
-
/** Canonical wire type symbol used by strict generation. */
|
|
257
|
-
readonly wireTypeSymbol: string;
|
|
284
|
+
/** Bidirectional projection between one environment's Context and its wire identity. */
|
|
285
|
+
export interface TypertContextAdapter<Wire = unknown> {
|
|
258
286
|
/**
|
|
259
|
-
*
|
|
287
|
+
* Read the identity represented by a live Context.
|
|
288
|
+
* @param ctx - Context in this adapter's environment.
|
|
289
|
+
* @returns the wire identity, or `undefined` when the Context has another kind.
|
|
290
|
+
*/
|
|
291
|
+
identity(ctx: Context): Wire | undefined;
|
|
292
|
+
/**
|
|
293
|
+
* Resolve a wire identity to a live Context in this adapter's environment.
|
|
294
|
+
* An asynchronous Client resolver may wait for its owner to create the Context.
|
|
260
295
|
* @param id - validated wire identity.
|
|
261
|
-
* @returns the
|
|
296
|
+
* @returns the Context, or `undefined` when it is unavailable.
|
|
262
297
|
*/
|
|
263
298
|
resolve(id: Wire): Context | undefined | Promise<Context | undefined>;
|
|
264
299
|
}
|
|
265
|
-
/**
|
|
300
|
+
/** Host Context adapter plus the wire declaration used by strict Remote methods. */
|
|
301
|
+
export interface TypertHostContextAdapter<Wire = unknown> extends TypertContextAdapter<Wire> {
|
|
302
|
+
/** Wire field carrying the Context identity. */
|
|
303
|
+
readonly wire: string;
|
|
304
|
+
/** Canonical wire type symbol used by strict generation. */
|
|
305
|
+
readonly wireTypeSymbol: string;
|
|
306
|
+
}
|
|
307
|
+
/** Composition-owned resolver replacing one Host Context adapter's default lookup policy. */
|
|
266
308
|
export type TypertHostContextResolver<Wire = unknown> = (id: Wire) => Context | undefined | Promise<Context | undefined>;
|
|
267
|
-
/** Client
|
|
268
|
-
export interface
|
|
309
|
+
/** Client-side bidirectional Context adapter. */
|
|
310
|
+
export interface TypertClientContextAdapter<Wire = unknown> {
|
|
269
311
|
/**
|
|
270
|
-
* Read the
|
|
271
|
-
* @param ctx - Context
|
|
272
|
-
* @returns the wire identity, or `undefined`
|
|
312
|
+
* Read the identity represented by a live Client Context.
|
|
313
|
+
* @param ctx - Client Context inspected by a scoped Remote caller.
|
|
314
|
+
* @returns the wire identity, or `undefined` for another Context kind.
|
|
273
315
|
*/
|
|
274
316
|
identity(ctx: Context): Wire | undefined;
|
|
317
|
+
/**
|
|
318
|
+
* Resolve a wire identity from the Client's currently materialized Contexts.
|
|
319
|
+
* @param id - validated wire identity.
|
|
320
|
+
* @returns the Client Context, or `undefined` when unavailable.
|
|
321
|
+
*/
|
|
322
|
+
resolve(id: Wire): Context | undefined;
|
|
323
|
+
}
|
|
324
|
+
/** Host Context identity selected from the registered adapter set. */
|
|
325
|
+
export interface TypertHostContextIdentity {
|
|
326
|
+
/** Merge-declared Context kind whose adapter recognized the Context. */
|
|
327
|
+
readonly kind: string;
|
|
328
|
+
/** Wire identity returned by that adapter. */
|
|
329
|
+
readonly identity: unknown;
|
|
275
330
|
}
|
|
276
331
|
/** Notification emitted after a Typert runtime registry changes. */
|
|
277
332
|
export interface TypertRegistryChange {
|
|
@@ -360,17 +415,17 @@ export interface TypertLookupRegistry {
|
|
|
360
415
|
*/
|
|
361
416
|
subscribe(listener: TypertRegistryListener): TypertDisposer;
|
|
362
417
|
}
|
|
363
|
-
/** Runtime registry for Host
|
|
418
|
+
/** Runtime registry for the Host and Client adapters of each Context kind. */
|
|
364
419
|
export interface TypertContextRegistry {
|
|
365
420
|
/**
|
|
366
|
-
* Register a Host Context
|
|
421
|
+
* Register a Host Context adapter.
|
|
367
422
|
* @param key - merge-declared Context key.
|
|
368
|
-
* @param
|
|
369
|
-
* @returns disposer withdrawing the exact
|
|
423
|
+
* @param adapter - owning package's bidirectional Host projection.
|
|
424
|
+
* @returns disposer withdrawing the exact adapter.
|
|
370
425
|
*/
|
|
371
|
-
registerHost<K extends StringKeyOf<TypertContextMap>>(key: K,
|
|
426
|
+
registerHost<K extends StringKeyOf<TypertContextMap>>(key: K, adapter: TypertHostContextAdapter<TypertContextWire<TypertContextMap[K]>>): TypertDisposer;
|
|
372
427
|
/**
|
|
373
|
-
* Override one Host Context key's
|
|
428
|
+
* Override one Host Context key's resolution policy for the calling fiber.
|
|
374
429
|
* Configuration may precede provider registration and restores the provider's default resolver on disposal.
|
|
375
430
|
* @param key - merge-declared Context key.
|
|
376
431
|
* @param resolver - composition-owned resolver used by every Host Context lookup of this key.
|
|
@@ -378,26 +433,33 @@ export interface TypertContextRegistry {
|
|
|
378
433
|
*/
|
|
379
434
|
configureHost<K extends StringKeyOf<TypertContextMap>>(key: K, resolver: TypertHostContextResolver<TypertContextWire<TypertContextMap[K]>>): TypertDisposer;
|
|
380
435
|
/**
|
|
381
|
-
* Register a Client Context
|
|
436
|
+
* Register a Client Context adapter.
|
|
382
437
|
* @param key - merge-declared Context key.
|
|
383
|
-
* @param
|
|
384
|
-
* @returns disposer withdrawing the exact
|
|
438
|
+
* @param adapter - owning package's bidirectional Client projection.
|
|
439
|
+
* @returns disposer withdrawing the exact adapter.
|
|
440
|
+
*/
|
|
441
|
+
registerClient<K extends StringKeyOf<TypertContextMap>>(key: K, adapter: TypertClientContextAdapter<TypertContextWire<TypertContextMap[K]>>): TypertDisposer;
|
|
442
|
+
/**
|
|
443
|
+
* Identify a live Host Context through the sole registered adapter set.
|
|
444
|
+
* @param ctx - Context projected by a Host-to-Client scoped event.
|
|
445
|
+
* @returns its kind and wire identity, or `undefined` when no adapter recognizes it.
|
|
446
|
+
* @throws when more than one Context kind recognizes the same Context.
|
|
385
447
|
*/
|
|
386
|
-
|
|
448
|
+
identifyHost(ctx: Context): TypertHostContextIdentity | undefined;
|
|
387
449
|
/**
|
|
388
|
-
* Look up a Host Context
|
|
450
|
+
* Look up a Host Context adapter.
|
|
389
451
|
* @param key - descriptor Context key.
|
|
390
|
-
* @returns the
|
|
452
|
+
* @returns the adapter, or `undefined` when absent.
|
|
391
453
|
*/
|
|
392
|
-
getHost(key: string):
|
|
454
|
+
getHost(key: string): TypertHostContextAdapter | undefined;
|
|
393
455
|
/**
|
|
394
|
-
* Look up a Client Context
|
|
456
|
+
* Look up a Client Context adapter.
|
|
395
457
|
* @param key - descriptor Context key.
|
|
396
|
-
* @returns the
|
|
458
|
+
* @returns the adapter, or `undefined` when absent.
|
|
397
459
|
*/
|
|
398
|
-
getClient(key: string):
|
|
460
|
+
getClient(key: string): TypertClientContextAdapter | undefined;
|
|
399
461
|
/**
|
|
400
|
-
* Observe later Context
|
|
462
|
+
* Observe later Context adapter changes.
|
|
401
463
|
* @param listener - synchronous contained observer.
|
|
402
464
|
* @returns disposer for this subscription.
|
|
403
465
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-typert-protocol",
|
|
3
3
|
"description": "Compiler-independent Remote metadata and Typert provider protocols",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.2-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -37,11 +37,11 @@
|
|
|
37
37
|
],
|
|
38
38
|
"license": "MIT",
|
|
39
39
|
"peerDependencies": {
|
|
40
|
-
"@deepseek-ai/
|
|
41
|
-
"@deepseek-ai/
|
|
40
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
41
|
+
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@deepseek-ai/dsh-invariants": "^0.1.
|
|
45
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
44
|
+
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
|
|
45
|
+
"@deepseek-ai/cordis": "^4.0.2"
|
|
46
46
|
}
|
|
47
47
|
}
|