@xneog/dsh-typert-registry 0.1.0 → 0.1.3-alpha.1

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 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/registry/README.md
5
- README.md: fa227b1c8faf1abd5a6492d4b8fe7d0c51ceeef1
6
- README.zh.md: b708e536acb7a5c4dc47e8c83180728e2ce150cd
5
+ README.md: e6133ed88ef15d99d09652b3c9b71dbbc7414f27
6
+ README.zh.md: da24ef689ca7767c8bac5f991958abd425f07dc0
package/README.md CHANGED
@@ -1,33 +1,131 @@
1
+ ---
2
+ description: "The runtime Typert registry: stores generated package reflection, live Zod schemas, and Remote invocation descriptors, and resolves them for consumers."
3
+ kind: "package-reference"
4
+ ---
5
+
1
6
  # @xneog/dsh-typert-registry
2
7
 
3
8
  English | [中文](README.zh.md)
4
9
 
5
- Runtime registry for generated Typert artifacts. A contribution carries one package face's business reflection and optional live Zod schemas; `ctx.typert` registers both atomically and withdraws them with the calling Cordis fiber. TypeScript analysis and code generation live in [`dsh-typert-generator`](../generator/README.md).
10
+ ## Summary
11
+
12
+ `dsh-typert-registry` makes generated Typert artifacts queryable at runtime: each package's reflection — services, events, and objects — its live Zod schemas, and Remote invocation descriptors live under stable keys that consumers can query or resolve on demand. Registrations are atomic and fiber-scoped: a contribution lands whole or not at all and is withdrawn automatically when the registering component unloads. The same service hosts the lookup and scoped-Context provider registries that Remote calls resolve through. It performs no TypeScript analysis and generates no schemas; the generator and the loader handle those.
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
+ Mount the registry in any Host or Client composition that stores or consumes generated Typert artifacts; it provides `ctx.typert`. There is no configuration.
29
+
30
+ ### Minimal setup
31
+
32
+ Load the registry plugin; the Client face is installed the same way by the Client runtime's own metadata, and both faces run the same implementation:
33
+
34
+ ```yaml
35
+ - name: '@xneog/dsh-typert-registry'
36
+ ```
37
+
38
+ ### Querying schemas and reflection
39
+
40
+ Consumers read schemas with `get(key)`, `resolve(key)`, or `list(filter?)` and package reflection with `getPackage(name, face?)` or `listPackages(filter?)`. `resolve()` distinguishes a malformed key, an absent package, and a registered package that contributes no schema under that name, each with its own error. `toJSONSchema(key)` projects a live Zod schema to JSON Schema without caching.
41
+
42
+ ### Registering a contribution
43
+
44
+ Generated artifacts register through the [loader](../loader/README.md) in Loader compositions; any other owner calls `ctx.typert.register(contribution)` directly and receives the exact disposer that withdraws it. Duplicate package-face identities, schema keys, invocation ids, or endpoints reject the whole batch before anything is committed.
45
+
46
+ ### Lookup and Context providers
47
+
48
+ Remote calls resolve Host objects and scoped Contexts through `ctx.typert.lookups` and `ctx.typert.contexts`. `registerHost()` installs one bidirectional Host Context adapter and its wire declaration, while `configureHost()` replaces only its resolver. `registerClient()` installs the bidirectional Client adapter for the same merge-declared kind. `identifyHost(ctx)` asks the Host adapters for the single kind and identity represented by a live Context and rejects ambiguous recognition.
49
+
50
+ -----
51
+
52
+ <a id="understand-the-implementation"></a>
53
+ ## Understand the implementation
54
+
55
+ <details>
56
+ <summary>Implementation internals — click to expand</summary>
6
57
 
