unacy 0.5.0 → 0.7.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.
Files changed (36) hide show
  1. package/README.md +104 -35
  2. package/dist/converters.d.ts +8 -8
  3. package/dist/converters.d.ts.map +1 -1
  4. package/dist/index.d.ts +2 -2
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/registry.d.ts +38 -44
  8. package/dist/registry.d.ts.map +1 -1
  9. package/dist/registry.js +44 -14
  10. package/dist/registry.js.map +1 -1
  11. package/dist/types.d.ts +44 -11
  12. package/dist/types.d.ts.map +1 -1
  13. package/dist/utils/validation.d.ts +1 -2
  14. package/dist/utils/validation.d.ts.map +1 -1
  15. package/dist/utils/validation.js.map +1 -1
  16. package/package.json +10 -8
  17. package/dist/__tests__/converters.test.d.ts +0 -2
  18. package/dist/__tests__/converters.test.d.ts.map +0 -1
  19. package/dist/__tests__/converters.test.js +0 -128
  20. package/dist/__tests__/converters.test.js.map +0 -1
  21. package/dist/__tests__/errors.test.d.ts +0 -2
  22. package/dist/__tests__/errors.test.d.ts.map +0 -1
  23. package/dist/__tests__/errors.test.js +0 -93
  24. package/dist/__tests__/errors.test.js.map +0 -1
  25. package/dist/__tests__/formatters.test.d.ts +0 -2
  26. package/dist/__tests__/formatters.test.d.ts.map +0 -1
  27. package/dist/__tests__/formatters.test.js +0 -244
  28. package/dist/__tests__/formatters.test.js.map +0 -1
  29. package/dist/__tests__/registry.test.d.ts +0 -2
  30. package/dist/__tests__/registry.test.d.ts.map +0 -1
  31. package/dist/__tests__/registry.test.js +0 -403
  32. package/dist/__tests__/registry.test.js.map +0 -1
  33. package/dist/__tests__/types.test.d.ts +0 -2
  34. package/dist/__tests__/types.test.d.ts.map +0 -1
  35. package/dist/__tests__/types.test.js +0 -115
  36. package/dist/__tests__/types.test.js.map +0 -1
package/README.md CHANGED
@@ -11,6 +11,7 @@ Type-safe unit and format conversion library with automatic multi-hop compositio
11
11
  - 🛡️ **Cycle detection** - Prevents infinite conversion loops
12
12
  - 📦 **Tree-shakeable** - Only bundle converters you use
13
13
  - ✨ **Fluent API** - Clean, readable conversion syntax
14
+ - 🎯 **Typed Metadata** - Native support for `number`, `string`, `boolean`, and `bigint` units
14
15
 
15
16
  ## Installation
16
17
 
@@ -21,15 +22,27 @@ pnpm add @unacy/core
21
22
  ## Quick Start
22
23
 
23
24
  ```typescript
24
- import { createRegistry, type WithUnits } from '@unacy/core';
25
+ import { createRegistry } from '@unacy/core';
26
+ import type { WithTypedUnits } from '@unacy/core';
25
27
 
26
- // Define your unit types
27
- type Celsius = WithUnits<number, 'Celsius'>;
28
- type Fahrenheit = WithUnits<number, 'Fahrenheit'>;
28
+ // Define metadata for your units (name + type)
29
+ const CelsiusMetadata = {
30
+ name: 'Celsius' as const,
31
+ type: 'number' as const
32
+ };
33
+
34
+ const FahrenheitMetadata = {
35
+ name: 'Fahrenheit' as const,
36
+ type: 'number' as const
37
+ };
29
38
 
30
- // Create a registry
39
+ // Define your unit types with metadata
40
+ type Celsius = WithTypedUnits<typeof CelsiusMetadata>;
41
+ type Fahrenheit = WithTypedUnits<typeof FahrenheitMetadata>;
42
+
43
+ // Create a registry and register converters
31
44
  const tempRegistry = createRegistry()
32
- .register('Celsius', 'Fahrenheit', (c) => ((c * 9/5) + 32) as Fahrenheit);
45
+ .register(CelsiusMetadata, FahrenheitMetadata, (c) => ((c * 9/5) + 32));
33
46
 
34
47
  // Create branded values using callable accessors (NEW!)
35
48
  const temp = tempRegistry.Celsius(25); // Returns Celsius type
@@ -57,8 +70,8 @@ Unit accessors are now callable functions that create branded values:
57
70
 
58
71
  ```typescript
59
72
  // Create branded values without manual type casting
60
- const temp = registry.Celsius(25); // Returns WithUnits<number, 'Celsius'>
61
- const distance = registry.meters(100); // Returns WithUnits<number, 'meters'>
73
+ const temp = registry.Celsius(25); // Returns WithTypedUnits<typeof CelsiusMetadata>
74
+ const distance = registry.meters(100); // Returns WithTypedUnits<typeof MetersMetadata>
62
75
 
