@deepseek-ai/dsh-brand 0.1.2-alpha.2 → 0.1.2-alpha.4
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 +23 -9
- package/README.zh.md +23 -9
- package/lib/index.js +15 -7
- package/lib/types/index.d.ts +16 -6
- package/package.json +4 -11
- package/lib/invariant.js +0 -22
- package/lib/types/invariant.d.ts +0 -16
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:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: f9bcea11d223645a9bb1d2194a129f353220c684
|
|
6
|
+
README.zh.md: be9d8f828e05655b41b054a11221e382c90ef58b
|
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: "Nominal string types
|
|
2
|
+
description: "Nominal string and number types with stateless constructors for packages that own confusable domain values."
|
|
3
3
|
kind: "package-library"
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
|
|
|
9
9
|
|
|
10
10
|
## Summary
|
|
11
11
|
|
|
12
|
-
`dsh-brand` makes structurally identical strings non-interchangeable at the type level: a `SessionId` cannot be passed where a `ToolCallId` is expected
|
|
12
|
+
`dsh-brand` makes structurally identical strings or numbers non-interchangeable at the type level: a `SessionId` cannot be passed where a `ToolCallId` is expected, and an event sequence cannot be passed where a log offset is required. `brandString<T>()` and `brandNumber<T>()` apply nominal brands without shared runtime state, so owning packages can define domain types without importing an unrelated capability.
|
|
13
13
|
|
|
14
14
|
## Table of Contents
|
|
15
15
|
|
|
@@ -23,7 +23,7 @@ English | [中文](README.zh.md)
|
|
|
23
23
|
<a id="use-this-package"></a>
|
|
24
24
|
## Use this package
|
|
25
25
|
|
|
26
|
-
Brand
|
|
26
|
+
Brand a domain value when it crosses a package boundary and could plausibly be confused with another value represented by the same primitive; not every string or number needs a brand. A branded value is a contract for TypeScript callers: it enters only functions that expect its domain, and a different brand is rejected at compile time.
|
|
27
27
|
|
|
28
28
|
### Branding a string
|
|
29
29
|
|
|
@@ -39,9 +39,23 @@ const sessionId = brandString<SessionId>('session-1')
|
|
|
39
39
|
|
|
40
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
41
|
|
|
42
|
+
### Branding a number
|
|
43
|
+
|
|
44
|
+
Declare a numeric brand in its owning package and apply it only after that package admits the number:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { brandNumber, type BrandedNumber } from '@deepseek-ai/dsh-brand'
|
|
48
|
+
|
|
49
|
+
export type SessionSeq = BrandedNumber<'SessionSeq'>
|
|
50
|
+
|
|
51
|
+
const seq = brandNumber<SessionSeq>(7)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`brandNumber()` returns the original number and performs no validation. The owning package validates requirements such as non-negative safe-integer range before branding. Comparison, arithmetic, logging, JSON serialization, and wire transport retain ordinary number behavior; arithmetic produces an unbranded number that the owner must admit again before it re-enters the domain.
|
|
55
|
+
|
|
42
56
|
### When to brand
|
|
43
57
|
|
|
44
|
-
Brand
|
|
58
|
+
Brand values 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`, and `SessionSeq` versus `SessionLogOffset` in `dsh-session`. Values that stay local or cannot be confused do not need this abstraction.
|
|
45
59
|
|
|
46
60
|
-----
|
|
47
61
|
|
|
@@ -51,18 +65,18 @@ Brand ids that cross package boundaries and could plausibly be confused — `Too
|
|
|
51
65
|
<details>
|
|
52
66
|
<summary>Implementation internals — click to expand</summary>
|
|
53
67
|
|
|
54
|
-
The
|
|
68
|
+
The package defines two intersection types, `string & { readonly [BRAND]: B }` and `number & { readonly [BRAND]: B }`, where `BRAND` is a module-private `unique symbol`.
|
|
55
69
|
|
|
56
70
|
### Source map
|
|
57
71
|
|
|
58
72
|
| File | Role |
|
|
59
73
|
|---|---|
|
|
60
|
-
| [`src/index.ts`](src/index.ts) | Branded string
|
|
61
|
-
|
|
|
74
|
+
| [`src/index.ts`](src/index.ts) | Branded string and number types with stateless constructors |
|
|
75
|
+
| — | No runtime invariant companion is published; this pure utility owns no event stream or mutable runtime data; its value algebra is enforced by unit tests. |
|
|
62
76
|
|
|
63
77
|
### How values stay portable
|
|
64
78
|
|
|
65
|
-
The private symbol never exists at runtime: TypeScript erases it, so branded values have no tag or prototype. `brandString()`
|
|
79
|
+
The private symbol never exists at runtime: TypeScript erases it, so branded values have no tag or prototype. `brandString()` and `brandNumber()` return their inputs unchanged. Separate installed copies therefore produce interchangeable values without sharing a registry or constructor identity.
|
|
66
80
|
|
|
67
81
|
### Why it stays dependency-free
|
|
68
82
|
|
|
@@ -75,7 +89,7 @@ Keeping these helpers in their own package means `dsh-jobs` can brand `JobId` wi
|
|
|
75
89
|
<a id="further-exploration"></a>
|
|
76
90
|
## Further Exploration
|
|
77
91
|
|
|
78
|
-
Read these pages when you need the
|
|
92
|
+
Read these pages when you need the values these primitives brand or the type conventions around them.
|
|
79
93
|
|
|
80
94
|
- [Core subsystem](../../../docs/subsystems/core.md) — where the shared `SessionId` brand and the type rules are documented.
|
|
81
95
|
- [LSP subsystem](../../../docs/subsystems/lsp.md) — `LspProviderId`, a branded provider id built on this primitive.
|
package/README.zh.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: "
|
|
2
|
+
description: "供拥有易混淆领域值的包使用的名义字符串与数字类型及无状态构造函数。"
|
|
3
3
|
kind: "package-library"
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -9,7 +9,7 @@ kind: "package-library"
|
|
|
9
9
|
|
|
10
10
|
## 概述
|
|
11
11
|
|
|
12
|
-
`dsh-brand`
|
|
12
|
+
`dsh-brand` 让结构相同的字符串或数字在类型层面不可互换:`SessionId` 无法传给期望 `ToolCallId` 的位置,事件序号也无法传给需要日志偏移量的位置。`brandString<T>()` 与 `brandNumber<T>()` 在不持有共享运行时状态的情况下应用名义品牌,让所属包可以定义领域类型,而无需导入不相关的能力。
|
|
13
13
|
|
|
14
14
|
## 目录
|
|
15
15
|
|
|
@@ -23,7 +23,7 @@ kind: "package-library"
|
|
|
23
23
|
<a id="use-this-package"></a>
|
|
24
24
|
## 使用本包
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
当领域值跨越包边界,并可能与使用同一原语表示的另一个值混淆时,为其添加品牌;并非每个字符串或数字都需要品牌。品牌化值是给 TypeScript 调用方的约定:它只会进入期望该领域的函数,不同品牌会在编译期被拒绝。
|
|
27
27
|
|
|
28
28
|
### 为字符串添加品牌
|
|
29
29
|
|
|
@@ -39,9 +39,23 @@ const sessionId = brandString<SessionId>('session-1')
|
|
|
39
39
|
|
|
40
40
|
`brandString()` 只改变静态类型,不执行运行时校验。所属类型若有领域文法,应在调用前完成校验。添加品牌后,该 id 与普通字符串一样比较、记录日志、序列化为 JSON 和跨 wire 传输。
|
|
41
41
|
|
|
42
|
+
### 为数字添加品牌
|
|
43
|
+
|
|
44
|
+
在所属包中声明数字品牌,并且仅在该包准入数字之后应用品牌:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { brandNumber, type BrandedNumber } from '@deepseek-ai/dsh-brand'
|
|
48
|
+
|
|
49
|
+
export type SessionSeq = BrandedNumber<'SessionSeq'>
|
|
50
|
+
|
|
51
|
+
const seq = brandNumber<SessionSeq>(7)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`brandNumber()` 原样返回数字,不执行校验。所属包会在添加品牌前校验非负安全整数范围等要求。比较、算术、日志、JSON 序列化与 wire 传输保留普通数字行为;算术会产生未品牌化数字,所属包必须重新准入该数字,才能让它再次进入领域。
|
|
55
|
+
|
|
42
56
|
### 何时添加品牌
|
|
43
57
|
|
|
44
|
-
|
|
58
|
+
为跨包边界且可能被混淆的值添加品牌——`dsh-llm` 中的 `ToolCallId`、`dsh-session` 中共享的 agent/会话 `SessionId`、`dsh-jobs` 中的 `JobId`,以及 `dsh-session` 中的 `SessionSeq` 与 `SessionLogOffset`。保持局部或无法混淆的值不需要这种抽象。
|
|
45
59
|
|
|
46
60
|
-----
|
|
47
61
|
|
|
@@ -51,18 +65,18 @@ const sessionId = brandString<SessionId>('session-1')
|
|
|
51
65
|
<details>
|
|
52
66
|
<summary>实现细节——点击展开</summary>
|
|
53
67
|
|
|
54
|
-
|
|
68
|
+
该包定义两个交叉类型:`string & { readonly [BRAND]: B }` 与 `number & { readonly [BRAND]: B }`,其中 `BRAND` 是模块私有的 `unique symbol`。
|
|
55
69
|
|
|
56
70
|
### 源码地图
|
|
57
71
|
|
|
58
72
|
| 文件 | 职责 |
|
|
59
73
|
|---|---|
|
|
60
|
-
| [`src/index.ts`](src/index.ts) |
|
|
61
|
-
|
|
|
74
|
+
| [`src/index.ts`](src/index.ts) | 品牌化字符串与数字类型及其无状态构造函数 |
|
|
75
|
+
| — | 不发布运行时不变量伴生入口;擦除由编译器保证。 |
|
|
62
76
|
|
|
63
77
|
### 值为何可移植
|
|
64
78
|
|
|
65
|
-
私有 symbol 在运行时不存在:TypeScript 会将其擦除,因此品牌化值没有标签或 prototype。`brandString()`
|
|
79
|
+
私有 symbol 在运行时不存在:TypeScript 会将其擦除,因此品牌化值没有标签或 prototype。`brandString()` 与 `brandNumber()` 都原样返回输入。因此,彼此独立安装的副本无需共享注册表或 constructor identity,也会生成可互换的值。
|
|
66
80
|
|
|
67
81
|
### 为何保持无依赖
|
|
68
82
|
|
|
@@ -75,7 +89,7 @@ const sessionId = brandString<SessionId>('session-1')
|
|
|
75
89
|
<a id="further-exploration"></a>
|
|
76
90
|
## 进一步探索
|
|
77
91
|
|
|
78
|
-
|
|
92
|
+
当你需要这些原语所品牌化的值或围绕它们的类型约定时,阅读以下页面。
|
|
79
93
|
|
|
80
94
|
- [核心子系统](../../../docs/subsystems/core.zh.md)——共享 `SessionId` 品牌与类型规则的记录位置。
|
|
81
95
|
- [LSP 子系统](../../../docs/subsystems/lsp.zh.md)——构建在本原语之上的品牌化提供方 id `LspProviderId`。
|
package/lib/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
//#region lib/types/index.js
|
|
2
2
|
/**
|
|
3
|
-
* Duplicate-install-safe nominal
|
|
3
|
+
* Duplicate-install-safe nominal primitive helpers.
|
|
4
4
|
*
|
|
5
|
-
* A brand makes structurally
|
|
6
|
-
* level: a `SessionId` cannot be passed where a `ToolCallId` is
|
|
7
|
-
*
|
|
8
|
-
* serialization
|
|
5
|
+
* A brand makes structurally identical strings or numbers non-interchangeable
|
|
6
|
+
* at the type level: a `SessionId` cannot be passed where a `ToolCallId` is
|
|
7
|
+
* expected, and an event sequence cannot be passed as a log offset. Comparison,
|
|
8
|
+
* logging, and serialization retain the underlying primitive behavior.
|
|
9
9
|
*
|
|
10
|
-
* This package owns no concrete
|
|
10
|
+
* This package owns no concrete domain value and keeps no runtime identity or mutable
|
|
11
11
|
* state, so independently installed copies produce interchangeable values.
|
|
12
12
|
*
|
|
13
13
|
* @module @deepseek-ai/dsh-brand
|
|
@@ -20,5 +20,13 @@
|
|
|
20
20
|
function brandString(value) {
|
|
21
21
|
return value;
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Apply a compile-time number brand without changing the value.
|
|
25
|
+
* @param value - number admitted by the domain that owns the target brand.
|
|
26
|
+
* @returns the same number with the requested compile-time brand.
|
|
27
|
+
*/
|
|
28
|
+
function brandNumber(value) {
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
23
31
|
//#endregion
|
|
24
|
-
export { brandString };
|
|
32
|
+
export { brandNumber, brandString };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Duplicate-install-safe nominal
|
|
2
|
+
* Duplicate-install-safe nominal primitive helpers.
|
|
3
3
|
*
|
|
4
|
-
* A brand makes structurally
|
|
5
|
-
* level: a `SessionId` cannot be passed where a `ToolCallId` is
|
|
6
|
-
*
|
|
7
|
-
* serialization
|
|
4
|
+
* A brand makes structurally identical strings or numbers non-interchangeable
|
|
5
|
+
* at the type level: a `SessionId` cannot be passed where a `ToolCallId` is
|
|
6
|
+
* expected, and an event sequence cannot be passed as a log offset. Comparison,
|
|
7
|
+
* logging, and serialization retain the underlying primitive behavior.
|
|
8
8
|
*
|
|
9
|
-
* This package owns no concrete
|
|
9
|
+
* This package owns no concrete domain value and keeps no runtime identity or mutable
|
|
10
10
|
* state, so independently installed copies produce interchangeable values.
|
|
11
11
|
*
|
|
12
12
|
* @module @deepseek-ai/dsh-brand
|
|
@@ -16,11 +16,21 @@ declare const BRAND: unique symbol;
|
|
|
16
16
|
export type Branded<B extends string> = string & {
|
|
17
17
|
readonly [BRAND]: B;
|
|
18
18
|
};
|
|
19
|
+
/** A number carrying a compile-time-only brand `B`. */
|
|
20
|
+
export type BrandedNumber<B extends string> = number & {
|
|
21
|
+
readonly [BRAND]: B;
|
|
22
|
+
};
|
|
19
23
|
/**
|
|
20
24
|
* Apply a compile-time string brand without changing the value.
|
|
21
25
|
* @param value - string admitted by the domain that owns the target brand.
|
|
22
26
|
* @returns the same string with the requested compile-time brand.
|
|
23
27
|
*/
|
|
24
28
|
export declare function brandString<T extends Branded<string>>(value: string | T): T;
|
|
29
|
+
/**
|
|
30
|
+
* Apply a compile-time number brand without changing the value.
|
|
31
|
+
* @param value - number admitted by the domain that owns the target brand.
|
|
32
|
+
* @returns the same number with the requested compile-time brand.
|
|
33
|
+
*/
|
|
34
|
+
export declare function brandNumber<T extends BrandedNumber<string>>(value: number | T): T;
|
|
25
35
|
export {};
|
|
26
36
|
//# sourceMappingURL=index.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-brand",
|
|
3
|
-
"description": "Stateless branded
|
|
4
|
-
"version": "0.1.2-alpha.
|
|
3
|
+
"description": "Stateless branded primitive types for the DeepSeek Harness",
|
|
4
|
+
"version": "0.1.2-alpha.4",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -18,25 +18,18 @@
|
|
|
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
|
"./src/*": "./src/*",
|
|
26
22
|
"./package.json": "./package.json"
|
|
27
23
|
},
|
|
28
24
|
"files": [
|
|
29
25
|
"lib/index.js",
|
|
30
|
-
"lib/invariant.js",
|
|
31
26
|
"lib/types/**/*.d.ts"
|
|
32
27
|
],
|
|
33
28
|
"license": "MIT",
|
|
34
29
|
"peerDependencies": {
|
|
35
|
-
"@deepseek-ai/cordis": "^4.0.2"
|
|
36
|
-
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2"
|
|
30
|
+
"@deepseek-ai/cordis": "^4.0.2"
|
|
37
31
|
},
|
|
38
32
|
"devDependencies": {
|
|
39
|
-
"@deepseek-ai/cordis": "^4.0.2"
|
|
40
|
-
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2"
|
|
33
|
+
"@deepseek-ai/cordis": "^4.0.2"
|
|
41
34
|
}
|
|
42
35
|
}
|
package/lib/invariant.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
//#region lib/types/invariant.js
|
|
2
|
-
/**
|
|
3
|
-
* Package-owned invariant companion for `@deepseek-ai/dsh-brand`.
|
|
4
|
-
* @module @deepseek-ai/dsh-brand/invariant
|
|
5
|
-
*/
|
|
6
|
-
const PACKAGE_NAME = "@deepseek-ai/dsh-brand";
|
|
7
|
-
/** Cordis companion plugin name. */
|
|
8
|
-
const name = "brand-invariant";
|
|
9
|
-
/** Service required before the companion can reserve package ownership. */
|
|
10
|
-
const inject = ["invariants"];
|
|
11
|
-
/**
|
|
12
|
-
* No runtime invariant: this utility owns no event stream, shared identity, or mutable module state.
|
|
13
|
-
*/
|
|
14
|
-
const install = () => {};
|
|
15
|
-
/**
|
|
16
|
-
* Register this package's invariant companion.
|
|
17
|
-
* @param ctx - Cordis context carrying the invariant service.
|
|
18
|
-
* @returns the installed registration's disposer after setup succeeds.
|
|
19
|
-
*/
|
|
20
|
-
const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
21
|
-
//#endregion
|
|
22
|
-
export { apply, inject, name };
|
package/lib/types/invariant.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Package-owned invariant companion for `@deepseek-ai/dsh-brand`.
|
|
3
|
-
* @module @deepseek-ai/dsh-brand/invariant
|
|
4
|
-
*/
|
|
5
|
-
import type { Context } from '@deepseek-ai/cordis';
|
|
6
|
-
/** Cordis companion plugin name. */
|
|
7
|
-
export declare const name = "brand-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
|