dsh-unitverse 0.1.0

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.
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Unit tables for dsh-unitverse.
3
+ *
4
+ * Every unit belongs to exactly one of the ten supported categories and is
5
+ * expressed relative to that category's base unit:
6
+ *
7
+ * - length -> meter (m)
8
+ * - area -> square meter (m²)
9
+ * - volume -> cubic meter (m³)
10
+ * - time -> second (s)
11
+ * - angle -> radian (rad)
12
+ * - speed -> meter per second (m/s)
13
+ * - temperature-> kelvin (K), affine: base = a * value + b
14
+ * - pressure -> pascal (Pa)
15
+ * - energy -> joule (J)
16
+ * - power -> watt (W)
17
+ *
18
+ * Notes on chosen conventions (documented in the READMEs):
19
+ * - 1 cal = 4.184 J (thermochemical calorie); 1 kcal = 4184 J.
20
+ * - 1 BTU = 1055.05585262 J (International Table).
21
+ * - Year/month use the Julian mean: year = 365.25 d, month = year / 12.
22
+ * - 1 mmHg = 133.322387415 Pa (conventional at 0 °C);
23
+ * 1 torr = 101325/760 Pa. Both are provided.
24
+ */
25
+ type CategoryId = 'length' | 'area' | 'volume' | 'time' | 'angle' | 'speed' | 'temperature' | 'pressure' | 'energy' | 'power';
26
+ interface CategoryMeta {
27
+ id: CategoryId;
28
+ labelZh: string;
29
+ labelEn: string;
30
+ baseSymbol: string;
31
+ }
32
+ declare const CATEGORY_ORDER: CategoryId[];
33
+ declare const CATEGORIES: Record<CategoryId, CategoryMeta>;
34
+ /**
35
+ * One unit definition.
36
+ *
37
+ * Linear units carry `factor`: `valueInBase = value * factor`.
38
+ * Temperature units instead carry `a` and `b` (affine):
39
+ * `kelvin = value * a + b`, which supports both absolute conversions and
40
+ * temperature differences (delta) where only `a` matters.
41
+ */
42
+ interface UnitDef {
43
+ /** Canonical machine id, e.g. `km` or `gal_us`. */
44
+ id: string;
45
+ category: CategoryId;
46
+ /** Preferred display symbol, e.g. `km`, `°C`, `km/h`. */
47
+ symbol: string;
48
+ /** Chinese display name, e.g. `千米`. */
49
+ nameZh: string;
50
+ /** English display name, e.g. `kilometer`. */
51
+ nameEn: string;
52
+ /** Every accepted spelling (already normalized, see {@link normalizeUnit}). */
53
+ aliases: readonly string[];
54
+ /** Linear multiplier to the base unit. */
55
+ factor?: number;
56
+ /** Temperature slope: kelvin = value * a + b. */
57
+ a?: number;
58
+ /** Temperature offset: kelvin = value * a + b. */
59
+ b?: number;
60
+ }
61
+ /** Definition of the delta (difference) temperature marker. */
62
+ declare const DELTA_MARKERS: readonly ["Δ", "delta", "diff"];
63
+ declare const UNITS: readonly UnitDef[];
64
+ /** Normalize a raw unit token for alias lookup. */
65
+ declare function normalizeUnit(raw: string): string;
66
+ interface AliasIndex {
67
+ map: Map<string, UnitDef>;
68
+ /** Alias -> list of units that claimed it (non-empty only when ambiguous). */
69
+ conflicts: Map<string, UnitDef[]>;
70
+ }
71
+ /** Build an alias lookup index and report duplicate aliases instead of hiding them. */
72
+ declare function buildAliasIndex(units?: readonly UnitDef[]): AliasIndex;
73
+ /** Shared alias index. */
74
+ declare const ALIAS_INDEX: AliasIndex;
75
+ /** Look up a unit by any alias/name/symbol, or `undefined`. */
76
+ declare function findUnit(token: string): UnitDef | undefined;
77
+ /** All units of one category (in declaration order). */
78
+ declare function unitsByCategory(category: CategoryId): readonly UnitDef[];
79
+ /** True when a token carries a temperature-delta marker (`Δ°C`). */
80
+ declare function stripDeltaMarker(token: string): {
81
+ base: string;
82
+ delta: boolean;
83
+ };
84
+
85
+ /**
86
+ * Pure conversion engine for dsh-unitverse.
87
+ *
88
+ * This module has no DeepSeek-Harness dependency on purpose: it is a plain
89
+ * TypeScript library that the DSH plugin entry (`src/index.ts`) wraps as a
90
+ * model-facing tool, and that tests exercise directly.
91
+ */
92
+
93
+ /** Options for {@link convert}. */
94
+ interface ConvertOptions {
95
+ /**
96
+ * Round the result to this many significant digits (default 10).
97
+ * Pass `undefined`/omit for the default.
98
+ */
99
+ significantDigits?: number;
100
+ }
101
+ /** Normalized, resolved unit identity after parsing a token. */
102
+ interface ResolvedUnit {
103
+ def: UnitDef;
104
+ /** Whether the caller asked for a temperature *difference* (Δ) conversion. */
105
+ delta: boolean;
106
+ /** Canonical spellings resolved from the input, e.g. `千米`. */
107
+ canonical: string;
108
+ }
109
+ /** A validated conversion request after both units parsed. */
110
+ interface ConvertRequest {
111
+ value: number;
112
+ from: ResolvedUnit;
113
+ to: ResolvedUnit;
114
+ }
115
+ /** Result of {@link convertDetailed}. */
116
+ interface ConvertResult {
117
+ /** Original numeric value. */
118
+ value: number;
119
+ /** Canonical from-unit display name (resolved input spelling). */
120
+ from: string;
121
+ /** Canonical to-unit display name. */
122
+ to: string;
123
+ /** Converted value. */
124
+ result: number;
125
+ /** Category id shared by both units, e.g. `length`. */
126
+ category: CategoryId;
127
+ /** Category display name (Chinese), e.g. `长度`. */
128
+ categoryZh: string;
129
+ /** Category display name (English), e.g. `Length`. */
130
+ categoryEn: string;
131
+ /** Input tokens exactly as the caller passed them. */
132
+ fromInput: string;
133
+ /** Input tokens exactly as the caller passed them. */
134
+ toInput: string;
135
+ /** Human summary line, e.g. `100 千米 = 62.13711922 英里 (长度)`. */
136
+ summary: string;
137
+ }
138
+ /** Error thrown when a conversion request is invalid. */
139
+ declare class UnitConversionError extends Error {
140
+ /** Machine-readable kind: `unknown-unit` | `category-mismatch` | `bad-value` | `mixed-delta` | `unsupported`. */
141
+ readonly kind: 'unknown-unit' | 'category-mismatch' | 'bad-value' | 'mixed-delta' | 'unsupported';
142
+ /** The offending raw token for `unknown-unit` errors. */
143
+ readonly token?: string;
144
+ constructor(kind: UnitConversionError['kind'], message: string, token?: string);
145
+ }
146
+ /**
147
+ * Parse and resolve one unit token. Supports case-insensitive matches
148
+ * against symbols, English names and Chinese names, plus an optional
149
+ * temperature-delta marker (`Δ°C`, `deltaC`, `ΔK`, ...).
150
+ */
151
+ declare function resolveUnit(token: string): ResolvedUnit;
152
+ /**
153
+ * Parse both unit tokens and verify they belong to the same category.
154
+ * `from`/`to` may be the same unit (returns the value unchanged).
155
+ */
156
+ declare function parseRequest(value: number, from: string, to: string): ConvertRequest;
157
+ /**
158
+ * Round to `digits` significant digits (default 10). Implemented through
159
+ * `toPrecision`, which stays finite across the full double range (a naive
160
+ * `10 ** exponent` overflows for very small/large magnitudes).
161
+ */
162
+ declare function roundSignificant(value: number, digits?: number): number;
163
+ /** Convert `value` from unit `from` to unit `to`. */
164
+ declare function convertRaw(value: number, from: ResolvedUnit, to: ResolvedUnit): number;
165
+ /**
166
+ * Convert a numeric value between two units of the same category.
167
+ *
168
+ * ```ts
169
+ * convert(100, 'km', 'mi') // 62.13711922
170
+ * convert(25, '°C', '°F') // 77
171
+ * convert(1, 'Δ°C', '°F') // 1.8 (temperature difference)
172
+ * convert(3, '公里/小时', 'm/s') // 0.8333333333
173
+ * ```
174
+ *
175
+ * @param value - the numeric amount to convert
176
+ * @param from - source unit token (symbol, English or Chinese name)
177
+ * @param to - target unit token
178
+ * @param options - {@link ConvertOptions}
179
+ * @throws {@link UnitConversionError} on unknown units, cross-category
180
+ * requests, or non-finite values.
181
+ */
182
+ declare function convert(value: number, from: string, to: string, options?: ConvertOptions): number;
183
+ /** Convert and return a structured {@link ConvertResult} including a summary. */
184
+ declare function convertDetailed(value: number, from: string, to: string, options?: ConvertOptions): ConvertResult;
185
+
186
+ export { ALIAS_INDEX as A, CATEGORIES as C, DELTA_MARKERS as D, type ResolvedUnit as R, UNITS as U, type AliasIndex as a, CATEGORY_ORDER as b, type CategoryId as c, type CategoryMeta as d, type ConvertOptions as e, type ConvertRequest as f, type ConvertResult as g, UnitConversionError as h, type UnitDef as i, buildAliasIndex as j, convert as k, convertDetailed as l, convertRaw as m, findUnit as n, normalizeUnit as o, parseRequest as p, roundSignificant as q, resolveUnit as r, stripDeltaMarker as s, unitsByCategory as u };
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Unit tables for dsh-unitverse.
3
+ *
4
+ * Every unit belongs to exactly one of the ten supported categories and is
5
+ * expressed relative to that category's base unit:
6
+ *
7
+ * - length -> meter (m)
8
+ * - area -> square meter (m²)
9
+ * - volume -> cubic meter (m³)
10
+ * - time -> second (s)
11
+ * - angle -> radian (rad)
12
+ * - speed -> meter per second (m/s)
13
+ * - temperature-> kelvin (K), affine: base = a * value + b
14
+ * - pressure -> pascal (Pa)
15
+ * - energy -> joule (J)
16
+ * - power -> watt (W)
17
+ *
18
+ * Notes on chosen conventions (documented in the READMEs):
19
+ * - 1 cal = 4.184 J (thermochemical calorie); 1 kcal = 4184 J.
20
+ * - 1 BTU = 1055.05585262 J (International Table).
21
+ * - Year/month use the Julian mean: year = 365.25 d, month = year / 12.
22
+ * - 1 mmHg = 133.322387415 Pa (conventional at 0 °C);
23
+ * 1 torr = 101325/760 Pa. Both are provided.
24
+ */
25
+ type CategoryId = 'length' | 'area' | 'volume' | 'time' | 'angle' | 'speed' | 'temperature' | 'pressure' | 'energy' | 'power';
26
+ interface CategoryMeta {
27
+ id: CategoryId;
28
+ labelZh: string;
29
+ labelEn: string;
30
+ baseSymbol: string;
31
+ }
32
+ declare const CATEGORY_ORDER: CategoryId[];
33
+ declare const CATEGORIES: Record<CategoryId, CategoryMeta>;
34
+ /**
35
+ * One unit definition.
36
+ *
37
+ * Linear units carry `factor`: `valueInBase = value * factor`.
38
+ * Temperature units instead carry `a` and `b` (affine):
39
+ * `kelvin = value * a + b`, which supports both absolute conversions and
40
+ * temperature differences (delta) where only `a` matters.
41
+ */
42
+ interface UnitDef {
43
+ /** Canonical machine id, e.g. `km` or `gal_us`. */
44
+ id: string;
45
+ category: CategoryId;
46
+ /** Preferred display symbol, e.g. `km`, `°C`, `km/h`. */
47
+ symbol: string;
48
+ /** Chinese display name, e.g. `千米`. */
49
+ nameZh: string;
50
+ /** English display name, e.g. `kilometer`. */
51
+ nameEn: string;
52
+ /** Every accepted spelling (already normalized, see {@link normalizeUnit}). */
53
+ aliases: readonly string[];
54
+ /** Linear multiplier to the base unit. */
55
+ factor?: number;
56
+ /** Temperature slope: kelvin = value * a + b. */
57
+ a?: number;
58
+ /** Temperature offset: kelvin = value * a + b. */
59
+ b?: number;
60
+ }
61
+ /** Definition of the delta (difference) temperature marker. */
62
+ declare const DELTA_MARKERS: readonly ["Δ", "delta", "diff"];
63
+ declare const UNITS: readonly UnitDef[];
64
+ /** Normalize a raw unit token for alias lookup. */
65
+ declare function normalizeUnit(raw: string): string;
66
+ interface AliasIndex {
67
+ map: Map<string, UnitDef>;
68
+ /** Alias -> list of units that claimed it (non-empty only when ambiguous). */
69
+ conflicts: Map<string, UnitDef[]>;
70
+ }
71
+ /** Build an alias lookup index and report duplicate aliases instead of hiding them. */
72
+ declare function buildAliasIndex(units?: readonly UnitDef[]): AliasIndex;
73
+ /** Shared alias index. */
74
+ declare const ALIAS_INDEX: AliasIndex;
75
+ /** Look up a unit by any alias/name/symbol, or `undefined`. */
76
+ declare function findUnit(token: string): UnitDef | undefined;
77
+ /** All units of one category (in declaration order). */
78
+ declare function unitsByCategory(category: CategoryId): readonly UnitDef[];
79
+ /** True when a token carries a temperature-delta marker (`Δ°C`). */
80
+ declare function stripDeltaMarker(token: string): {
81
+ base: string;
82
+ delta: boolean;
83
+ };
84
+
85
+ /**
86
+ * Pure conversion engine for dsh-unitverse.
87
+ *
88
+ * This module has no DeepSeek-Harness dependency on purpose: it is a plain
89
+ * TypeScript library that the DSH plugin entry (`src/index.ts`) wraps as a
90
+ * model-facing tool, and that tests exercise directly.
91
+ */
92
+
93
+ /** Options for {@link convert}. */
94
+ interface ConvertOptions {
95
+ /**
96
+ * Round the result to this many significant digits (default 10).
97
+ * Pass `undefined`/omit for the default.
98
+ */
99
+ significantDigits?: number;
100
+ }
101
+ /** Normalized, resolved unit identity after parsing a token. */
102
+ interface ResolvedUnit {
103
+ def: UnitDef;
104
+ /** Whether the caller asked for a temperature *difference* (Δ) conversion. */
105
+ delta: boolean;
106
+ /** Canonical spellings resolved from the input, e.g. `千米`. */
107
+ canonical: string;
108
+ }
109
+ /** A validated conversion request after both units parsed. */
110
+ interface ConvertRequest {
111
+ value: number;
112
+ from: ResolvedUnit;
113
+ to: ResolvedUnit;
114
+ }
115
+ /** Result of {@link convertDetailed}. */
116
+ interface ConvertResult {
117
+ /** Original numeric value. */
118
+ value: number;
119
+ /** Canonical from-unit display name (resolved input spelling). */
120
+ from: string;
121
+ /** Canonical to-unit display name. */
122
+ to: string;
123
+ /** Converted value. */
124
+ result: number;
125
+ /** Category id shared by both units, e.g. `length`. */
126
+ category: CategoryId;
127
+ /** Category display name (Chinese), e.g. `长度`. */
128
+ categoryZh: string;
129
+ /** Category display name (English), e.g. `Length`. */
130
+ categoryEn: string;
131
+ /** Input tokens exactly as the caller passed them. */
132
+ fromInput: string;
133
+ /** Input tokens exactly as the caller passed them. */
134
+ toInput: string;
135
+ /** Human summary line, e.g. `100 千米 = 62.13711922 英里 (长度)`. */
136
+ summary: string;
137
+ }
138
+ /** Error thrown when a conversion request is invalid. */
139
+ declare class UnitConversionError extends Error {
140
+ /** Machine-readable kind: `unknown-unit` | `category-mismatch` | `bad-value` | `mixed-delta` | `unsupported`. */
141
+ readonly kind: 'unknown-unit' | 'category-mismatch' | 'bad-value' | 'mixed-delta' | 'unsupported';
142
+ /** The offending raw token for `unknown-unit` errors. */
143
+ readonly token?: string;
144
+ constructor(kind: UnitConversionError['kind'], message: string, token?: string);
145
+ }
146
+ /**
147
+ * Parse and resolve one unit token. Supports case-insensitive matches
148
+ * against symbols, English names and Chinese names, plus an optional
149
+ * temperature-delta marker (`Δ°C`, `deltaC`, `ΔK`, ...).
150
+ */
151
+ declare function resolveUnit(token: string): ResolvedUnit;
152
+ /**
153
+ * Parse both unit tokens and verify they belong to the same category.
154
+ * `from`/`to` may be the same unit (returns the value unchanged).
155
+ */
156
+ declare function parseRequest(value: number, from: string, to: string): ConvertRequest;
157
+ /**
158
+ * Round to `digits` significant digits (default 10). Implemented through
159
+ * `toPrecision`, which stays finite across the full double range (a naive
160
+ * `10 ** exponent` overflows for very small/large magnitudes).
161
+ */
162
+ declare function roundSignificant(value: number, digits?: number): number;
163
+ /** Convert `value` from unit `from` to unit `to`. */
164
+ declare function convertRaw(value: number, from: ResolvedUnit, to: ResolvedUnit): number;
165
+ /**
166
+ * Convert a numeric value between two units of the same category.
167
+ *
168
+ * ```ts
169
+ * convert(100, 'km', 'mi') // 62.13711922
170
+ * convert(25, '°C', '°F') // 77
171
+ * convert(1, 'Δ°C', '°F') // 1.8 (temperature difference)
172
+ * convert(3, '公里/小时', 'm/s') // 0.8333333333
173
+ * ```
174
+ *
175
+ * @param value - the numeric amount to convert
176
+ * @param from - source unit token (symbol, English or Chinese name)
177
+ * @param to - target unit token
178
+ * @param options - {@link ConvertOptions}
179
+ * @throws {@link UnitConversionError} on unknown units, cross-category
180
+ * requests, or non-finite values.
181
+ */
182
+ declare function convert(value: number, from: string, to: string, options?: ConvertOptions): number;
183
+ /** Convert and return a structured {@link ConvertResult} including a summary. */
184
+ declare function convertDetailed(value: number, from: string, to: string, options?: ConvertOptions): ConvertResult;
185
+
186
+ export { ALIAS_INDEX as A, CATEGORIES as C, DELTA_MARKERS as D, type ResolvedUnit as R, UNITS as U, type AliasIndex as a, CATEGORY_ORDER as b, type CategoryId as c, type CategoryMeta as d, type ConvertOptions as e, type ConvertRequest as f, type ConvertResult as g, UnitConversionError as h, type UnitDef as i, buildAliasIndex as j, convert as k, convertDetailed as l, convertRaw as m, findUnit as n, normalizeUnit as o, parseRequest as p, roundSignificant as q, resolveUnit as r, stripDeltaMarker as s, unitsByCategory as u };