@x1a0f3n9/dsh-typert-protocol 0.1.2-alpha.6

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DeepSeek
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,6 @@
1
+ # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
2
+ # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
+ # after editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/typert/protocol/README.md
5
+ README.md: 7f7c817073c7c5ca3bb3746fe681f8fd426a3167
6
+ README.zh.md: 462e20b80967a8e7546a9cab71e6d1eebe4cc807
package/README.md ADDED
@@ -0,0 +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
+
6
+ # @x1a0f3n9/dsh-typert-protocol
7
+
8
+ English | [中文](README.zh.md)
9
+
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 '@x1a0f3n9/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 '@x1a0f3n9/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.
69
+
70
+ -----
71
+
72
+ <a id="understand-the-implementation"></a>
73
+ ## Understand the implementation
74
+
75
+ <details>
76
+ <summary>Implementation internals — click to expand</summary>
77
+
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).
79
+
80
+ ### Design concept
81
+
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`.
83
+
84
+ ### Remote markers
85
+
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.
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
+ | — | No runtime invariant companion is published; decorators retain private immutable declarations and bindings are frozen values with no independent event stream to cross-check. |
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>
123
+ ## Model Experience
124
+
125
+ None, as compiler-independent Remote protocol declarations register nothing model-facing.
126
+
127
+ #### KV Cache effect
128
+
129
+ No direct effect; the declared contracts reach a request only when an assembly places them in one.
130
+
131
+ ## Known Limitations and Deferred Work
132
+
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 ADDED
@@ -0,0 +1,149 @@
1
+ ---
2
+ description: "共享的 Typert Remote 协议:业务包、生成产物、Host Gateway 与 Client API 使用的装饰器、wire 描述符、编解码器与提供方约定。"
3
+ kind: "package-library"
4
+ ---
5
+
6
+ # @x1a0f3n9/dsh-typert-protocol
7
+
8
+ [English](README.md) | 中文
9
+
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 '@x1a0f3n9/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 '@x1a0f3n9/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 私有持有。
69
+
70
+ -----
71
+
72
+ <a id="understand-the-implementation"></a>
73
+ ## 理解实现
74
+
75
+ <details>
76
+ <summary>实现细节——点击展开</summary>
77
+
78
+ 本节解释声明如何保持与编译器无关,以及每个约定在哪里执行;编程模型已在[使用本包](#use-this-package)中说明。
79
+
80
+ ### 设计理念
81
+
82
+ 本包把严格反射留在编译器中:装饰器初始化器把最小标记保存在 Service 原型上的带版本描述符中。描述符使用稳定的字符串属性名,因此协议包的另一个已安装副本也能读取同一组标记。完整的参数、结果、查找与 schema 反射是 Typert 构建流水线的职责,通过 `InvocationDescriptor` 交付。
83
+
84
+ ### Remote 标记
85
+
86
+ `@Remote` 与 `@RemoteScope` 调度一个初始化器,把方法名、可选导出名与调用模式追加到原型描述符;`remoteMethods(service)` 校验其版本,并返回与已存描述符分离、按声明顺序排列的快照,供 Gateway 的源码模式回退读取。标记要求公开、非静态、具名字符串的实例方法,同一方法上的冲突标记会被拒绝。
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
+ | — | 不发布运行时不变量伴生入口;decorator 只保留私有不可变声明,binding 也是冻结值,没有可供交叉核对的独立事件流。 |
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>
123
+ ## 模型体验
124
+
125
+ 无,因为与编译器无关的 Remote 协议声明不注册任何面向模型的内容。
126
+
127
+ #### KV Cache 影响
128
+
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>
146
+
147
+ 无。
148
+
149
+ </details>
package/lib/index.js ADDED
@@ -0,0 +1,184 @@
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
40
+ //#region lib/types/index.js
41
+ /**
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.
45
+ * @module @x1a0f3n9/dsh-typert-protocol
46
+ */
47
+ const TYPERT_REMOTE_SEGMENT_PATTERN = /^[A-Za-z0-9_$.-]+$/;
48
+ /**
49
+ * Test one generated Remote name against the Connection endpoint grammar.
50
+ * @param value - namespace, method, lookup, or Context segment.
51
+ * @returns whether the value can cross the shared RPC carrier unchanged.
52
+ */
53
+ function isTypertRemoteSegment(value) {
54
+ return value !== "." && value !== ".." && TYPERT_REMOTE_SEGMENT_PATTERN.test(value);
55
+ }
56
+ const REMOTE_METHOD_DESCRIPTOR = "@x1a0f3n9/dsh-typert-protocol/remote-methods";
57
+ /**
58
+ * Bind one visible Service field to a Cordis key and Remote namespace.
59
+ * @param service - owning Service instance, normally `this`.
60
+ * @param serviceKey - exact Cordis service key.
61
+ * @param options - optional distinct wire namespace.
62
+ * @returns a frozen, inspectable binding with no compiler-injected metadata.
63
+ */
64
+ function bindTypertRemote(service, serviceKey, options = {}) {
65
+ validateName("service key", serviceKey);
66
+ const namespace = options.namespace ?? serviceKey;
67
+ validateName("namespace", namespace);
68
+ return Object.freeze({
69
+ service,
70
+ serviceKey,
71
+ namespace
72
+ });
73
+ }
74
+ /** Cordis Service base that exposes its registered name through Typert Gateway. */
75
+ var TypertRemoteService = class extends Service {
76
+ /** Visible binding consumed by the Gateway's source-mode discovery. */
77
+ typertRemote;
78
+ /**
79
+ * Register the Service and bind the same key to Typert Gateway.
80
+ * @param ctx - owning Cordis Context.
81
+ * @param serviceKey - exact Cordis service key and default wire namespace.
82
+ * @param options - optional distinct wire namespace.
83
+ */
84
+ constructor(ctx, serviceKey, options = {}) {
85
+ super(ctx, serviceKey);
86
+ this.typertRemote = bindTypertRemote(this, this.name, options);
87
+ }
88
+ };
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");
97
+ }
98
+ if (context === void 0) throw new TypeError("typert-protocol: Remote decorator context is missing");
99
+ addMarkerInitializer(context, { kind: "direct" });
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
+ }
109
+ /**
110
+ * Create a decorator for a method resolved from one Remote Scope.
111
+ * @param key - scope key declared through the Context map.
112
+ * @param exportName - optional Remote export name; defaults to the method name.
113
+ * @returns a standard method decorator that records a versioned prototype descriptor.
114
+ */
115
+ function RemoteScope(key, exportName) {
116
+ validateName("Scope key", key);
117
+ if (exportName !== void 0) validateName("Remote export name", exportName);
118
+ return remoteDecorator({
119
+ kind: "context",
120
+ context: key
121
+ }, void 0, exportName);
122
+ }
123
+ /**
124
+ * Read Remote markers attached to a live Service's class prototype.
125
+ * The returned snapshot cannot mutate the stored descriptor.
126
+ * @param service - live Service instance.
127
+ * @returns markers in class declaration order.
128
+ */
129
+ function remoteMethods(service) {
130
+ const prototype = Object.getPrototypeOf(service);
131
+ if (prototype === null) return [];
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;
144
+ }
145
+ function addMarkerInitializer(context, invocation, mode, exportName) {
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");
147
+ const method = context.name;
148
+ context.addInitializer(function() {
149
+ const prototype = Object.getPrototypeOf(this);
150
+ if (prototype === null) throw new TypeError(`typert-protocol: cannot mark Remote method "${method}" on an object without a prototype`);
151
+ mark(prototype, method, invocation, mode, exportName);
152
+ });
153
+ }
154
+ function mark(prototype, method, invocation, mode, exportName) {
155
+ const descriptor = readRemoteMethodDescriptor(prototype);
156
+ const marker = Object.freeze({
157
+ method,
158
+ ...exportName === void 0 || exportName === method ? {} : { exportName },
159
+ ...mode === void 0 ? {} : { mode },
160
+ invocation: Object.freeze(invocation)
161
+ });
162
+ const current = descriptor?.methods.find((candidate) => candidate.method === method);
163
+ if (current !== void 0) {
164
+ if (current.exportName === marker.exportName && current.mode === marker.mode && sameInvocation(current.invocation, invocation)) return;
165
+ throw new Error(`typert-protocol: Remote method "${method}" has conflicting invocation markers`);
166
+ }
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
+ });
174
+ }
175
+ function sameInvocation(left, right) {
176
+ if (left.kind === "direct") return right.kind === "direct";
177
+ if (right.kind === "direct") return false;
178
+ return left.context === right.context;
179
+ }
180
+ function validateName(subject, value) {
181
+ if (!isTypertRemoteSegment(value)) throw new TypeError(`typert-protocol: ${subject} must contain only RPC endpoint segment characters`);
182
+ }
183
+ //#endregion
184
+ export { Remote, RemoteError, RemoteScope, TypertRemoteService, bindTypertRemote, isTypertRemoteSegment, remoteErrorOf, remoteMethods };
@@ -0,0 +1,97 @@
1
+ /**
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.
5
+ * @module @x1a0f3n9/dsh-typert-protocol
6
+ */
7
+ import { Service, type Context } from '@deepseek-ai/cordis';
8
+ import type { TypertContextMap } from './types.ts';
9
+ export { RemoteError, remoteErrorOf } from './remote-error.ts';
10
+ /**
11
+ * Test one generated Remote name against the Connection endpoint grammar.
12
+ * @param value - namespace, method, lookup, or Context segment.
13
+ * @returns whether the value can cross the shared RPC carrier unchanged.
14
+ */
15
+ export declare function isTypertRemoteSegment(value: string): boolean;
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';
17
+ /** Options for an explicit Service-to-Gateway binding. */
18
+ export interface TypertGatewayBindingOptions {
19
+ /** Wire namespace; defaults to the Cordis service key. */
20
+ readonly namespace?: string;
21
+ }
22
+ /** Visible declaration that one Service participates in Typert Gateway export. */
23
+ export interface TypertGatewayBinding<Service extends object = object> {
24
+ readonly service: Service;
25
+ readonly serviceKey: string;
26
+ readonly namespace: string;
27
+ }
28
+ /** Invocation mode recorded by a Remote method decorator. */
29
+ export type RemoteInvocationMarker = {
30
+ readonly kind: 'direct';
31
+ } | {
32
+ readonly kind: 'context';
33
+ readonly context: string;
34
+ };
35
+ /** One decorator marker discovered for a live Service instance. */
36
+ export interface RemoteMethodMarker {
37
+ /** Public instance method carrying the implementation. */
38
+ readonly method: string;
39
+ /** Endpoint method when it differs from the implementation member. */
40
+ readonly exportName?: string;
41
+ /** Stream methods yield many independently validated result items. */
42
+ readonly mode?: 'stream';
43
+ readonly invocation: RemoteInvocationMarker;
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
+ }
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;
51
+ /**
52
+ * Bind one visible Service field to a Cordis key and Remote namespace.
53
+ * @param service - owning Service instance, normally `this`.
54
+ * @param serviceKey - exact Cordis service key.
55
+ * @param options - optional distinct wire namespace.
56
+ * @returns a frozen, inspectable binding with no compiler-injected metadata.
57
+ */
58
+ export declare function bindTypertRemote<Service extends object>(service: Service, serviceKey: string, options?: TypertGatewayBindingOptions): TypertGatewayBinding<Service>;
59
+ /** Cordis Service base that exposes its registered name through Typert Gateway. */
60
+ export declare abstract class TypertRemoteService<out T = never> extends Service<T> {
61
+ /** Visible binding consumed by the Gateway's source-mode discovery. */
62
+ readonly typertRemote: TypertGatewayBinding<this>;
63
+ /**
64
+ * Register the Service and bind the same key to Typert Gateway.
65
+ * @param ctx - owning Cordis Context.
66
+ * @param serviceKey - exact Cordis service key and default wire namespace.
67
+ * @param options - optional distinct wire namespace.
68
+ */
69
+ protected constructor(ctx: Context, serviceKey: string, options?: TypertGatewayBindingOptions);
70
+ }
71
+ /**
72
+ * Mark one public instance method as a direct Remote invocation.
73
+ * @param _method - decorated method; retained only by the class itself.
74
+ * @param context - standard decorator context used to schedule private marking.
75
+ */
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;
77
+ /**
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.
80
+ * @returns a standard method decorator.
81
+ */
82
+ export declare function Remote(option: string | RemoteMethodOptions): RemoteMethodDecorator;
83
+ /**
84
+ * Create a decorator for a method resolved from one Remote Scope.
85
+ * @param key - scope key declared through the Context map.
86
+ * @param exportName - optional Remote export name; defaults to the method name.
87
+ * @returns a standard method decorator that records a versioned prototype descriptor.
88
+ */
89
+ export declare function RemoteScope(key: Extract<keyof TypertContextMap, string>, exportName?: string): RemoteMethodDecorator;
90
+ /**
91
+ * Read Remote markers attached to a live Service's class prototype.
92
+ * The returned snapshot cannot mutate the stored descriptor.
93
+ * @param service - live Service instance.
94
+ * @returns markers in class declaration order.
95
+ */
96
+ export declare function remoteMethods(service: object): readonly RemoteMethodMarker[];
97
+ //# sourceMappingURL=index.d.ts.map