7
- Package reflection is keyed by `<package>#<face>`. Schemas are keyed by `<package>#<name>` and retain the producer's Zod instance. JSON Schema is computed on demand at the consumer edge.
58
+ This section explains how the registry stores and owns contributions; the consumer API is covered in [Use this package](#use-this-package).
8
59
 
9
- ## Public API
60
+ ### Design concept
10
61
 
11
- - `TypertRegistry` is the default plugin and provides `ctx.typert`.
12
- - `ctx.typert.lookups.register()` registers the wire declaration and default resolver owned by the business package; `configure()` registers a resolver owned by Host composition that may run asynchronously. Their lifetimes are independent: configuration may precede the provider, and unloading the configuration restores the default policy.
13
- - `ctx.typert.contexts.registerHost()` and `configureHost()` apply the same ownership split to scoped Context identity; `registerClient()` supplies the corresponding Client Context binder.
14
- - `register(contribution)` rejects malformed identities and duplicate package-face or schema keys before committing anything, then returns the exact Cordis effect disposer.
15
- - `get(key)`, `resolve(key)`, and `list(filter?)` query live schemas. `resolve()` distinguishes a malformed key, an absent package, and a package that contributes no schema under that name.
16
- - `getPackage(packageName, face?)` and `listPackages(filter?)` query generated service, event, and object reflection; the default face is `host`.
17
- - `toJSONSchema(key, params?)` projects a live schema with `z.toJSONSchema()` without caching the result.
18
- - `typertKey()` and `typertPackageKey()` compose the two stable identity forms.
62
+ The registry is built on one principle: a contribution is one atomic, fiber-owned commit. `register()` validates the package-face identity, schemas, and invocation descriptors first, then commits everything under a single Cordis effect whose disposer withdraws exactly that contribution. Duplicate identities fail at the owning operation boundary before any state changes.
19
63
 
20
- The `@xneog/dsh-typert-registry/types` subpath contains the pure contribution and record contracts. [`dsh-typert-loader`](../loader/README.md) discovers and registers generated host artifacts in Loader compositions; direct `ctx.typert.register()` supports other composition owners.
64
+ ### Sub-registries
21
65
 
66
+ - `ctx.typert.local` — current-environment invocation definitions, including `hasSeen()` history for source-mode fallback.
67
+ - `ctx.typert.remotes` — consumer-selected contributions mounted in the calling fiber.
68
+ - `ctx.typert.lookups` — lookup providers plus composition-owned resolver overrides per key.
69
+ - `ctx.typert.contexts` — Host Context providers and Client Context binders per scoped key.
70
+
71
+ Each sub-registry publishes `TypertRegistryChange` events to subscribed listeners; a throwing listener is logged and does not stop later listeners.
72
+
73
+ ### Identity and validation
74
+
75
+ Keys are stable: `<package>#<face>` for reflection, `<package>#<name>` for schemas, and `<namespace>/<method>` for endpoints. Validation rejects names containing `#`, wire names outside the RPC segment grammar, duplicate keys, and lookup definitions whose wire declaration changes during the registry lifetime; strict codecs must carry a parseable schema.
76
+
77
+ ### Source map
78
+
79
+ | File | Role |
80
+ |---|---|
81
+ | [`src/service.ts`](src/service.ts) | `TypertRegistry` service, stores, validation, effect wiring |
82
+ | [`src/types.ts`](src/types.ts) | Contribution, record, and filter types |
83
+ | [`src/client/index.ts`](src/client/index.ts) | Client face installing the same registry |
84
+ | — | No runtime invariant companion is published; schema and package-reflection records mutate together inside register/dispose, with no independent event or second data source to cross-check; duplicate identities fail at the owning operation boundary. |
85
+
86
+ </details>
87
+
88
+ -----
89
+
90
+ <a id="further-exploration"></a>
91
+ ## Further Exploration
92
+
93
+ Read these pages when the package-level contract is not enough; they move from the registry to what feeds it and what consumes it.
94
+
95
+ - [Typert loader](../loader/README.md) — automatic registration of generated host artifacts.
96
+ - [Typert generator](../generator/README.md) — what produces the contributions the registry stores.
97
+ - [Typert protocol](../protocol/README.md) — the descriptors, codecs, and provider contracts the registry serves.
98
+ - [Typert subsystem reference](../../../docs/subsystems/typert.md) — the literal `ctx.typert` contract.
99
+ - [API Gateway reference](../../../docs/api-gateway.md) — the main consumer of invocation descriptors and providers.
100
+
101
+ -----
102
+
103
+ <a id="model-experience"></a>
22
104
  ## Model Experience
23
105
 
24
- None, as the registry contributes no prompt, tool, or session event; consumers such as `cordis_inspect` own any model-visible projection.
106
+ None, as this runtime type registry's consumers (cordis_inspect, wire faces, gates) own any model-visible projection of registry contents.
25
107
 
26
108
  #### KV Cache effect
27
109
 
28
- No direct effect. A consumer that places reflection in a request owns the resulting prefix change.
110
+ No direct effect; a consumer that places reflection or schemas in a request owns the resulting prefix change.
29
111
 
30
112
  ## Known Limitations and Deferred Work
31
113
 
32
- - The registry stores generated reflection but does not merge host and client graphs or resolve TypeScript references. Those are analyzer and emitter concerns.
33
- - Schema keys omit the face because host and client run in separate contexts. Registering same-named schemas from both faces into one context is rejected as a duplicate.
114
+ <a id="known-limitations-and-deferred-work"></a>
115
+
116
+
117
+ These limits define what the registry stores and rejects; they are current package constraints, not a task backlog.
118
+
119
+ - **No graph merging** — the registry stores generated reflection per face but does not merge host and client graphs or resolve TypeScript references; those are analyzer and emitter concerns.
120
+ - **Schema keys omit the face** — host and client run in separate contexts, so registering same-named schemas from both faces into one context is rejected as a duplicate.
121
+ - **JSON Schema projection is uncached** — `toJSONSchema()` returns a fresh document per call; consumers that project repeatedly own caching.
122
+
123
+ <a id="dev-note"></a>
124
+ ### Dev Note
125
+
126
+ <details>
127
+ <summary>Working context for maintainers — click to expand</summary>
128
+
129
+ None.
130
+
131
+ </details>
package/README.zh.md CHANGED
@@ -1,33 +1,131 @@
1
+ ---
2
+ description: "运行时 Typert 注册表:保存生成的包反射、实时 Zod schema 与 Remote 调用描述符,并按需为消费方解析。"
3
+ kind: "package-reference"
4
+ ---
5
+
1
6
  # @xneog/dsh-typert-registry
2
7
 
3
8
  [English](README.md) | 中文
4
9
 
5
- 生成的 Typert 产物所用的运行时注册表。每个注册项包含某个包在一个 face 上的业务反射信息,以及可选的运行时 Zod schema;`ctx.typert` 会以原子方式同时注册两者,并在发起调用的 Cordis fiber 释放时一并移除它们。TypeScript 分析和代码生成由 [`dsh-typert-generator`](../generator/README.md) 负责。
10
+ ## 概述
11
+
12
+ `dsh-typert-registry` 让生成的 Typert 产物在运行时可按需查询:每个包的反射——服务、事件与对象——其实时 Zod schema 与 Remote 调用描述符都保存在稳定键下,消费方可以按需查询或解析。注册是原子且按 fiber 作用域的:贡献要么整体落地要么完全不落地,并在注册组件卸载时自动撤销。同一服务还托管 Remote 调用所经由的 lookup 与作用域 Context 提供方注册表。它不执行 TypeScript 分析,也不生成 schema;这些由生成器与 loader 负责。
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
+ 在存储或消费生成 Typert 产物的任何 Host 或 Client 组合中挂载本注册表;它提供 `ctx.typert`。没有配置。
29
+
30
+ ### 最小设置
31
+
32
+ 加载注册表插件;Client face 由 Client 运行时自身的元数据以同样方式安装,两个 face 运行同一实现:
33
+
34
+ ```yaml
35
+ - name: '@xneog/dsh-typert-registry'
36
+ ```
37
+
38
+ ### 查询 schema 与反射
39
+
40
+ 消费方用 `get(key)`、`resolve(key)` 或 `list(filter?)` 读取 schema,用 `getPackage(name, face?)` 或 `listPackages(filter?)` 读取包反射。`resolve()` 能区分格式错误的键、未注册的包,以及已注册但未以该名称提供 schema 的包,各自给出不同的错误。`toJSONSchema(key)` 把实时 Zod schema 投影为 JSON Schema,且不缓存结果。
41
+
42
+ ### 注册贡献
43
+
44
+ 生成产物在 Loader 组合中通过 [loader](../loader/README.zh.md) 注册;其他所有者直接调用 `ctx.typert.register(contribution)`,并获得撤销它的同一资源释放函数。重复的包与 face 组合键、schema 键、调用 id 或端点会在提交任何内容之前拒绝整个批次。
45
+
46
+ ### Lookup 与 Context 提供方
47
+
48
+ Remote 调用通过 `ctx.typert.lookups` 与 `ctx.typert.contexts` 解析 Host 对象与作用域 Context。`registerHost()` 安装一个双向 Host Context adapter 及其 wire 声明,`configureHost()` 只替换其中的 resolver。`registerClient()` 为同一个 merge-declared kind 安装双向 Client adapter。`identifyHost(ctx)` 通过 Host adapter 识别活 Context 所代表的唯一 kind 与 identity,并拒绝歧义识别。
49
+
50
+ -----
51
+
52
+ <a id="understand-the-implementation"></a>
53
+ ## 理解实现
54
+
55
+ <details>
56
+ <summary>实现细节——点击展开</summary>
6
57
 
7
- 包反射信息以 `<package>#<face>` 为键。schema 以 `<package>#<name>` 为键,并保留生成方的 Zod 实例。系统按需在消费方边界计算 JSON Schema。
58
+ 本节解释注册表如何存储与拥有贡献;消费方 API 已在[使用本包](#use-this-package)中说明。
8
59
 
9
- ## 公开 API
60
+ ### 设计理念
10
61
 
11
- - `TypertRegistry` 是默认插件,并提供 `ctx.typert`。
12
- - `ctx.typert.lookups.register()` 注册由业务包拥有的协议声明和默认解析器;`configure()` 注册由宿主组合拥有且可异步执行的解析器。两者的生命周期相互独立:配置可以先于提供方注册,卸载配置会恢复默认策略。
13
- - `ctx.typert.contexts.registerHost()` 和 `configureHost()` 对具作用域的上下文身份采用同样的所有权划分;`registerClient()` 提供对应的客户端上下文绑定器。
14
- - `register(contribution)` 会在提交任何内容之前拒绝格式错误的标识,以及重复的包与 face 组合键或 schema 键,随后返回 Cordis effect 提供的同一资源释放函数。
15
- - `get(key)`、`resolve(key)` 和 `list(filter?)` 查询当前有效的 schema。`resolve()` 能区分格式错误的键、未注册的包,以及已注册但未以该名称提供 schema 的包。
16
- - `getPackage(packageName, face?)` 和 `listPackages(filter?)` 查询生成的服务、事件和对象反射信息;默认 face 为 `host`。
17
- - `toJSONSchema(key, params?)` 使用 `z.toJSONSchema()` 投影当前有效的 schema,且不缓存结果。
18
- - `typertKey()` 和 `typertPackageKey()` 构造两种稳定的标识形式。
62
+ 注册表建立在一个原则之上:贡献是一次原子、由 fiber 拥有的提交。`register()` 先校验包与 face 组合键、schema 与调用描述符,再在单个 Cordis effect 下提交全部内容,该 effect 的资源释放函数精确撤销这一贡献。重复标识在拥有该操作的所有权边界失败,此时任何状态都未改变。
19
63
 
20
- `@xneog/dsh-typert-registry/types` 子路径包含注册项和记录的纯类型约定。[`dsh-typert-loader`](../loader/README.md) 会在 Loader 组合中发现并注册生成的宿主侧产物;其他组合所有者可以直接调用 `ctx.typert.register()`。
64
+ ### 子注册表
21
65
 
66
+ - `ctx.typert.local`——当前环境的调用定义,含供源码模式回退使用的 `hasSeen()` 历史。
67
+ - `ctx.typert.remotes`——在调用方 fiber 中挂载的、由消费方选中的贡献。
68
+ - `ctx.typert.lookups`——lookup 提供方,以及按键配置的组合方解析器覆盖。
69
+ - `ctx.typert.contexts`——按作用域键配置的 Host Context 提供方与 Client Context 绑定器。
70
+
71
+ 每个子注册表都会向已订阅的监听器发布 `TypertRegistryChange` 事件;抛异常的监听器会被记录日志,且不会阻止后续监听器。
72
+
73
+ ### 标识与校验
74
+
75
+ 键是稳定的:反射用 `<package>#<face>`,schema 用 `<package>#<name>`,端点用 `<namespace>/<method>`。校验会拒绝含 `#` 的名称、超出 RPC 端点段文法的 wire 名称、重复键,以及在其注册表生命周期内改变 wire 声明的 lookup 定义;严格编解码器必须携带可解析的 schema。
76
+
77
+ ### 源码地图
78
+
79
+ | 文件 | 职责 |
80
+ |---|---|
81
+ | [`src/service.ts`](src/service.ts) | `TypertRegistry` 服务、存储、校验、effect 接线 |
82
+ | [`src/types.ts`](src/types.ts) | 贡献、记录与过滤器类型 |
83
+ | [`src/client/index.ts`](src/client/index.ts) | 安装同一注册表的 Client face |
84
+ | — | 不发布运行时不变式伴生入口;schema 与 package-reflection record 在 register/dispose 内一起变更,没有独立 event 或第二数据源可供交叉核对;重复 identity 在所属操作处失败。 |
85
+
86
+ </details>
87
+
88
+ -----
89
+
90
+ <a id="further-exploration"></a>
91
+ ## 进一步探索
92
+
93
+ 当包级约定不够用时阅读以下页面;它们从注册表逐步进入供给它与消费它的内容。
94
+
95
+ - [Typert loader](../loader/README.zh.md)——生成宿主产物的自动注册。
96
+ - [Typert 生成器](../generator/README.zh.md)——产生注册表所存贡献的包。
97
+ - [Typert 协议](../protocol/README.zh.md)——注册表所服务的描述符、编解码器与提供方约定。
98
+ - [Typert 子系统参考](../../../docs/subsystems/typert.zh.md)——字面的 `ctx.typert` 约定。
99
+ - [API Gateway 参考](../../../docs/api-gateway.zh.md)——调用描述符与提供方的主要消费方。
100
+
101
+ -----
102
+
103
+ <a id="model-experience"></a>
22
104
  ## 模型体验
23
105
 
24
- 无。注册表不会提供提示词、工具或会话事件;所有模型可见投影均由 `cordis_inspect` 等消费方负责。
106
+ 无,因为该运行时类型注册表的消费方(cordis_inspect、wire faces、门禁)拥有注册表内容的任何模型可见投影。
25
107
 
26
108
  #### KV Cache 影响
27
109
 
28
- 无直接影响。将反射信息放入请求的消费方负责由此产生的前缀变化。
110
+ 无直接影响;把反射或 schema 放入请求的消费方负责由此产生的前缀变化。
111
+
112
+ ## 已知限制与延期工作
113
+
114
+ <a id="known-limitations-and-deferred-work"></a>
115
+
116
+
117
+ 这些限制说明注册表存储与拒绝什么;它们是当前包约束,不是任务积压。
118
+
119
+ - **不合并图**——注册表按 face 存储生成的反射,但不合并宿主侧与客户端侧的图,也不解析 TypeScript 引用;这些是分析器与生成器的事。
120
+ - **schema 键不含 face**——宿主侧与客户端侧在不同上下文中运行,因此在同一上下文中注册来自两个 face 的同名 schema 会被作为重复项拒绝。
121
+ - **JSON Schema 投影不缓存**——`toJSONSchema()` 每次调用都返回全新文档;需要重复投影的消费方自行负责缓存。
122
+
123
+ <a id="dev-note"></a>
124
+ ### 开发备注
125
+
126
+ <details>
127
+ <summary>维护者的工作上下文——点击展开</summary>
29
128
 
30
- ## 已知限制与暂缓事项
129
+ 无。
31
130
 
32
- - 注册表存储生成的反射信息,但不会合并宿主侧与客户端侧的图,也不会解析 TypeScript 引用;这些由分析器和产物输出器负责。
33
- - schema 键不包含 face,因为宿主侧和客户端侧在不同的上下文中运行。若在同一上下文中注册来自两个 face 的同名 schema,系统会将其作为重复项拒绝。
131
+ </details>
package/lib/client.js CHANGED
@@ -1056,25 +1056,40 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
1056
1056
  }
1057
1057
  view(ctx) {
1058
1058
  return {
1059
- registerHost: (key, provider) => this.registerHost(ctx, key, provider),
1059
+ registerHost: (key, adapter) => this.registerHost(ctx, key, adapter),
1060
1060
  configureHost: (key, resolver) => this.configureHost(ctx, key, resolver),
1061
- registerClient: (key, binder) => this.registerClient(ctx, key, binder),
1061
+ registerClient: (key, adapter) => this.registerClient(ctx, key, adapter),
1062
+ identifyHost: (context) => this.identifyHost(context),
1062
1063
  getHost: (key) => this.getHost(key),
1063
1064
  getClient: (key) => this.clients.get(key)?.provider,
1064
1065
  subscribe: (listener) => this.changes.subscribe(ctx, listener)
1065
1066
  };
1066
1067
  }
1067
1068
  getHost(key) {
1068
- const provider = this.hosts.get(key)?.provider;
1069
- if (provider === void 0) return void 0;
1069
+ const adapter = this.hosts.get(key)?.provider;
1070
+ if (adapter === void 0) return void 0;
1070
1071
  const resolver = this.hostResolvers.get(key)?.provider;
1071
- if (resolver === void 0) return provider;
1072
+ if (resolver === void 0) return adapter;
1072
1073
  return {
1073
- wire: provider.wire,
1074
- wireTypeSymbol: provider.wireTypeSymbol,
1074
+ wire: adapter.wire,
1075
+ wireTypeSymbol: adapter.wireTypeSymbol,
1076
+ identity: (context) => adapter.identity(context),
1075
1077
  resolve: (id) => resolver.resolve(id)
1076
1078
  };
1077
1079
  }
1080
+ identifyHost(ctx) {
1081
+ let match;
1082
+ for (const key of this.hosts.keys()) {
1083
+ const identity = this.getHost(key)?.identity(ctx);
1084
+ if (identity === void 0) continue;
1085
+ if (match !== void 0) throw new Error(`typert: Host Context is recognized by both ${JSON.stringify(match.kind)} and ${JSON.stringify(key)}`);
1086
+ match = {
1087
+ kind: key,
1088
+ identity
1089
+ };
1090
+ }
1091
+ return match;
1092
+ }
1078
1093
  configureHost(ctx, key, resolver) {
1079
1094
  validateSegment("Context key", key);
1080
1095
  if (this.hostResolvers.has(key)) throw new Error(`typert: host-context "${key}" resolver is already configured`);
@@ -1100,15 +1115,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
1100
1115
  };
1101
1116
  }, `typert.contexts.configureHost(${JSON.stringify(key)})`);
