@deepseek-ai/dsh-brand 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 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/util/brand/README.md
5
- README.md: ce05a652e2f863f737bf71fc473224fc16e25ab9
6
- README.zh.md: 07d1279b601d38d4dcf9ca10ddeccb3c3ed3547b
5
+ README.md: 646a3e781ce8540277259f4ffd67b78f3049160b
6
+ README.zh.md: 68290b35b64b4f519e2da06fc519d9d008e23646
package/README.md CHANGED
@@ -1,28 +1,94 @@
1
- # dsh-brand
1
+ ---
2
+ description: "Nominal string types and stateless constructors for packages that own identifiers crossing package boundaries."
3
+ kind: "package-library"
4
+ ---
5
+
6
+ # @deepseek-ai/dsh-brand
2
7
 
3
8
  English | [中文](README.zh.md)
4
9
 
5
- The `Branded<B>` nominal-typing primitive — a tiny, **type-only** package (no runtime code, no harness-package dependency) shared by every package that owns a cross-boundary id.
10
+ ## Summary
11
+
12
+ `dsh-brand` makes structurally identical strings non-interchangeable at the type level: a `SessionId` cannot be passed where a `ToolCallId` is expected even though both are plain strings at runtime. `brandString<T>()` applies a nominal brand to one domain-owned string without shared runtime state and lets capability packages own their concrete id types without importing an unrelated capability.
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
+ - [Dev Note](#dev-note)
20
+
21
+ -----
22
+
23
+ <a id="use-this-package"></a>
24
+ ## Use this package
25
+
26
+ Brand the ids a package owns when they cross a package boundary and could plausibly be confused with another package's ids; not every string needs a brand. A branded id is a contract for TypeScript callers: it only ever enters the functions that expect it, and an id from another package is rejected at compile time.
6
27
 
7
- ## What `Branded` is
28
+ ### Branding a string
8
29
 
9
- A brand makes structurally-identical strings non-interchangeable at the type level: a `SessionId` cannot be passed where a `CallId` is expected, even though both are plain `string`s at runtime.
30
+ Declare the branded type in the owning package and apply it at the point where that package admits a string:
10
31
 
11
32
  ```ts
12
- import type { Branded } from '@deepseek-ai/dsh-brand'
33
+ import { brandString, type Branded } from '@deepseek-ai/dsh-brand'
13
34
 
14
35
  export type SessionId = Branded<'SessionId'>
15
36
 
16
- /** Brand a string as a SessionId (a plain cast — zero runtime cost). */
17
- export function SessionId(id: string): SessionId {
18
- return id as SessionId
19
- }
37
+ const sessionId = brandString<SessionId>('session-1')
20
38
  ```
21
39
 
22
- Construction goes through the per-id factory in the owning package. Comparison, logging, JSON serialization, and the wire format behave as for an ordinary string; the brand is erased at compile time.
40
+ `brandString()` changes only the static type and performs no runtime validation. Validate domain grammar before calling it when the owning type has one. Once branded, the id compares, logs, serializes to JSON, and crosses the wire as an ordinary string.
41
+
42
+ ### When to brand
43
+
44
+ Brand ids that cross package boundaries and could plausibly be confused — `ToolCallId` in `dsh-llm`, the shared agent/session `SessionId` in `dsh-session`, `JobId` in `dsh-jobs`, `LspProviderId` in `dsh-lsp`. Strings that never leave their owning package do not need this abstraction.
45
+
46
+ -----
47
+
48
+ <a id="understand-the-implementation"></a>
49
+ ## Understand the implementation
50
+
51
+ <details>
52
+ <summary>Implementation internals — click to expand</summary>
53
+
54
+ The primitive is one intersection type: `string & { readonly [BRAND]: B }`, where `BRAND` is a module-private `unique symbol`.
55
+
56
+ ### Source map
57
+
58
+ | File | Role |
59
+ |---|---|
60
+ | [`src/index.ts`](src/index.ts) | Branded string type and its stateless constructor |
61
+ | [`src/invariant.ts`](src/invariant.ts) | Invariant companion (no runtime invariant; erasure is enforced by the compiler) |
62
+
63
+ ### How values stay portable
64
+
65
+ The private symbol never exists at runtime: TypeScript erases it, so branded values have no tag or prototype. `brandString()` returns its input unchanged. Separate installed copies therefore produce interchangeable values without sharing a registry or constructor identity.
66
+
67
+ ### Why it stays dependency-free
68
+
69
+ Keeping these helpers in their own package means `dsh-jobs` can brand `JobId` without importing an unrelated capability package, while each capability still owns the meaning and validation of its concrete ids.
70
+
71
+ </details>
72
+
73
+ -----
74
+
75
+ <a id="further-exploration"></a>
76
+ ## Further Exploration
77
+
78
+ Read these pages when you need the ids this primitive brands or the type conventions around it.
79
+
80
+ - [Core subsystem](../../../docs/subsystems/core.md) — where the shared `SessionId` brand and the type rules are documented.
81
+ - [LSP subsystem](../../../docs/subsystems/lsp.md) — `LspProviderId`, a branded provider id built on this primitive.
82
+ - [Jobs package](../../jobs/jobs/README.md) — the `JobId` brand owned by the jobs capability.
83
+
84
+ -----
85
+
86
+ <a id="dev-note"></a>
87
+ ## Dev Note
23
88
 
24
- ## Policy: brand ids that cross package boundaries
89
+ <details>
90
+ <summary>Working context for maintainers — click to expand</summary>
25
91
 
26
- A package brands the ids it owns — `CallId` in `dsh-llm`, the shared agent/session `SessionId` in `dsh-session`, and `JobId` in `dsh-jobs`. Brand cross-package ids that could plausibly be confused; not every string needs one.
92
+ None.
27
93
 
28
- This package owns only the primitive. Keeping it dependency-free lets `dsh-jobs`, for example, brand `JobId` without importing an unrelated capability package merely to reach `Branded`.
94
+ </details>
package/README.zh.md CHANGED
@@ -1,28 +1,94 @@
1
- # dsh-brand
1
+ ---
2
+ description: "供拥有跨包标识符的包使用的名义字符串类型与无状态构造函数。"
3
+ kind: "package-library"
4
+ ---
5
+
6
+ # @deepseek-ai/dsh-brand
2
7
 
3
8
  [English](README.md) | 中文
4
9
 
5
- `Branded<B>` 名义类型原语:一个微小的**仅类型**包,无运行时代码,也不依赖其他 harness 包;所有负责跨边界 id 的包都会共享它。
10
+ ## 概述
11
+
12
+ `dsh-brand` 让结构相同的字符串在类型层面不可互换:即使 `SessionId` 与 `ToolCallId` 在运行时都是普通字符串,前者也无法传给期望后者的位置。`brandString<T>()` 为领域拥有的字符串应用名义品牌且不持有共享运行时状态,让能力包可以拥有自己的具体 id 类型,而无需导入不相关的能力。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [进一步探索](#further-exploration)
19
+ - [开发备注](#dev-note)
20
+
21
+ -----
22
+
23
+ <a id="use-this-package"></a>
24
+ ## 使用本包
25
+
26
+ 当包拥有的 id 跨越包边界、并可能与其他包的 id 混淆时,为其添加品牌;并非每个字符串都需要品牌。品牌化 id 是给 TypeScript 调用方的约定:它只会进入期望它的函数,来自其他包的 id 会在编译期被拒绝。
6
27
 
7
- ## `Branded` 是什么
28
+ ### 为字符串添加品牌
8
29
 
9
- 品牌使 `SessionId` 和 `CallId` 这样结构相同的字符串在类型层面不可互换,尽管两者在运行时都是普通 `string`。
30
+ 在所属包中声明品牌化类型,并在该包准入字符串的位置应用品牌:
10
31
 
11
32
  ```ts
12
- import type { Branded } from '@deepseek-ai/dsh-brand'
33
+ import { brandString, type Branded } from '@deepseek-ai/dsh-brand'
13
34
 
14
35
  export type SessionId = Branded<'SessionId'>
15
36
 
16
- /** Brand a string as a SessionId (a plain cast — zero runtime cost). */
17
- export function SessionId(id: string): SessionId {
18
- return id as SessionId
19
- }
37
+ const sessionId = brandString<SessionId>('session-1')
20
38
  ```
21
39
 
22
- 构造操作通过所属包中各 id 专用的工厂完成。比较、日志记录、JSON 序列化和协议格式(wire format)的行为与普通字符串相同;品牌信息会在编译时被擦除。
40
+ `brandString()` 只改变静态类型,不执行运行时校验。所属类型若有领域文法,应在调用前完成校验。添加品牌后,该 id 与普通字符串一样比较、记录日志、序列化为 JSON 和跨 wire 传输。
41
+
42
+ ### 何时添加品牌
43
+
44
+ 为跨包边界且可能被混淆的 id 添加品牌——`dsh-llm` 中的 `ToolCallId`、`dsh-session` 中共享的 agent/会话 `SessionId`、`dsh-jobs` 中的 `JobId`、`dsh-lsp` 中的 `LspProviderId`。从不离开所属包的字符串不需要这种抽象。
45
+
46
+ -----
47
+
48
+ <a id="understand-the-implementation"></a>
49
+ ## 理解实现
50
+
51
+ <details>
52
+ <summary>实现细节——点击展开</summary>
53
+
54
+ 该原语是一个交叉类型:`string & { readonly [BRAND]: B }`,其中 `BRAND` 是模块私有的 `unique symbol`。
55
+
56
+ ### 源码地图
57
+
58
+ | 文件 | 职责 |
59
+ |---|---|
60
+ | [`src/index.ts`](src/index.ts) | 品牌化字符串类型及其无状态构造函数 |
61
+ | [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件(无运行时不变式;擦除由编译器保证) |
62
+
63
+ ### 值为何可移植
64
+
65
+ 私有 symbol 在运行时不存在:TypeScript 会将其擦除,因此品牌化值没有标签或 prototype。`brandString()` 原样返回输入。因此,彼此独立安装的副本无需共享注册表或 constructor identity,也会生成可互换的值。
66
+
67
+ ### 为何保持无依赖
68
+
69
+ 把这些 helper 放在独立包中,意味着 `dsh-jobs` 可以为 `JobId` 添加品牌,而无需导入不相关的能力包;每个能力仍然拥有其具体 id 的含义与校验。
70
+
71
+ </details>
72
+
73
+ -----
74
+
75
+ <a id="further-exploration"></a>
76
+ ## 进一步探索
77
+
78
+ 当你需要本原语所品牌化的 id 或围绕它的类型约定时,阅读以下页面。
79
+
80
+ - [核心子系统](../../../docs/subsystems/core.zh.md)——共享 `SessionId` 品牌与类型规则的记录位置。
81
+ - [LSP 子系统](../../../docs/subsystems/lsp.zh.md)——构建在本原语之上的品牌化提供方 id `LspProviderId`。
82
+ - [jobs 包](../../jobs/jobs/README.zh.md)——由 jobs 能力拥有的 `JobId` 品牌。
83
+
84
+ -----
85
+
86
+ <a id="dev-note"></a>
87
+ ## 开发备注
23
88
 
24
- ## 策略:为跨包边界的 id 添加品牌
89
+ <details>
90
+ <summary>维护者的工作上下文——点击展开</summary>
25
91
 
26
- 包为自己拥有的 id 添加品牌:`CallId` 位于 `dsh-llm`,共享的 agent/会话 `SessionId` 位于 `dsh-session`,`JobId` 位于 `dsh-jobs`。为可能被混淆的跨包 id 添加品牌,但无需为每个字符串都添加。
92
+ 无。
27
93
 
28
- 该包只负责这一原语。保持无依赖意味着,例如 `dsh-jobs` 可以为 `JobId` 使用品牌类型,而无需仅为使用 `Branded` 而导入不相关的功能包。
94
+ </details>
package/lib/index.js CHANGED
@@ -1 +1,24 @@
1
- export {};
1
+ //#region lib/types/index.js
2
+ /**
3
+ * Duplicate-install-safe nominal string helpers.
4
+ *
5
+ * A brand makes structurally-identical strings non-interchangeable at the type
6
+ * level: a `SessionId` cannot be passed where a `ToolCallId` is expected, even
7
+ * though both are plain strings at runtime. Comparison, logging, and
8
+ * serialization all behave as ordinary strings.
9
+ *
10
+ * This package owns no concrete id and keeps no runtime identity or mutable
11
+ * state, so independently installed copies produce interchangeable values.
12
+ *
13
+ * @module @deepseek-ai/dsh-brand
14
+ */
15
+ /**
16
+ * Apply a compile-time string brand without changing the value.
17
+ * @param value - string admitted by the domain that owns the target brand.
18
+ * @returns the same string with the requested compile-time brand.
19
+ */
20
+ function brandString(value) {
21
+ return value;
22
+ }
23
+ //#endregion
24
+ export { brandString };
package/lib/invariant.js CHANGED
@@ -9,8 +9,7 @@ const name = "brand-invariant";
9
9
  /** Service required before the companion can reserve package ownership. */
10
10
  const inject = ["invariants"];
11
11
  /**
12
- * No runtime invariant: this pure utility owns no event stream or mutable runtime data; its value
13
- * algebra is enforced by unit tests.
12
+ * No runtime invariant: this utility owns no event stream, shared identity, or mutable module state.
14
13
  */
15
14
  const install = () => {};
16
15
  /**
@@ -1,22 +1,13 @@
1
1
  /**
2
- * The `Branded<B>` nominal-typing primitive — a type-only utility (no runtime
3
- * code, no harness-package dependency) shared by every package that owns a
4
- * cross-boundary id.
2
+ * Duplicate-install-safe nominal string helpers.
5
3
  *
6
4
  * A brand makes structurally-identical strings non-interchangeable at the type
7
- * level: a `SessionId` cannot be passed where a `CallId` is expected, even
8
- * though both are plain strings at runtime. Construction goes through a per-id
9
- * factory in the OWNING package (a plain cast inside — zero runtime cost);
10
- * comparison, logging, and serialization all behave as ordinary strings.
5
+ * level: a `SessionId` cannot be passed where a `ToolCallId` is expected, even
6
+ * though both are plain strings at runtime. Comparison, logging, and
7
+ * serialization all behave as ordinary strings.
11
8
  *
12
- * Policy: a package brands the ids it owns — `CallId` in dsh-llm (tool-call
13
- * correlation), the shared agent/session `SessionId` in dsh-session, and
14
- * `JobId` in dsh-jobs. Branding is for ids that cross package boundaries and
15
- * could plausibly be confused; not every string needs a brand.
16
- * This package owns ONLY the primitive — no concrete id, no runtime code beyond
17
- * the (erased) type — so the brand vocabulary stays dependency-free and a
18
- * package can brand its ids without depending on an unrelated capability
19
- * package.
9
+ * This package owns no concrete id and keeps no runtime identity or mutable
10
+ * state, so independently installed copies produce interchangeable values.
20
11
  *
21
12
  * @module @deepseek-ai/dsh-brand
22
13
  */
@@ -25,5 +16,11 @@ declare const BRAND: unique symbol;
25
16
  export type Branded<B extends string> = string & {
26
17
  readonly [BRAND]: B;
27
18
  };
19
+ /**
20
+ * Apply a compile-time string brand without changing the value.
21
+ * @param value - string admitted by the domain that owns the target brand.
22
+ * @returns the same string with the requested compile-time brand.
23
+ */
24
+ export declare function brandString<T extends Branded<string>>(value: string | T): T;
28
25
  export {};
29
26
  //# sourceMappingURL=index.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-brand",
3
- "description": "Type-only Branded<B> nominal-typing primitive for the DeepSeek Harness",
4
- "version": "0.1.1-rc.2",
3
+ "description": "Stateless branded-string primitives for the DeepSeek Harness",
4
+ "version": "0.1.2-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -32,11 +32,11 @@
32
32
  ],
33
33
  "license": "MIT",
34
34
  "peerDependencies": {
35
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
36
- "@deepseek-ai/cordis": "^4.0.1"
35
+ "@deepseek-ai/cordis": "^4.0.2",
36
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2"
37
37
  },
38
38
  "devDependencies": {
39
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
40
- "@deepseek-ai/cordis": "^4.0.1"
39
+ "@deepseek-ai/cordis": "^4.0.2",
40
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2"
41
41
  }
42
42
  }