63
76
  // Fluent workflow
64
77
  const fahrenheit = registry.Celsius.to.Fahrenheit(registry.Celsius(20));
@@ -73,6 +86,27 @@ const tempOld: Celsius = 25 as Celsius;
73
86
  // - Works seamlessly with conversions
74
87
  ```
75
88
 
89
+ ### Typed Metadata
90
+
91
+ Define metadata with minimal required fields (name + type):
92
+
93
+ ```typescript
94
+ const CelsiusMetadata = {
95
+ name: 'Celsius' as const,
96
+ type: 'number' as const
97
+ };
98
+
99
+ const EtherMetadata = {
100
+ name: 'ether' as const,
101
+ type: 'bigint' as const
102
+ };
103
+
104
+ const FlagMetadata = {
105
+ name: 'enabled' as const,
106
+ type: 'boolean' as const
107
+ };
108
+ ```
109
+
76
110
  ### Basic Unit Conversions
77
111
 
78
112
  ```typescript
@@ -94,16 +128,26 @@ console.log(meters); // 10
94
128
  ### Bidirectional Converters
95
129
 
96
130
  ```typescript
97
- import { createRegistry, type WithUnits } from '@unacy/core';
131
+ import { createRegistry } from '@unacy/core';
132
+ import type { WithTypedUnits } from '@unacy/core';
98
133
 
99
- type Meters = WithUnits<number, 'meters'>;
100
- type Kilometers = WithUnits<number, 'kilometers'>;
134
+ const MetersMetadata = {
135
+ name: 'meters' as const,
136
+ type: 'number' as const
137
+ };
101
138
 
102
- const registry = createRegistry<'meters' | 'kilometers'>()
103
- .registerBidirectional('meters', 'kilometers', {
104
- to: (m) => (m / 1000) as Kilometers,
105
- from: (km) => (km * 1000) as Meters
106
- });
139
+ const KilometersMetadata = {
140
+ name: 'kilometers' as const,
141
+ type: 'number' as const
142
+ };
143
+
144
+ type Meters = WithTypedUnits<typeof MetersMetadata>;
145
+ type Kilometers = WithTypedUnits<typeof KilometersMetadata>;
146
+
147
+ const registry = createRegistry()
148
+ .register(MetersMetadata, KilometersMetadata, {
149
+ to: (m: number) => (m / 1000),
150
+ from: (km: number) => (km * 1000)
107
151
  });
108
152
 
109
153
  // Both directions work automatically
@@ -116,18 +160,22 @@ const m = registry.convert(5 as Kilometers, 'kilometers').to('meters'); // 5000
116
160
  The registry automatically composes converters via shortest path:
117
161
 
118
162
  ```typescript