1102
1117
  }
1103
- registerHost(ctx, key, provider) {
1118
+ registerHost(ctx, key, adapter) {
1104
1119
  validateSegment("Context key", key);
1105
- validateWireName("Context wire field", provider.wire);
1106
- validateNonempty("Context wire type symbol", provider.wireTypeSymbol);
1107
- return this.registerProvider(ctx, this.hosts, "host-context", key, provider);
1120
+ validateWireName("Context wire field", adapter.wire);
1121
+ validateNonempty("Context wire type symbol", adapter.wireTypeSymbol);
1122
+ return this.registerProvider(ctx, this.hosts, "host-context", key, adapter);
1108
1123
  }
1109
- registerClient(ctx, key, binder) {
1124
+ registerClient(ctx, key, adapter) {
1110
1125
  validateSegment("Context key", key);
1111
- return this.registerProvider(ctx, this.clients, "client-context", key, binder);
1126
+ return this.registerProvider(ctx, this.clients, "client-context", key, adapter);
1112
1127
  }
1113
1128
  registerProvider(ctx, table, kind, key, provider) {
1114
1129
  if (table.has(key)) throw new Error(`typert: ${kind} provider "${key}" is already registered`);
@@ -1176,7 +1191,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
1176
1191
  get lookups() {
1177
1192
  return this.lookupStore.view(this.ctx);
1178
1193
  }
1179
- /** Host Context providers and Client Context binders. */
1194
+ /** Host and Client Context adapters. */
1180
1195
  get contexts() {
1181
1196
  return this.contextStore.view(this.ctx);
1182
1197
  }
package/lib/index.js CHANGED
@@ -264,25 +264,40 @@ var ContextStore = class {
264
264
  }
265
265
  view(ctx) {
266
266
  return {
267
- registerHost: (key, provider) => this.registerHost(ctx, key, provider),
267
+ registerHost: (key, adapter) => this.registerHost(ctx, key, adapter),
268
268
  configureHost: (key, resolver) => this.configureHost(ctx, key, resolver),
269
- registerClient: (key, binder) => this.registerClient(ctx, key, binder),
269
+ registerClient: (key, adapter) => this.registerClient(ctx, key, adapter),
270
+ identifyHost: (context) => this.identifyHost(context),
270
271
  getHost: (key) => this.getHost(key),
271
272
  getClient: (key) => this.clients.get(key)?.provider,
272
273
  subscribe: (listener) => this.changes.subscribe(ctx, listener)
273
274
  };
274
275
  }
275
276
  getHost(key) {
276
- const provider = this.hosts.get(key)?.provider;
277
- if (provider === void 0) return void 0;
277
+ const adapter = this.hosts.get(key)?.provider;
278
+ if (adapter === void 0) return void 0;
278
279
  const resolver = this.hostResolvers.get(key)?.provider;
279
- if (resolver === void 0) return provider;
280
+ if (resolver === void 0) return adapter;
280
281
  return {
281
- wire: provider.wire,
282
- wireTypeSymbol: provider.wireTypeSymbol,
282
+ wire: adapter.wire,
283
+ wireTypeSymbol: adapter.wireTypeSymbol,
284
+ identity: (context) => adapter.identity(context),
283
285
  resolve: (id) => resolver.resolve(id)
284
286
  };
285
287
  }
288
+ identifyHost(ctx) {
289
+ let match;
290
+ for (const key of this.hosts.keys()) {
291
+ const identity = this.getHost(key)?.identity(ctx);
292
+ if (identity === void 0) continue;
293
+ if (match !== void 0) throw new Error(`typert: Host Context is recognized by both ${JSON.stringify(match.kind)} and ${JSON.stringify(key)}`);
294
+ match = {
295
+ kind: key,
296
+ identity
297
+ };
298
+ }
299
+ return match;
300
+ }
286
301
  configureHost(ctx, key, resolver) {
287
302
  validateSegment("Context key", key);
288
303
  if (this.hostResolvers.has(key)) throw new Error(`typert: host-context "${key}" resolver is already configured`);
@@ -308,15 +323,15 @@ var ContextStore = class {
308
323
  };
309
324
  }, `typert.contexts.configureHost(${JSON.stringify(key)})`);
310
325
  }
311
- registerHost(ctx, key, provider) {
326
+ registerHost(ctx, key, adapter) {
312
327
  validateSegment("Context key", key);
313
- validateWireName("Context wire field", provider.wire);
314
- validateNonempty("Context wire type symbol", provider.wireTypeSymbol);
315
- return this.registerProvider(ctx, this.hosts, "host-context", key, provider);
328
+ validateWireName("Context wire field", adapter.wire);
329
+ validateNonempty("Context wire type symbol", adapter.wireTypeSymbol);
330
+ return this.registerProvider(ctx, this.hosts, "host-context", key, adapter);
316
331
  }
317
- registerClient(ctx, key, binder) {
332
+ registerClient(ctx, key, adapter) {
318
333
  validateSegment("Context key", key);
319
- return this.registerProvider(ctx, this.clients, "client-context", key, binder);
334
+ return this.registerProvider(ctx, this.clients, "client-context", key, adapter);
320
335
  }
321
336
  registerProvider(ctx, table, kind, key, provider) {
322
337
  if (table.has(key)) throw new Error(`typert: ${kind} provider "${key}" is already registered`);
@@ -384,7 +399,7 @@ var TypertRegistry = class extends Service {
384
399
  get lookups() {
385
400
  return this.lookupStore.view(this.ctx);
386
401
  }
387
- /** Host Context providers and Client Context binders. */
402
+ /** Host and Client Context adapters. */
388
403
  get contexts() {
389
404
  return this.contextStore.view(this.ctx);
390
405
  }
@@ -47,7 +47,7 @@ export declare class TypertRegistry extends Service implements TypertRegistryCon
47
47
  get remotes(): TypertRemoteRegistry;
48
48
  /** Host object lookup providers. */
49
49
  get lookups(): TypertLookupRegistry;
50
- /** Host Context providers and Client Context binders. */
50
+ /** Host and Client Context adapters. */
51
51
  get contexts(): TypertContextRegistry;
52
52
  /**
53
53
  * Register one generated contribution atomically for the calling fiber.
@@ -267,27 +267,42 @@ class ContextStore {
267
267
  }
268
268
  view(ctx) {
269
269
  return {
270
- registerHost: (key, provider) => this.registerHost(ctx, key, provider),
270
+ registerHost: (key, adapter) => this.registerHost(ctx, key, adapter),
271
271
  configureHost: (key, resolver) => this.configureHost(ctx, key, resolver),
272
- registerClient: (key, binder) => this.registerClient(ctx, key, binder),
272
+ registerClient: (key, adapter) => this.registerClient(ctx, key, adapter),
273
+ identifyHost: context => this.identifyHost(context),
273
274
  getHost: key => this.getHost(key),
274
275
  getClient: key => this.clients.get(key)?.provider,
275
276
  subscribe: listener => this.changes.subscribe(ctx, listener),
276
277
  };
277
278
  }
278
279
  getHost(key) {
279
- const provider = this.hosts.get(key)?.provider;
280
- if (provider === undefined)
280
+ const adapter = this.hosts.get(key)?.provider;
281
+ if (adapter === undefined)
281
282
  return undefined;
282
283
  const resolver = this.hostResolvers.get(key)?.provider;
283
284
  if (resolver === undefined)
284
- return provider;
285
+ return adapter;
285
286
  return {
286
- wire: provider.wire,
287
- wireTypeSymbol: provider.wireTypeSymbol,
287
+ wire: adapter.wire,
288
+ wireTypeSymbol: adapter.wireTypeSymbol,
289
+ identity: context => adapter.identity(context),
288
290
  resolve: id => resolver.resolve(id),
289
291
  };
290
292
  }
293
+ identifyHost(ctx) {
294
+ let match;
295
+ for (const key of this.hosts.keys()) {
296
+ const identity = this.getHost(key)?.identity(ctx);
297
+ if (identity === undefined)
298
+ continue;
299
+ if (match !== undefined) {
300
+ throw new Error(`typert: Host Context is recognized by both ${JSON.stringify(match.kind)} and ${JSON.stringify(key)}`);
301
+ }
302
+ match = { kind: key, identity };
303
+ }
304
+ return match;
305
+ }
291
306
  configureHost(ctx, key, resolver) {
292
307
  validateSegment('Context key', key);
293
308
  if (this.hostResolvers.has(key))
@@ -309,15 +324,15 @@ class ContextStore {
309
324
  };
310
325
  }, `typert.contexts.configureHost(${JSON.stringify(key)})`);
311
326
  }
312
- registerHost(ctx, key, provider) {
327
+ registerHost(ctx, key, adapter) {
313
328
  validateSegment('Context key', key);
314
- validateWireName('Context wire field', provider.wire);
315
- validateNonempty('Context wire type symbol', provider.wireTypeSymbol);
316
- return this.registerProvider(ctx, this.hosts, 'host-context', key, provider);
329
+ validateWireName('Context wire field', adapter.wire);
330
+ validateNonempty('Context wire type symbol', adapter.wireTypeSymbol);
331
+ return this.registerProvider(ctx, this.hosts, 'host-context', key, adapter);
317
332
  }
318
- registerClient(ctx, key, binder) {
333
+ registerClient(ctx, key, adapter) {
319
334
  validateSegment('Context key', key);
320
- return this.registerProvider(ctx, this.clients, 'client-context', key, binder);
335
+ return this.registerProvider(ctx, this.clients, 'client-context', key, adapter);
321
336
  }
322
337
  registerProvider(ctx, table, kind, key, provider) {
323
338
  if (table.has(key))
@@ -378,7 +393,7 @@ export class TypertRegistry extends Service {
378
393
  get lookups() {
379
394
  return this.lookupStore.view(this.ctx);
380
395
  }
381
- /** Host Context providers and Client Context binders. */
396
+ /** Host and Client Context adapters. */
382
397
  get contexts() {
383
398
  return this.contextStore.view(this.ctx);
384
399
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xneog/dsh-typert-registry",
3
3
  "description": "Runtime registry for generated package reflection and Zod schemas",
4
- "version": "0.1.0",
4
+ "version": "0.1.3-alpha.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -18,10 +18,6 @@
18
18
  "types": "./lib/types/index.d.ts",
19
19
  "default": "./lib/index.js"
20
20
  },
21
- "./invariant": {
22
- "types": "./lib/types/invariant.d.ts",
23
- "default": "./lib/invariant.js"
24
- },
25
21
  "./client": {
26
22
  "types": "./lib/types/client/index.d.ts",
27
23
  "default": "./lib/client.js"
@@ -42,23 +38,20 @@
42
38
  },
43
39
  "files": [
44
40
  "lib/index.js",
45
- "lib/invariant.js",
46
41
  "lib/client.js",
47
42
  "lib/types/**/*.js",
48
43
  "lib/types/**/*.d.ts"
49
44
  ],
50
45
  "license": "MIT",
51
46
  "dependencies": {
52
- "@xneog/dsh-typert-protocol": "0.1.0",
53
47
  "zod": "^4.4.3"
54
48
  },
55
49
  "peerDependencies": {
56
- "@xneog/dsh-invariants": "0.1.0",
57
- "@xneog/cordis": "0.1.0"
50
+ "@xneog/cordis": "^4.0.2"
58
51
  },
59
52
  "devDependencies": {
60
- "@xneog/dsh-invariants": "0.1.0",
61
- "@xneog/cordis": "0.1.0"
53
+ "@xneog/cordis": "^4.0.2",
54
+ "@xneog/dsh-typert-protocol": "^0.1.3-alpha.1"
62
55
  },
63
56
  "scripts": {
64
57
  "bundle": "tsdown",
package/lib/invariant.js DELETED
@@ -1,24 +0,0 @@
1
- //#region lib/types/invariant.js
2
- /**
3
- * Package-owned invariant companion for `@xneog/dsh-typert-registry`.
4
- * @module @xneog/dsh-typert-registry/invariant
5
- */
6
- const PACKAGE_NAME = "@xneog/dsh-typert-registry";
7
- /** Cordis companion plugin name. */
8
- const name = "typert-registry-invariant";
9
- /** Service required before the companion can reserve package ownership. */
10
- const inject = ["invariants"];
11
- /**
12
- * No runtime invariant: schema and package-reflection records mutate together
13
- * inside register/dispose, with no independent event or second data source to
14
- * cross-check; duplicate identities fail at the owning operation boundary.
15
- */
16
- const install = () => {};
17
- /**
18
- * Register this package's invariant companion.
19
- * @param ctx - Cordis context carrying the invariant service.
20
- * @returns the installed registration's disposer after setup succeeds.
21
- */
22
- const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
23
- //#endregion
24
- export { apply, inject, name };
@@ -1,16 +0,0 @@
1
- /**
2
- * Package-owned invariant companion for `@xneog/dsh-typert-registry`.
3
- * @module @xneog/dsh-typert-registry/invariant
4
- */
5
- import type { Context } from '@xneog/cordis';
6
- /** Cordis companion plugin name. */
7
- export declare const name = "typert-registry-invariant";
8
- /** Service required before the companion can reserve package ownership. */
9
- export declare const inject: string[];
10
- /**
11
- * Register this package's invariant companion.
12
- * @param ctx - Cordis context carrying the invariant service.
13
- * @returns the installed registration's disposer after setup succeeds.
14
- */
15
- export declare const apply: (ctx: Context) => Promise<() => void>;
16
- //# sourceMappingURL=invariant.d.ts.map
@@ -1,23 +0,0 @@
1
- /**
2
- * Package-owned invariant companion for `@xneog/dsh-typert-registry`.
3
- * @module @xneog/dsh-typert-registry/invariant
4
- */
5
- const PACKAGE_NAME = '@xneog/dsh-typert-registry';
6
- /** Cordis companion plugin name. */
7
- export const name = 'typert-registry-invariant';
8
- /** Service required before the companion can reserve package ownership. */
9
- export const inject = ['invariants'];
10
- /**
11
- * No runtime invariant: schema and package-reflection records mutate together
12
- * inside register/dispose, with no independent event or second data source to
13
- * cross-check; duplicate identities fail at the owning operation boundary.
14
- */
15
- const install = () => { };
16
- /**
17
- * Register this package's invariant companion.
18
- * @param ctx - Cordis context carrying the invariant service.
19
- * @returns the installed registration's disposer after setup succeeds.
20
- */
21
- export const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
22
- /* jscpd:ignore-end */
23
- //# sourceMappingURL=invariant.js.map