119
- type Meters = WithUnits<number, 'meters'>;
120
- type Kilometers = WithUnits<number, 'kilometers'>;
121
- type Miles = WithUnits<number, 'miles'>;
122
-
123
- const registry = createRegistry<'meters' | 'kilometers' | 'miles'>()
124
- .registerBidirectional('meters', 'kilometers', {
125
- to: (m) => (m / 1000) as Kilometers,
126
- from: (km) => (km * 1000) as Meters
163
+ const MetersMetadata = { name: 'meters' as const, type: 'number' as const };
164
+ const KilometersMetadata = { name: 'kilometers' as const, type: 'number' as const };
165
+ const MilesMetadata = { name: 'miles' as const, type: 'number' as const };
166
+
167
+ type Meters = WithTypedUnits<typeof MetersMetadata>;
168
+ type Kilometers = WithTypedUnits<typeof KilometersMetadata>;
169
+ type Miles = WithTypedUnits<typeof MilesMetadata>;
170
+
171
+ const registry = createRegistry()
172
+ .register(MetersMetadata, KilometersMetadata, {
173
+ to: (m: number) => (m / 1000),
174
+ from: (km: number) => (km * 1000)
127
175
  })
128
- .registerBidirectional('kilometers', 'miles', {
129
- to: (km) => (km * 0.621371) as Miles,
130
- from: (mi) => (mi / 0.621371) as Kilometers
176
+ .register(KilometersMetadata, MilesMetadata, {
177
+ to: (km: number) => (km * 0.621371),
178
+ from: (mi: number) => (mi / 0.621371)
131
179
  });
132
180
 
133
181
  // No direct meters→miles converter registered!
@@ -169,14 +217,34 @@ const date = iso8601.parse('2026-01-06T12:00:00.000Z');
169
217
 
170
218
  ### Types
171
219
 
220
+ #### `WithTypedUnits<M extends TypedMetadata<T>>`
221
+ Brand a value with strongly-typed metadata for compile-time unit safety.
222
+
223
+ ```typescript
224
+ const CelsiusMetadata = { name: 'Celsius' as const, type: 'number' as const };
225
+ type Celsius = WithTypedUnits<typeof CelsiusMetadata>;
226
+ const temp: Celsius = tempRegistry.Celsius(25);
227
+ ```
228
+
172
229
  #### `WithUnits<T, U>`
173
- Brand a value with a unit identifier for compile-time safety.
230
+ Legacy: Brand a value with a unit identifier for compile-time safety.
174
231
 
175
232
  ```typescript
176
233
  type Celsius = WithUnits<number, 'Celsius'>;
177
234
  const temp: Celsius = 25 as Celsius;
178
235
  ```
179
236
 
237
+ #### `TypedMetadata<T>`
238
+ Minimal metadata type with name and type information.
239
+
240
+ ```typescript
241
+ type NumericMetadata = TypedMetadata<number>;
242
+ // { name: string; type: 'number' }
243
+
244
+ type StringMetadata = TypedMetadata<string>;
245
+ // { name: string; type: 'string' }
246
+ ```
247
+
180
248
  #### `WithFormat<T, F>`
181
249
  Brand a value with a format identifier for serialization safety.
182
250
 
@@ -198,8 +266,8 @@ Pair of converters for two-way transformations.
198
266
 
199
267
  ```typescript
200
268
  const meterKm: BidirectionalConverter<Meters, Kilometers> = {
201
- to: (m) => (m / 1000) as Kilometers,
202
- from: (km) => (km * 1000) as Meters
269
+ to: (m: number) => (m / 1000),
270
+ from: (km: number) => (km * 1000)
203
271
  };
204
272
  ```
205
273
 
@@ -216,14 +284,14 @@ const registry = createRegistry<'A' | 'B' | 'C'>();
216
284
  Register a unidirectional converter.
217
285
 
218
286
  ```typescript
219
- registry.register('Celsius', 'Fahrenheit', celsiusToFahrenheit);
287
+ registry.register(CelsiusMetadata, FahrenheitMetadata, celsiusToFahrenheit);
220
288
  ```
221
289
 
222
- #### `registerBidirectional(from, to, converter)`
290
+ #### `register(from, to, converter)` (bidirectional)
223
291
  Register both directions at once.
224
292
 
225
293
  ```typescript
226
- registry.registerBidirectional('meters', 'kilometers', meterKm);
294
+ registry.register(MetersMetadata, KilometersMetadata, meterKm);
227
295
  ```
228
296
 
229
297
  #### `convert(value, fromUnit).to(toUnit)`
@@ -243,11 +311,12 @@ const result = registry.convert(value, 'Celsius').to('Fahrenheit');
243
311
 
244
312
  ## Best Practices
245
313
 
246
- 1. **Define unit types at module boundaries** for consistency
314
+ 1. **Define metadata as const at module boundaries** for consistency
247
315
  2. **Use bidirectional converters** when both directions are needed
248
316
  3. **Document precision loss** in converters
249
317
  4. **Cache registries** for performance
250
- 5. **Validate with Zod** in parsers
318
+ 5. **Use `WithTypedUnits`** for brand-new code; leverage type inference
319
+ 6. **Validate with Zod** in parsers
251
320
 
252
321
  ## Performance
253
322
 
@@ -2,7 +2,7 @@
2
2
  * Type-safe converter function signatures
3
3
  * @packageDocumentation
4
4
  */
5
- import type { PrimitiveType, Unwrap, WithUnits, Relax as BaseRelax } from './types.js';
5
+ import type { PrimitiveType, Relax as BaseRelax } from './types.js';
6
6
  /**
7
7
  * Unidirectional converter from one unit to another.
8
8
  *
@@ -23,9 +23,9 @@ import type { PrimitiveType, Unwrap, WithUnits, Relax as BaseRelax } from './typ
23
23
  * ((c * 9/5) + 32) as Fahrenheit;
24
24
  * ```
25
25
  */
26
- export type Converter<TInput extends WithUnits<PrimitiveType, string>, TOutput extends WithUnits<PrimitiveType, string>> = (input: TInput) => TOutput;
27
- export type RelaxConverter<ConverterType> = ConverterType extends Converter<infer A, infer B> ? (input: BaseRelax<A>) => BaseRelax<B> : (input: PrimitiveType) => PrimitiveType;
28
- export type Relax<T extends PrimitiveType | Converter<any, any> | BidirectionalConverter<any, any>> = T extends PrimitiveType ? BaseRelax<T> : T extends Converter<infer A, infer B> ? RelaxConverter<T> : RelaxBidirectionalConverter<T>;
26
+ export type Converter<TInput, TOutput> = (input: TInput) => TOutput;
27
+ export type RelaxConverter<ConverterType> = ConverterType extends Converter<infer A extends PrimitiveType, infer B extends PrimitiveType> ? (input: BaseRelax<A>) => BaseRelax<B> : (input: PrimitiveType) => PrimitiveType;
28
+ export type Relax<T extends PrimitiveType | Converter<any, any> | BidirectionalConverter<any, any>> = T extends PrimitiveType ? BaseRelax<T> : T extends Converter<unknown, unknown> ? RelaxConverter<T> : RelaxBidirectionalConverter<T>;
29
29
  /**
30
30
  * Bidirectional converter with forward and reverse transformations.
31
31
  *
@@ -48,13 +48,13 @@ export type Relax<T extends PrimitiveType | Converter<any, any> | BidirectionalC
48
48
  * };
49
49
  * ```
50
50
  */
51
- export type BidirectionalConverter<TInput extends WithUnits<PrimitiveType, string>, TOutput extends WithUnits<PrimitiveType, string>> = {
51
+ export type BidirectionalConverter<TInput, TOutput> = {
52
52
  to: Converter<TInput, TOutput>;
53
53
  from: Converter<TOutput, TInput>;
54
54
  };
55
- export type RelaxBidirectionalConverter<ConverterType> = ConverterType extends BidirectionalConverter<infer A, infer B> ? {
56
- to: (input: Unwrap<A> | A) => Unwrap<B> | B;
57
- from: (input: Unwrap<B> | B) => Unwrap<A> | A;
55
+ export type RelaxBidirectionalConverter<ConverterType> = ConverterType extends BidirectionalConverter<infer A extends PrimitiveType, infer B extends PrimitiveType> ? {
56
+ to: (input: BaseRelax<A>) => B;
57
+ from: (input: BaseRelax<B>) => A;
58
58
  } : {
59
59
  to: (input: PrimitiveType) => PrimitiveType;
60
60
  from: (input: PrimitiveType) => PrimitiveType;
@@ -1 +1 @@
1
- {"version":3,"file":"converters.d.ts","sourceRoot":"","sources":["../src/converters.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,SAAS,CACnB,MAAM,SAAS,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,EAC/C,OAAO,SAAS,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,IAC9C,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;AAE/B,MAAM,MAAM,cAAc,CAAC,aAAa,IACtC,aAAa,SAAS,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,GAC7C,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,GACrC,CAAC,KAAK,EAAE,aAAa,KAAK,aAAa,CAAC;AAE9C,MAAM,MAAM,KAAK,CACf,CAAC,SAAS,aAAa,GAAG,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,sBAAsB,CAAC,GAAG,EAAE,GAAG,CAAC,IAC9E,CAAC,SAAS,aAAa,GACvB,SAAS,CAAC,CAAC,CAAC,GACZ,CAAC,SAAS,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,GACnC,cAAc,CAAC,CAAC,CAAC,GACjB,2BAA2B,CAAC,CAAC,CAAC,CAAC;AAErC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,sBAAsB,CAChC,MAAM,SAAS,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,EAC/C,OAAO,SAAS,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,IAC9C;IACF,EAAE,EAAE,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,EAAE,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,2BAA2B,CAAC,aAAa,IACnD,aAAa,SAAS,sBAAsB,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,GAC1D;IACE,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC5C,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;CAC/C,GACD;IACE,EAAE,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,aAAa,CAAC;IAC5C,IAAI,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,aAAa,CAAC;CAC/C,CAAC"}
1
+ {"version":3,"file":"converters.d.ts","sourceRoot":"","sources":["../src/converters.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,YAAY,CAAC;AAEpE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,SAAS,CAAC,MAAM,EAAE,OAAO,IAAI,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;AAEpE,MAAM,MAAM,cAAc,CAAC,aAAa,IACtC,aAAa,SAAS,SAAS,CAAC,MAAM,CAAC,SAAS,aAAa,EAAE,MAAM,CAAC,SAAS,aAAa,CAAC,GACzF,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,GACrC,CAAC,KAAK,EAAE,aAAa,KAAK,aAAa,CAAC;AAE9C,MAAM,MAAM,KAAK,CACf,CAAC,SAAS,aAAa,GAAG,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,sBAAsB,CAAC,GAAG,EAAE,GAAG,CAAC,IAC9E,CAAC,SAAS,aAAa,GACvB,SAAS,CAAC,CAAC,CAAC,GACZ,CAAC,SAAS,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,GACnC,cAAc,CAAC,CAAC,CAAC,GACjB,2BAA2B,CAAC,CAAC,CAAC,CAAC;AAErC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,sBAAsB,CAAC,MAAM,EAAE,OAAO,IAAI;IACpD,EAAE,EAAE,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,EAAE,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,2BAA2B,CAAC,aAAa,IACnD,aAAa,SAAS,sBAAsB,CAC1C,MAAM,CAAC,SAAS,aAAa,EAC7B,MAAM,CAAC,SAAS,aAAa,CAC9B,GACG;IACE,EAAE,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAC/B,IAAI,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;CAClC,GACD;IACE,EAAE,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,aAAa,CAAC;IAC5C,IAAI,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,aAAa,CAAC;CAC/C,CAAC"}
package/dist/index.d.ts CHANGED
@@ -2,10 +2,10 @@
2
2
  * Unacy Core - Type-safe unit and format conversion library
3
3
  * @packageDocumentation
4
4
  */
5
- export type { WithUnits, WithFormat, UnitMetadata } from './types.js';
5
+ export type { WithUnits, WithTypedUnits, WithFormat, BaseMetadata, TypedMetadata, UnitMetadata, Relax, PrimitiveType, ToPrimitiveType, ToPrimitiveTypeName } from './types.js';
6
6
  export type { Converter, BidirectionalConverter } from './converters.js';
7
7
  export type { Formatter, Parser, FormatterParser } from './formatters.js';
8
- export type { ConverterRegistry } from './registry.js';
8
+ export type { UnitRegistry, UnitMap, UnitAccessor } from './registry.js';
9
9
  export { createRegistry } from './registry.js';
10
10
  export { UnacyError, CycleError, MaxDepthError, ConversionError, ParseError } from './errors.js';
11
11
  export { createParserWithSchema } from './utils/validation.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAGtE,YAAY,EAAE,SAAS,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAGzE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAG1E,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGjG,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,YAAY,EACV,SAAS,EACT,cAAc,EACd,UAAU,EACV,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,KAAK,EACL,aAAa,EACb,eAAe,EACf,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAGpB,YAAY,EAAE,SAAS,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAGzE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAG1E,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGjG,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,SAAS;AACT,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEjG,YAAY;AACZ,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAwBH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,SAAS;AACT,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEjG,YAAY;AACZ,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -3,11 +3,13 @@
3
3
  * @packageDocumentation
4
4
  */
5
5
  import type { Converter, BidirectionalConverter, Relax } from './converters.js';
6
- import type { OptionalWithUnits, UnitsFor, WithUnits, UnitMetadata } from './types.js';
6
+ import type { UnitsFor, WithUnits, TypedMetadata, NameFor, WithTypedUnits, UnitsOf, BaseMetadata, PrimitiveType, Relax as RelaxUnits } from './types.js';
7
7
  /**
8
8
  * Represents a conversion edge from one unit to another
9
+ * Edges store the unit names as strings, but the type system ensures
10
+ * they correspond to valid WithUnits types
9
11
  */
10
- type Edge<From extends string = string, To extends string = string> = readonly [From, To];
12
+ type Edge<From extends WithTypedUnits<any> = WithTypedUnits<TypedMetadata<PrimitiveType>>, To extends WithTypedUnits<any> = WithTypedUnits<TypedMetadata<PrimitiveType>>> = readonly [From, To];
11
13
  /**
12
14
  * Extract all unique 'from' units from a list of edges
13
15
  */
@@ -15,83 +17,75 @@ type FromUnits<Edges extends readonly Edge[]> = Edges[number][0];
15
17
  /**
16
18
  * Extract all 'to' units for a specific 'from' unit string
17
19
  */
18
- type ToUnitsFor<Edges extends readonly Edge[], FromUnit extends string> = Extract<Edges[number], readonly [FromUnit, any]>[1];
20
+ type ToUnitsFor<Edges extends readonly Edge[], FromUnit extends WithTypedUnits<any>> = Extract<Edges[number], readonly [FromUnit, any]>[1];
21
+ /**
22
+ * Extract metadata type from a WithUnits type
23
+ */
19
24
  /**
20
25
  * Type for unit accessor with metadata and conversion methods
21
26
  * Can be called as a function to create branded unit values
22
27
  */
23
- export type UnitAccessor<From extends string, Edges extends readonly Edge[]> = {
28
+ export type UnitAccessor<From extends WithTypedUnits<TypedMetadata>, Edges extends readonly Edge[]> = {
24
29
  /**
25
30
  * Create a branded value with this unit
26
31
  * @param value - The numeric value to brand
27
32
  * @returns The value branded with this unit type
28
33
  */
29
- (value: number): WithUnits<number, From>;
34
+ (value: number): From;
30
35
  to: {
31
- [To in ToUnitsFor<Edges, From>]: (value: OptionalWithUnits<number, From>) => WithUnits<number, To>;
36
+ [To in ToUnitsFor<Edges, From> as UnitsFor<To>]: (value: RelaxUnits<From>) => To;
32
37
  };
33
38
  /**
34
39
  * Add metadata to this unit
35
40
  */
36
- addMetadata(metadata: UnitMetadata): ConverterRegistry<Edges extends readonly (infer E)[] ? E[] : never> & ConverterMap<Edges>;
41
+ addMetadata<NewM extends Record<string, unknown>>(metadata: NewM): UnitRegistry<Edges extends readonly (infer E)[] ? E[] : never> & UnitMap<Edges>;
37
42
  /**
38
43
  * Register a converter from this unit to another unit
39
44
  */
40
- register<To extends string>(to: To, converter: Relax<Converter<WithUnits<number, From>, WithUnits<number, To>>>): ConverterRegistry<[...Edges, Edge<From, To>]> & ConverterMap<[...Edges, Edge<From, To>]>;
41
- register<To extends string>(to: To, converter: Relax<BidirectionalConverter<WithUnits<number, From>, WithUnits<number, To>>>): ConverterRegistry<[...Edges, Edge<From, To>, Edge<To, From>]> & ConverterMap<[...Edges, Edge<From, To>, Edge<To, From>]>;
42
- } & {
43
- [K: string]: unknown;
44
- };
45
+ register<To extends WithTypedUnits<ToMeta>, ToMeta extends TypedMetadata<PrimitiveType>>(to: UnitsFor<To> | ToMeta, converter: Relax<Converter<From, To>>): UnitRegistry<[...Edges, Edge<From, To>]> & UnitMap<[...Edges, Edge<From, To>]>;
46
+ register<To extends WithTypedUnits<ToMeta>, ToMeta extends TypedMetadata<PrimitiveType>>(to: UnitsFor<To> | ToMeta, converter: Relax<BidirectionalConverter<From, To>>): UnitRegistry<[...Edges, Edge<From, To>, Edge<To, From>]> & UnitMap<[...Edges, Edge<From, To>, Edge<To, From>]>;
47
+ } & UnitsOf<From>;
45
48
  /**
46
49
  * Type for unit-based conversion accessors
47
50
  * Provides the shape: registry.Celsius.to.Fahrenheit(value)
48
51
  * Only allows conversions that have been registered
49
52
  */
50
- export type ConverterMap<Edges extends readonly Edge[]> = {
51
- [From in FromUnits<Edges>]: UnitAccessor<From, Edges>;
53
+ export type UnitMap<Edges extends readonly Edge[]> = {
54
+ [FU in FromUnits<Edges> as UnitsFor<FU>]: UnitAccessor<FU, Edges>;
52
55
  };
53
56
  /**
54
57
  * Registry for managing and composing unit converters
55
58
  */
56
- export interface ConverterRegistry<Edges extends Edge[] = []> {
59
+ export interface UnitRegistry<Edges extends Edge[] = []> {
60
+ register<From extends WithTypedUnits<FromMeta>, FromMeta extends TypedMetadata<PrimitiveType>>(unit: FromMeta): this & {
61
+ [K in NameFor<From>]: UnitAccessor<From, Edges>;
62
+ };
57
63
  /**
58
64
  * Register a unidirectional converter
59
65
  *
60
- * @param from - Source unit
61
- * @param to - Destination unit
66
+ * @param from - Source unit (string name or metadata object)
67
+ * @param to - Destination unit (string name or metadata object)
62
68
  * @param converter - Converter function
63
69
  * @returns New registry instance with the converter registered
64
70
  */
65
- register<From extends WithUnits<number, UF>, To extends WithUnits<number, UY>, UF extends string, UY extends string>(from: UF, to: UY, converter: Relax<Converter<From, To>>): ConverterRegistry<[...Edges, Edge<UnitsFor<From>, UnitsFor<To>>]> & ConverterMap<[...Edges, Edge<UnitsFor<From>, UnitsFor<To>>]>;
71
+ register<From extends WithTypedUnits<FromMeta>, To extends WithTypedUnits<ToMeta>, FromMeta extends TypedMetadata<PrimitiveType>, ToMeta extends TypedMetadata<PrimitiveType>>(from: UnitsFor<From> | FromMeta, to: UnitsFor<To> | ToMeta, converter: Converter<From, RelaxUnits<To>>): UnitRegistry<[...Edges, Edge<From, To>]> & UnitMap<[...Edges, Edge<From, To>]>;
66
72
  /**
67
73
  * Register a bidirectional converter (both directions)
68
74
  *
69
- * @param from - First unit
70
- * @param to - Second unit
75
+ * @param from - First unit (string name or metadata object)
76
+ * @param to - Second unit (string name or metadata object)
71
77
  * @param converter - Bidirectional converter object
72
78
  * @returns New registry instance with both converters registered
73
79
  */
74
- register<From extends WithUnits<number, string>, To extends WithUnits<number, string>>(from: UnitsFor<From> | {
75
- name: UnitsFor<From>;
76
- }, to: UnitsFor<To> | {
77
- name: UnitsFor<To>;
78
- }, converter: Relax<BidirectionalConverter<From, To>>): ConverterRegistry<[
79
- ...Edges,
80
- Edge<UnitsFor<From>, UnitsFor<To>>,
81
- Edge<UnitsFor<To>, UnitsFor<From>>
82
- ]> & ConverterMap<[
83
- ...Edges,
84
- Edge<UnitsFor<From>, UnitsFor<To>>,
85
- Edge<UnitsFor<To>, UnitsFor<From>>
86
- ]>;
80
+ register<From extends WithTypedUnits<FromMeta>, To extends WithTypedUnits<ToMeta>, FromMeta extends TypedMetadata<PrimitiveType>, ToMeta extends TypedMetadata<PrimitiveType>>(from: UnitsFor<From> | FromMeta, to: UnitsFor<To> | ToMeta, converter: BidirectionalConverter<RelaxUnits<From>, RelaxUnits<To>>): UnitRegistry<[...Edges, Edge<From, To>, Edge<To, From>]> & UnitMap<[...Edges, Edge<From, To>, Edge<To, From>]>;
87
81
  /**
88
82
  * Explicitly allow a conversion path in the type system (for multi-hop conversions)
89
83
  *
90
84
  * This method verifies that a conversion path exists at runtime (via BFS) and adds it
91
85
  * to the type system so it can be used with type-safe accessor syntax.
92
86
  *
93
- * @param from - Source unit string
94
- * @param to - Destination unit string
87
+ * @param from - Source unit (string name or metadata object)
88
+ * @param to - Destination unit (string name or metadata object)
95
89
  * @returns New registry instance with the conversion path enabled in types
96
90
  * @throws ConversionError if no path exists between the units
97
91
  *
@@ -106,7 +100,7 @@ export interface ConverterRegistry<Edges extends Edge[] = []> {
106
100
  * const f = registry.Celsius.to.Fahrenheit(temp);
107
101
  * ```
108
102
  */
109
- allow<From extends string, To extends string>(from: From, to: To): ConverterRegistry<[...Edges, Edge<From, To>]> & ConverterMap<[...Edges, Edge<From, To>]>;
103
+ allow<From extends WithTypedUnits<FromMeta>, To extends WithTypedUnits<ToMeta>, FromMeta extends TypedMetadata<PrimitiveType>, ToMeta extends TypedMetadata<PrimitiveType>>(from: UnitsFor<From> | FromMeta, to: UnitsFor<To> | ToMeta): UnitRegistry<[...Edges, Edge<From, To>]> & UnitMap<[...Edges, Edge<From, To>]>;
110
104
  /**
111
105
  * Get a converter (direct or composed via BFS)
112
106
  *
@@ -114,7 +108,7 @@ export interface ConverterRegistry<Edges extends Edge[] = []> {
114
108
  * @param to - Destination unit
115
109
  * @returns Converter function, or undefined if no path exists
116
110
  */
117
- getConverter<From extends WithUnits<number, string>, To extends WithUnits<number, string>>(from: UnitsFor<From>, to: UnitsFor<To>): Converter<From, To> | undefined;
111
+ getConverter<From extends WithUnits<PrimitiveType, BaseMetadata>, To extends WithUnits<PrimitiveType, BaseMetadata>>(from: UnitsFor<From>, to: UnitsFor<To>): Converter<From, To> | undefined;
118
112
  /**
119
113
  * Convert a value using fluent API
120
114
  *
@@ -122,8 +116,8 @@ export interface ConverterRegistry<Edges extends Edge[] = []> {
122
116
  * @param fromUnit - Source unit
123
117
  * @returns Object with to() method for conversion
124
118
  */
125
- convert<From extends WithUnits<number, string>>(value: From, fromUnit: UnitsFor<From>): {
126
- to<To extends WithUnits<number, string>>(unit: UnitsFor<To>): To;
119
+ convert<From extends WithUnits<PrimitiveType, BaseMetadata>>(value: From, fromUnit: UnitsFor<From>): {
120
+ to<To extends WithUnits<PrimitiveType, BaseMetadata>>(unit: UnitsFor<To>): To;
127
121
  };
128
122
  }
129
123
  /**
@@ -134,10 +128,10 @@ export interface ConverterRegistry<Edges extends Edge[] = []> {
134
128
  *
135
129
  * @example
136
130
  * ```typescript
137
- * type Celsius = WithUnits<number, 'Celsius'>;
138
- * type Fahrenheit = WithUnits<number, 'Fahrenheit'>;
139
- * type Meters = WithUnits<number, 'meters'>;
140
- * type Kilometers = WithUnits<number, 'kilometers'>;
131
+ * type Celsius = WithUnits<PrimitiveType, 'Celsius'>;
132
+ * type Fahrenheit = WithUnits<PrimitiveType, 'Fahrenheit'>;
133
+ * type Meters = WithUnits<PrimitiveType, 'meters'>;
134
+ * type Kilometers = WithUnits<PrimitiveType, 'kilometers'>;
141
135
  *
142
136
  * // Without pre-declared units
143
137
  * const registry = createRegistry()
@@ -152,6 +146,6 @@ export interface ConverterRegistry<Edges extends Edge[] = []> {
152
146
  * console.log(fahrenheit); // 77
153
147
  * ```
154
148
  */
155
- export declare function createRegistry<Edges extends readonly Edge[] = []>(): ConverterRegistry<Edges extends readonly (infer E)[] ? E[] : never> & ConverterMap<Edges>;
149
+ export declare function createRegistry<Edges extends readonly Edge[] = []>(): UnitRegistry<Edges extends readonly (infer E)[] ? E[] : never> & UnitMap<Edges>;
156
150
  export {};
157
151
  //# sourceMappingURL=registry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
package/dist/registry.js CHANGED
@@ -112,38 +112,68 @@ class ConverterRegistryImpl {
112
112
  this.unitAccessors.set(fromUnit, accessorProxy);
113
113
  }
114
114
  }
115
+ // Implementation
115
116
  register(from, to, converter) {
117
+ // Handle single unit registration (overload 1)
118
+ if (to === undefined && converter === undefined) {
119
+ const unitMetadata = from;
120
+ const newMetadata = new Map(this.metadata);
121
+ const unitName = unitMetadata.name;
122
+ const existingMetadata = newMetadata.get(unitName) || {};
123
+ newMetadata.set(unitName, { ...existingMetadata, ...unitMetadata });
124
+ return createRegistryFromGraph(this.graph, this.pathCache, newMetadata);
125
+ }
126
+ // Extract metadata if provided and store unit names
127
+ let fromName;
128
+ let toName;
129
+ const newMetadata = new Map(this.metadata);
116
130
  if (typeof from === 'object' && 'name' in from) {
117
- from = from.name;
131
+ fromName = from.name;
132
+ // Store metadata for the from unit
133
+ const existingFromMetadata = newMetadata.get(fromName) || {};
134
+ newMetadata.set(fromName, { ...existingFromMetadata, ...from });
135
+ }
136
+ else {
137
+ fromName = from;
118
138
  }
119
139
  if (typeof to === 'object' && 'name' in to) {
120
- to = to.name;
140
+ toName = to.name;
141
+ // Store metadata for the to unit
142
+ const existingToMetadata = newMetadata.get(toName) || {};
143
+ newMetadata.set(toName, { ...existingToMetadata, ...to });
144
+ }
145
+ else {
146
+ toName = to;
121
147
  }
122
148
  // Check if it's a bidirectional converter
123
149
  if (typeof converter === 'object' && 'to' in converter && 'from' in converter) {
124
150
  // Handle bidirectional converter
125
151
  const biConverter = converter;
152
+ // Pass the original metadata objects (not just names) to preserve metadata
126
153
  return this.register(from, to, biConverter.to).register(to, from, biConverter.from);
127
154
  }
128
155
  // Handle unidirectional converter
129
156
  const newGraph = new Map(this.graph);
130
- if (!newGraph.has(from)) {
131
- newGraph.set(from, new Map());
157
+ if (!newGraph.has(fromName)) {
158
+ newGraph.set(fromName, new Map());
132
159
  }
133
- const fromMap = new Map(newGraph.get(from));
134
- fromMap.set(to, converter);
135
- newGraph.set(from, fromMap);
160
+ const fromMap = new Map(newGraph.get(fromName));
161
+ fromMap.set(toName, converter);
162
+ newGraph.set(fromName, fromMap);
136
163
  // Return new registry instance (immutable) with proxy
137
- return createRegistryFromGraph(newGraph, new Map(), this.metadata);
164
+ return createRegistryFromGraph(newGraph, new Map(), newMetadata);
138
165
  }
139
166
  registerBidirectional(from, to, converter) {
140
167
  return this.register(from, to, converter.to).register(to, from, converter.from);
141
168
  }
142
169
  allow(from, to) {
170
+ // Extract unit names from metadata objects if provided
171
+ const fromName = typeof from === 'object' && 'name' in from ? from.name : from;
172
+ const toName = typeof to === 'object' && 'name' in to ? to.name : to;
143
173
  // Verify that a conversion path exists at runtime
144
- const converter = this.getConverter(from, to);
174
+ const converter = this.getConverter(fromName, toName);
145
175
  if (!converter) {
146
- throw new ConversionError(from, to, 'No conversion path exists');
176
+ throw new ConversionError(fromName, toName, 'No conversion path exists');
147
177
  }
148
178
  // Return the same registry instance with updated type information
149
179
  // The actual conversion already works via BFS, we just need to expose it in types
@@ -208,10 +238,10 @@ class ConverterRegistryImpl {
208
238
  *
209
239
  * @example
210
240
  * ```typescript
211
- * type Celsius = WithUnits<number, 'Celsius'>;
212
- * type Fahrenheit = WithUnits<number, 'Fahrenheit'>;
213
- * type Meters = WithUnits<number, 'meters'>;
214
- * type Kilometers = WithUnits<number, 'kilometers'>;
241
+ * type Celsius = WithUnits<PrimitiveType, 'Celsius'>;
242
+ * type Fahrenheit = WithUnits<PrimitiveType, 'Fahrenheit'>;
243
+ * type Meters = WithUnits<PrimitiveType, 'meters'>;
244
+ * type Kilometers = WithUnits<PrimitiveType, 'kilometers'>;
215
245
  *
216
246
  * // Without pre-declared units
217
247
  * const registry = createRegistry()