unacy 0.6.0 → 0.8.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 (40) hide show
  1. package/README.md +193 -47
  2. package/dist/converters.d.ts +28 -4
  3. package/dist/converters.d.ts.map +1 -1
  4. package/dist/errors.js.map +1 -1
  5. package/dist/index.d.ts +5 -4
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/registry.d.ts +39 -41
  10. package/dist/registry.d.ts.map +1 -1
  11. package/dist/registry.js +58 -14
  12. package/dist/registry.js.map +1 -1
  13. package/dist/types.d.ts +159 -18
  14. package/dist/types.d.ts.map +1 -1
  15. package/dist/types.js.map +1 -1
  16. package/dist/utils/validation.d.ts +106 -0
  17. package/dist/utils/validation.d.ts.map +1 -1
  18. package/dist/utils/validation.js +317 -0
  19. package/dist/utils/validation.js.map +1 -1
  20. package/package.json +16 -8
  21. package/dist/__tests__/converters.test.d.ts +0 -2
  22. package/dist/__tests__/converters.test.d.ts.map +0 -1
  23. package/dist/__tests__/converters.test.js +0 -128
  24. package/dist/__tests__/converters.test.js.map +0 -1
  25. package/dist/__tests__/errors.test.d.ts +0 -2
  26. package/dist/__tests__/errors.test.d.ts.map +0 -1
  27. package/dist/__tests__/errors.test.js +0 -93
  28. package/dist/__tests__/errors.test.js.map +0 -1
  29. package/dist/__tests__/formatters.test.d.ts +0 -2
  30. package/dist/__tests__/formatters.test.d.ts.map +0 -1
  31. package/dist/__tests__/formatters.test.js +0 -244
  32. package/dist/__tests__/formatters.test.js.map +0 -1
  33. package/dist/__tests__/registry.test.d.ts +0 -2
  34. package/dist/__tests__/registry.test.d.ts.map +0 -1
  35. package/dist/__tests__/registry.test.js +0 -403
  36. package/dist/__tests__/registry.test.js.map +0 -1
  37. package/dist/__tests__/types.test.d.ts +0 -2
  38. package/dist/__tests__/types.test.d.ts.map +0 -1
  39. package/dist/__tests__/types.test.js +0 -115
  40. package/dist/__tests__/types.test.js.map +0 -1
package/README.md CHANGED
@@ -11,6 +11,8 @@ 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
15
+ - 🧩 **Non-Primitive Types** - First-class support for enums, classes, records, and tuples
14
16
 
15
17
  ## Installation
16
18
 
@@ -21,15 +23,27 @@ pnpm add @unacy/core
21
23
  ## Quick Start
22
24
 
23
25
  ```typescript
24
- import { createRegistry, type WithUnits } from '@unacy/core';
26
+ import { createRegistry } from '@unacy/core';
27
+ import type { WithTypedUnits } from '@unacy/core';
25
28
 
26
- // Define your unit types
27
- type Celsius = WithUnits<number, 'Celsius'>;
28
- type Fahrenheit = WithUnits<number, 'Fahrenheit'>;
29
+ // Define metadata for your units (name + type)
30
+ const CelsiusMetadata = {
31
+ name: 'Celsius' as const,
32
+ type: 'number' as const
33
+ };
34
+
35
+ const FahrenheitMetadata = {
36
+ name: 'Fahrenheit' as const,
37
+ type: 'number' as const
38
+ };
29
39
 
30
- // Create a registry
40
+ // Define your unit types with metadata
41
+ type Celsius = WithTypedUnits<typeof CelsiusMetadata>;
42
+ type Fahrenheit = WithTypedUnits<typeof FahrenheitMetadata>;
43
+
44
+ // Create a registry and register converters
31
45
  const tempRegistry = createRegistry()
32
- .register('Celsius', 'Fahrenheit', (c) => ((c * 9/5) + 32) as Fahrenheit);
46
+ .register(CelsiusMetadata, FahrenheitMetadata, (c) => ((c * 9/5) + 32));
33
47
 
34
48
  // Create branded values using callable accessors (NEW!)
35
49
  const temp = tempRegistry.Celsius(25); // Returns Celsius type
@@ -44,9 +58,6 @@ const fahrenheit2 = tempRegistry.Celsius.to.Fahrenheit(tempRegistry.Celsius(30))
44
58
 
45
59
  console.log(fahrenheit1); // 77
46
60
  console.log(fahrenheit2); // 86
47
-
48
- // Old way still works (manual casting)
49
- const tempOld: Celsius = 25 as Celsius;
50
61
  ```
51
62
 
52
63
  ## Usage Examples
@@ -57,15 +68,12 @@ Unit accessors are now callable functions that create branded values:
57
68
 
58
69
  ```typescript
59
70
  // 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'>
71
+ const temp = registry.Celsius(25); // Returns WithTypedUnits<typeof CelsiusMetadata>
72
+ const distance = registry.meters(100); // Returns WithTypedUnits<typeof MetersMetadata>
62
73
 
63
74
  // Fluent workflow
64
75
  const fahrenheit = registry.Celsius.to.Fahrenheit(registry.Celsius(20));
65
76
 
66
- // Compare with old way (still works)
67
- const tempOld: Celsius = 25 as Celsius;
68
-
69
77
  // Benefits:
70
78
  // - Cleaner syntax
71
79
  // - Less verbose than manual casting
@@ -73,11 +81,32 @@ const tempOld: Celsius = 25 as Celsius;
73
81
  // - Works seamlessly with conversions
74
82
  ```
75
83
 
84
+ ### Typed Metadata
85
+
86
+ Define metadata with minimal required fields (name + type):
87
+
88
+ ```typescript
89
+ const CelsiusMetadata = {
90
+ name: 'Celsius' as const,
91
+ type: 'number' as const
92
+ };
93
+
94
+ const EtherMetadata = {
95
+ name: 'ether' as const,
96
+ type: 'bigint' as const
97
+ };
98
+
99
+ const FlagMetadata = {
100
+ name: 'enabled' as const,
101
+ type: 'boolean' as const
102
+ };
103
+ ```
104
+
76
105
  ### Basic Unit Conversions
77
106
 
78
107
  ```typescript
79
108
  // Same registry as above
80
- const distance: Meters = 10 as Meters;
109
+ const distance = distanceRegistry.meters(10);
81
110
 
82
111
  // Access units directly via property syntax
83
112
  const feet = distanceRegistry.meters.to.feet(distance);
@@ -87,28 +116,38 @@ console.log(feet); // 32.8084
87
116
  const feet2 = distanceRegistry.meters.to.feet(distanceRegistry.meters(10));
88
117
 
89
118
  // Works in both directions
90
- const meters = distanceRegistry.feet.to.meters(32.8084 as Feet) satisfies Meters;
119
+ const meters = distanceRegistry.feet.to.meters(distanceRegistry.feet(32.8084));
91
120
  console.log(meters); // 10
92
121
  ```
93
122
 
94
123
  ### Bidirectional Converters
95
124
 
96
125
  ```typescript
97
- import { createRegistry, type WithUnits } from '@unacy/core';
126
+ import { createRegistry } from '@unacy/core';
127
+ import type { WithTypedUnits } from '@unacy/core';
98
128
 
99
- type Meters = WithUnits<number, 'meters'>;
100
- type Kilometers = WithUnits<number, 'kilometers'>;
129
+ const MetersMetadata = {
130
+ name: 'meters' as const,
131
+ type: 'number' as const
132
+ };
101
133
 
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
- });
134
+ const KilometersMetadata = {
135
+ name: 'kilometers' as const,
136
+ type: 'number' as const
137
+ };
138
+
139
+ type Meters = WithTypedUnits<typeof MetersMetadata>;
140
+ type Kilometers = WithTypedUnits<typeof KilometersMetadata>;
141
+
142
+ const registry = createRegistry()
143
+ .register(MetersMetadata, KilometersMetadata, {
144
+ to: (m: number) => (m / 1000),
145
+ from: (km: number) => (km * 1000)
107
146
  });
108
147
 
109
148
  // Both directions work automatically
110
- const km = registry.convert(5000 as Meters, 'meters').to('kilometers'); // 5
111
- const m = registry.convert(5 as Kilometers, 'kilometers').to('meters'); // 5000
149
+ const km = registry.convert(registry.meters(5000), 'meters').to('kilometers'); // 5
150
+ const m = registry.convert(registry.kilometers(5), 'kilometers').to('meters'); // 5000
112
151
  ```
113
152
 
114
153
  ### Multi-Hop Auto-Composition
@@ -116,24 +155,27 @@ const m = registry.convert(5 as Kilometers, 'kilometers').to('meters'); // 5000
116
155
  The registry automatically composes converters via shortest path:
117
156
 
118
157
  ```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
158
+ const MetersMetadata = { name: 'meters' as const, type: 'number' as const };
159
+ const KilometersMetadata = { name: 'kilometers' as const, type: 'number' as const };
160
+ const MilesMetadata = { name: 'miles' as const, type: 'number' as const };
161
+
162
+ type Meters = WithTypedUnits<typeof MetersMetadata>;
163
+ type Kilometers = WithTypedUnits<typeof KilometersMetadata>;
164
+ type Miles = WithTypedUnits<typeof MilesMetadata>;
165
+
166
+ const registry = createRegistry()
167
+ .register(MetersMetadata, KilometersMetadata, {
168
+ to: (m: number) => (m / 1000),
169
+ from: (km: number) => (km * 1000)
127
170
  })
128
- .registerBidirectional('kilometers', 'miles', {
129
- to: (km) => (km * 0.621371) as Miles,
130
- from: (mi) => (mi / 0.621371) as Kilometers
171
+ .register(KilometersMetadata, MilesMetadata, {
172
+ to: (km: number) => (km * 0.621371),
173
+ from: (mi: number) => (mi / 0.621371)
131
174
  });
132
175
 
133
176
  // No direct meters→miles converter registered!
134
177
  // Registry auto-composes: meters → kilometers → miles
135
- const meters: Meters = 5000 as Meters;
136
- const miles = registry.convert(meters, 'meters').to('miles');
178
+ const miles = registry.convert(registry.meters(5000), 'meters').to('miles');
137
179
  console.log(miles); // 3.106855
138
180
  ```
139
181
 
@@ -165,18 +207,121 @@ const str = iso8601.format(now); // "2026-01-06T12:00:00.000Z"
165
207
  const date = iso8601.parse('2026-01-06T12:00:00.000Z');
166
208
  ```
167
209
 
210
+ ### Non-Primitive Types
211
+
212
+ Beyond primitives, the registry supports enums, classes, records, and tuples
213
+ as unit types. The `type` field in metadata IS the runtime value itself.
214
+
215
+ #### Enum Units
216
+
217
+ ```typescript
218
+ import { createRegistry } from '@unacy/core';
219
+ import type { WithTypedUnits, TypedMetadata } from '@unacy/core';
220
+
221
+ enum LogLevel { DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3 }
222
+
223
+ const LogLevelMeta = { name: 'LogLevel', type: LogLevel } as const;
224
+ type LogLevelUnit = WithTypedUnits<typeof LogLevelMeta>;
225
+
226
+ const registry = createRegistry()
227
+ .register(LogLevelMeta, /* ... */);
228
+ ```
229
+
230
+ #### Class Units
231
+
232
+ ```typescript
233
+ class Temperature {
234
+ constructor(public value: number, public scale: string) {}
235
+ }
236
+
237
+ const TempMeta = { name: 'Temperature', type: Temperature } as const;
238
+ type TempUnit = WithTypedUnits<typeof TempMeta>;
239
+ ```
240
+
241
+ #### Record Units
242
+
243
+ ```typescript
244
+ const PointSchema = { x: 'number', y: 'number' } as const;
245
+ const PointMeta = { name: 'Point', type: PointSchema } as const;
246
+ type PointUnit = WithTypedUnits<typeof PointMeta>;
247
+
248
+ // Nested schemas
249
+ const LineMeta = {
250
+ name: 'Line',
251
+ type: { start: { x: 'number', y: 'number' }, end: { x: 'number', y: 'number' } }
252
+ } as const;
253
+ ```
254
+
255
+ #### Tuple Units
256
+
257
+ ```typescript
258
+ const RGBSchema = ['number', 'number', 'number'] as const;
259
+ const RGBMeta = { name: 'RGB', type: RGBSchema } as const;
260
+ type RGBUnit = WithTypedUnits<typeof RGBMeta>;
261
+
262
+ // Optional and rest elements
263
+ const HeaderSchema = ['string', 'number?', '...string'] as const;
264
+ ```
265
+
266
+ #### Runtime Type Guards
267
+
268
+ ```typescript
269
+ import {
270
+ isEnumMetadata, isClassMetadata,
271
+ isRecordMetadata, isTupleMetadata,
272
+ detectMetadataKind
273
+ } from '@unacy/core';
274
+
275
+ detectMetadataKind(LogLevelMeta); // 'enum'
276
+ detectMetadataKind(TempMeta); // 'class'
277
+ detectMetadataKind(PointMeta); // 'record'
278
+ detectMetadataKind(RGBMeta); // 'tuple'
279
+
280
+ if (isEnumMetadata(meta)) {
281
+ // meta.type is narrowed to EnumType
282
+ }
283
+ ```
284
+
168
285
  ## API Reference
169
286
 
170
287
  ### Types
171
288
 
289
+ #### `WithTypedUnits<M extends TypedMetadata<T>>`
290
+ Brand a value with strongly-typed metadata for compile-time unit safety.
291
+
292
+ ```typescript
293
+ const CelsiusMetadata = { name: 'Celsius' as const, type: 'number' as const };
294
+ type Celsius = WithTypedUnits<typeof CelsiusMetadata>;
295
+ const temp: Celsius = tempRegistry.Celsius(25);
296
+ ```
297
+
172
298
  #### `WithUnits<T, U>`
173
- Brand a value with a unit identifier for compile-time safety.
299
+ Legacy: Brand a value with a unit identifier for compile-time safety.
174
300
 
175
301
  ```typescript
176
302
  type Celsius = WithUnits<number, 'Celsius'>;
177
303
  const temp: Celsius = 25 as Celsius;
178
304
  ```
179
305
 
306
+ #### `TypedMetadata<T>`
307
+ Minimal metadata type with name and type information.
308
+
309
+ ```typescript
310
+ type NumericMetadata = TypedMetadata<number>;
311
+ // { name: string; type: 'number' }
312
+
313
+ type StringMetadata = TypedMetadata<string>;
314
+ // { name: string; type: 'string' }
315
+
316
+ // Non-primitive: type IS the value itself
317
+ type EnumMetadata = TypedMetadata<typeof LogLevel>;
318
+ // { name: string; type: typeof LogLevel }
319
+ ```
320
+
321
+ #### `SupportedType`
322
+ Union of all types that can be used as a unit base:
323
+ `number | string | boolean | bigint | EnumType | ClassType | RecordSchema | TupleSchema`
324
+
180
325
  #### `WithFormat<T, F>`
181
326
  Brand a value with a format identifier for serialization safety.
182
327
 
@@ -198,8 +343,8 @@ Pair of converters for two-way transformations.
198
343
 
199
344
  ```typescript
200
345
  const meterKm: BidirectionalConverter<Meters, Kilometers> = {
201
- to: (m) => (m / 1000) as Kilometers,
202
- from: (km) => (km * 1000) as Meters
346
+ to: (m: number) => (m / 1000),
347
+ from: (km: number) => (km * 1000)
203
348
  };
204
349
  ```
205
350
 
@@ -216,14 +361,14 @@ const registry = createRegistry<'A' | 'B' | 'C'>();
216
361
  Register a unidirectional converter.
217
362
 
218
363
  ```typescript
219
- registry.register('Celsius', 'Fahrenheit', celsiusToFahrenheit);
364
+ registry.register(CelsiusMetadata, FahrenheitMetadata, celsiusToFahrenheit);
220
365
  ```
221
366
 
222
- #### `registerBidirectional(from, to, converter)`
367
+ #### `register(from, to, converter)` (bidirectional)
223
368
  Register both directions at once.
224
369
 
225
370
  ```typescript
226
- registry.registerBidirectional('meters', 'kilometers', meterKm);
371
+ registry.register(MetersMetadata, KilometersMetadata, meterKm);
227
372
  ```
228
373
 
229
374
  #### `convert(value, fromUnit).to(toUnit)`
@@ -243,11 +388,12 @@ const result = registry.convert(value, 'Celsius').to('Fahrenheit');
243
388
 
244
389
  ## Best Practices
245
390
 
246
- 1. **Define unit types at module boundaries** for consistency
391
+ 1. **Define metadata as const at module boundaries** for consistency
247
392
  2. **Use bidirectional converters** when both directions are needed
248
393
  3. **Document precision loss** in converters
249
394
  4. **Cache registries** for performance
250
- 5. **Validate with Zod** in parsers
395
+ 5. **Use `WithTypedUnits`** for brand-new code; leverage type inference
396
+ 6. **Validate with Zod** in parsers
251
397
 
252
398
  ## Performance
253
399
 
@@ -2,7 +2,7 @@
2
2
  * Type-safe converter function signatures
3
3
  * @packageDocumentation
4
4
  */
5
- import type { PrimitiveType, Relax as BaseRelax } from './types.js';
5
+ import type { PrimitiveType, Relax as BaseRelax, Unwrap } from './types.js';
6
6
  /**
7
7
  * Unidirectional converter from one unit to another.
8
8
  *
@@ -25,7 +25,7 @@ import type { PrimitiveType, Relax as BaseRelax } from './types.js';
25
25
  */
26
26
  export type Converter<TInput, TOutput> = (input: TInput) => TOutput;
27
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<infer A extends PrimitiveType, infer B extends PrimitiveType> ? RelaxConverter<T> : RelaxBidirectionalConverter<T>;
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
  *
@@ -53,10 +53,34 @@ export type BidirectionalConverter<TInput, TOutput> = {
53
53
  from: Converter<TOutput, TInput>;
54
54
  };
55
55
  export type RelaxBidirectionalConverter<ConverterType> = ConverterType extends BidirectionalConverter<infer A extends PrimitiveType, infer B extends PrimitiveType> ? {
56
- to: (input: BaseRelax<A>) => BaseRelax<B>;
57
- from: (input: BaseRelax<B>) => BaseRelax<A>;
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;
61
61
  };
62
+ /**
63
+ * A converter that accepts the branded input type but returns
64
+ * unwrapped output. This eliminates the need to cast return values
65
+ * to branded types inside converter functions, while preserving
66
+ * full autocompletion on the input parameter.
67
+ *
68
+ * Since `Tagged<T, ...> extends T`, strict converters returning branded
69
+ * types are also assignable to this type.
70
+ *
71
+ * @template TInput - Source unit-tagged type
72
+ * @template TOutput - Destination unit-tagged type
73
+ */
74
+ export type RelaxedConverter<TInput, TOutput> = (input: TInput) => Unwrap<TOutput>;
75
+ /**
76
+ * A bidirectional converter with relaxed (unwrapped) output types.
77
+ * Input remains branded for full autocompletion.
78
+ *
79
+ * @template TInput - First unit-tagged type
80
+ * @template TOutput - Second unit-tagged type
81
+ */
82
+ export type RelaxedBidirectionalConverter<TInput, TOutput> = {
83
+ to: RelaxedConverter<TInput, TOutput>;
84
+ from: RelaxedConverter<TOutput, TInput>;
85
+ };
62
86
  //# sourceMappingURL=converters.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"converters.d.ts","sourceRoot":"","sources":["../src/converters.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAqB,KAAK,IAAI,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,SAAS,CACnB,MAAM,EACN,OAAO,IACL,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;AAE/B,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,MAAM,CAAC,SAAS,aAAa,EAAE,MAAM,CAAC,SAAS,aAAa,CAAC,GAC/E,cAAc,CAAC,CAAC,CAAC,GACjB,2BAA2B,CAAC,CAAC,CAAC,CAAC;AAErC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,sBAAsB,CAChC,MAAM,EACN,OAAO,IACL;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,SAAS,aAAa,EAAE,MAAM,CAAC,SAAS,aAAa,CAAC,GACtG;IACE,EAAE,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC;IAC1C,IAAI,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC;CAC7C,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,EAAE,MAAM,YAAY,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;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;AAER;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,gBAAgB,CAAC,MAAM,EAAE,OAAO,IAAI,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,OAAO,CAAC,CAAC;AAEnF;;;;;;GAMG;AACH,MAAM,MAAM,6BAA6B,CAAC,MAAM,EAAE,OAAO,IAAI;IAC3D,EAAE,EAAE,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,IAAI,EAAE,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;CACzC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,OAAO,UAAW,SAAQ,KAAK;IACnC,YAAY,OAAe,EAAE;QAC3B,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QAEzB,wDAAwD;QACxD,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC;IAAA,CACnD;CACF;AAED;;GAEG;AACH,MAAM,OAAO,UAAW,SAAQ,UAAU;IACxB,IAAI,CAAgB;IAEpC,YAAY,IAAmB,EAAE;QAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAK,CAAC,CAAC;QAC7C,KAAK,CAAC,sCAAsC,OAAO,EAAE,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAAA,CAClB;CACF;AAED;;GAEG;AACH,MAAM,OAAO,aAAc,SAAQ,UAAU;IAC3B,IAAI,CAAc;IAClB,EAAE,CAAc;IAChB,QAAQ,CAAS;IAEjC,YAAY,IAAiB,EAAE,EAAe,EAAE,QAAgB,EAAE;QAChE,KAAK,CACH,+BAA+B,QAAQ,kCAAkC,MAAM,CAAC,IAAI,CAAC,OAAO,MAAM,CAAC,EAAE,CAAC,EAAE,CACzG,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAAA,CAC1B;CACF;AAED;;GAEG;AACH,MAAM,OAAO,eAAgB,SAAQ,UAAU;IAC7B,IAAI,CAAc;IAClB,EAAE,CAAc;IAEhC,YAAY,IAAiB,EAAE,EAAe,EAAE,MAAe,EAAE;QAC/D,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9C,KAAK,CAAC,uBAAuB,MAAM,CAAC,IAAI,CAAC,OAAO,MAAM,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,CAAC;QAC1E,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IAAA,CACd;CACF;AAED;;GAEG;AACH,MAAM,OAAO,UAAW,SAAQ,UAAU;IACxB,MAAM,CAAS;IACf,KAAK,CAAS;IACd,MAAM,CAAS;IAE/B,YAAY,MAAc,EAAE,KAAa,EAAE,MAAc,EAAE;QACzD,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;QAC9E,MAAM,YAAY,GAAG,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC;QAE1D,KAAK,CAAC,iBAAiB,YAAY,QAAQ,MAAM,KAAK,MAAM,EAAE,CAAC,CAAC;QAChE,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IAAA,CACtB;CACF"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,OAAO,UAAW,SAAQ,KAAK;IACnC,YAAY,OAAe,EAAE;QAC3B,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QAEzB,wDAAwD;QACxD,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC;IAAA,CACnD;CACF;AAED;;GAEG;AACH,MAAM,OAAO,UAAW,SAAQ,UAAU;IACxB,IAAI,CAAgB;IAEpC,YAAY,IAAmB,EAAE;QAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7C,KAAK,CAAC,sCAAsC,OAAO,EAAE,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAAA,CAClB;CACF;AAED;;GAEG;AACH,MAAM,OAAO,aAAc,SAAQ,UAAU;IAC3B,IAAI,CAAc;IAClB,EAAE,CAAc;IAChB,QAAQ,CAAS;IAEjC,YAAY,IAAiB,EAAE,EAAe,EAAE,QAAgB,EAAE;QAChE,KAAK,CACH,+BAA+B,QAAQ,kCAAkC,MAAM,CAAC,IAAI,CAAC,OAAO,MAAM,CAAC,EAAE,CAAC,EAAE,CACzG,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAAA,CAC1B;CACF;AAED;;GAEG;AACH,MAAM,OAAO,eAAgB,SAAQ,UAAU;IAC7B,IAAI,CAAc;IAClB,EAAE,CAAc;IAEhC,YAAY,IAAiB,EAAE,EAAe,EAAE,MAAe,EAAE;QAC/D,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9C,KAAK,CAAC,uBAAuB,MAAM,CAAC,IAAI,CAAC,OAAO,MAAM,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,CAAC;QAC1E,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IAAA,CACd;CACF;AAED;;GAEG;AACH,MAAM,OAAO,UAAW,SAAQ,UAAU;IACxB,MAAM,CAAS;IACf,KAAK,CAAS;IACd,MAAM,CAAS;IAE/B,YAAY,MAAc,EAAE,KAAa,EAAE,MAAc,EAAE;QACzD,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;QAC9E,MAAM,YAAY,GAAG,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC;QAE1D,KAAK,CAAC,iBAAiB,YAAY,QAAQ,MAAM,KAAK,MAAM,EAAE,CAAC,CAAC;QAChE,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IAAA,CACtB;CACF"}
package/dist/index.d.ts CHANGED
@@ -2,11 +2,12 @@
2
2
  * Unacy Core - Type-safe unit and format conversion library
3
3
  * @packageDocumentation
4
4
  */
5
- export type { WithUnits, WithFormat, UnitMetadata, Relax, PrimitiveType, ToPrimitiveType, ToPrimitiveTypeName } from './types.js';
6
- export type { Converter, BidirectionalConverter } from './converters.js';
5
+ export type { WithUnits, WithTypedUnits, WithFormat, BaseMetadata, TypedMetadata, UnitMetadata, Relax, PrimitiveType, SupportedType, EnumType, ClassType, RecordSchema, TupleSchema, ToPrimitiveTypeName } from './types.js';
6
+ export type { Converter, BidirectionalConverter, RelaxedConverter, RelaxedBidirectionalConverter } from './converters.js';
7
7
  export type { Formatter, Parser, FormatterParser } from './formatters.js';
8
- export type { ConverterRegistry, ConverterMap, UnitAccessor } 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
- export { createParserWithSchema } from './utils/validation.js';
11
+ export { createParserWithSchema, validateEnum, validateClass, validateRecordSchema, validateTupleSchema, isEnumMetadata, isClassMetadata, isRecordMetadata, isTupleMetadata, detectMetadataKind } from './utils/validation.js';
12
+ export type { PrimitiveTypeFromName, InferFromRecordSchema, InferFromTupleSchema } from './types.js';
12
13
  //# sourceMappingURL=index.d.ts.map
@@ -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,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAGlI,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,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACnF,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,aAAa,EACb,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,WAAW,EACX,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAGpB,YAAY,EACV,SAAS,EACT,sBAAsB,EACtB,gBAAgB,EAChB,6BAA6B,EAC9B,MAAM,iBAAiB,CAAC;AAGzB,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,EACL,sBAAsB,EACtB,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,kBAAkB,EACnB,MAAM,uBAAuB,CAAC;AAG/B,YAAY,EACV,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -6,5 +6,5 @@ export { createRegistry } from './registry.js';
6
6
  // Errors
7
7
  export { UnacyError, CycleError, MaxDepthError, ConversionError, ParseError } from './errors.js';
8
8
  // Utilities
9
- export { createParserWithSchema } from './utils/validation.js';
9
+ export { createParserWithSchema, validateEnum, validateClass, validateRecordSchema, validateTupleSchema, isEnumMetadata, isClassMetadata, isRecordMetadata, isTupleMetadata, detectMetadataKind } from './utils/validation.js';
10
10
  //# sourceMappingURL=index.js.map
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;AAiCH,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,EACL,sBAAsB,EACtB,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,kBAAkB,EACnB,MAAM,uBAAuB,CAAC"}
@@ -2,14 +2,14 @@
2
2
  * Converter registry with auto-composition via BFS
3
3
  * @packageDocumentation
4
4
  */
5
- import type { Converter, BidirectionalConverter, Relax } from './converters.js';
6
- import type { UnitsFor, WithUnits, UnitMetadata, PrimitiveType, Relax as RelaxUnits } from './types.js';
5
+ import type { Converter, RelaxedConverter, RelaxedBidirectionalConverter } from './converters.js';
6
+ import type { UnitsFor, WithUnits, TypedMetadata, NameFor, WithTypedUnits, UnitsOf, BaseMetadata, SupportedType, InferCallableArgs, Relax as RelaxUnits } from './types.js';
7
7
  /**
8
8
  * Represents a conversion edge from one unit to another
9
9
  * Edges store the unit names as strings, but the type system ensures
10
10
  * they correspond to valid WithUnits types
11
11
  */
12
- type Edge<From extends WithUnits = WithUnits<PrimitiveType, string>, To extends WithUnits = WithUnits<PrimitiveType, string>> = readonly [From, To];
12
+ type Edge<From extends WithTypedUnits<any> = WithTypedUnits<TypedMetadata<SupportedType>>, To extends WithTypedUnits<any> = WithTypedUnits<TypedMetadata<SupportedType>>> = readonly [From, To];
13
13
  /**
14
14
  * Extract all unique 'from' units from a list of edges
15
15
  */
@@ -17,79 +17,77 @@ type FromUnits<Edges extends readonly Edge[]> = Edges[number][0];
17
17
  /**
18
18
  * Extract all 'to' units for a specific 'from' unit string
19
19
  */
20
- type ToUnitsFor<Edges extends readonly Edge[], FromUnit extends WithUnits> = 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
+ */
21
24
  /**
22
25
  * Type for unit accessor with metadata and conversion methods
23
26
  * Can be called as a function to create branded unit values
24
27
  */
25
- export type UnitAccessor<From extends WithUnits<PrimitiveType, FromUnits>, Edges extends readonly Edge[], FromUnits extends string> = {
28
+ export type UnitAccessor<From extends WithTypedUnits<TypedMetadata<SupportedType>>, Edges extends readonly Edge[]> = {
26
29
  /**
27
- * Create a branded value with this unit
28
- * @param value - The numeric value to brand
30
+ * Create a branded value with this unit.
31
+ * For tuples, pass members as individual arguments.
32
+ * For classes, pass constructor parameters directly.
33
+ * @param args - The value(s) to brand
29
34
  * @returns The value branded with this unit type
30
35
  */
31
- (value: number): From;
36
+ (...args: InferCallableArgs<From>): From;
32
37
  to: {
33
- [To in ToUnitsFor<Edges, From> as UnitsFor<To>]: (value: RelaxUnits<From>) => WithUnits<PrimitiveType, To>;
38
+ [To in ToUnitsFor<Edges, From> as UnitsFor<To>]: (value: RelaxUnits<From>) => To;
34
39
  };
35
40
  /**
36
41
  * Add metadata to this unit
37
42
  */
38
- addMetadata(metadata: UnitMetadata): ConverterRegistry<Edges extends readonly (infer E)[] ? E[] : never> & ConverterMap<Edges>;
43
+ addMetadata<NewM extends Record<string, unknown>>(metadata: NewM): UnitRegistry<Edges extends readonly (infer E)[] ? E[] : never> & UnitMap<Edges>;
39
44
  /**
40
45
  * Register a converter from this unit to another unit
41
46
  */
42
- register<To extends WithUnits<PrimitiveType, ToUnits>, ToUnits extends string>(to: ToUnits, converter: Relax<Converter<From, To>>): ConverterRegistry<[...Edges, Edge<From, To>]> & ConverterMap<[...Edges, Edge<From, To>]>;
43
- register<To extends WithUnits<PrimitiveType, ToUnits>, ToUnits extends string>(to: ToUnits, converter: Relax<BidirectionalConverter<From, To>>): ConverterRegistry<[...Edges, Edge<From, To>, Edge<To, From>]> & ConverterMap<[...Edges, Edge<From, To>, Edge<To, From>]>;
44
- } & {
45
- [K: string]: unknown;
46
- };
47
+ register<To extends WithTypedUnits<ToMeta>, ToMeta extends TypedMetadata<SupportedType>>(to: UnitsFor<To> | ToMeta, converter: RelaxedConverter<From, To>): UnitRegistry<[...Edges, Edge<From, To>]> & UnitMap<[...Edges, Edge<From, To>]>;
48
+ register<To extends WithTypedUnits<ToMeta>, ToMeta extends TypedMetadata<SupportedType>>(to: UnitsFor<To> | ToMeta, converter: RelaxedBidirectionalConverter<From, To>): UnitRegistry<[...Edges, Edge<From, To>, Edge<To, From>]> & UnitMap<[...Edges, Edge<From, To>, Edge<To, From>]>;
49
+ } & UnitsOf<From>;
47
50
  /**
48
51
  * Type for unit-based conversion accessors
49
52
  * Provides the shape: registry.Celsius.to.Fahrenheit(value)
50
53
  * Only allows conversions that have been registered
51
54
  */
52
- export type ConverterMap<Edges extends readonly Edge[]> = {
53
- [FU in FromUnits<Edges> as UnitsFor<FU>]: UnitAccessor<WithUnits<PrimitiveType, UnitsFor<FU>>, Edges, UnitsFor<FromUnits<Edges>>>;
55
+ export type UnitMap<Edges extends readonly Edge[]> = {
56
+ [FU in FromUnits<Edges> as UnitsFor<FU>]: UnitAccessor<FU, Edges>;
54
57
  };
55
58
  /**
56
59
  * Registry for managing and composing unit converters
57
60
  */
58
- export interface ConverterRegistry<Edges extends Edge[] = []> {
61
+ export interface UnitRegistry<Edges extends Edge[] = []> {
62
+ register<From extends WithTypedUnits<FromMeta>, FromMeta extends TypedMetadata<SupportedType>>(unit: FromMeta): this & {
63
+ [K in NameFor<From>]: UnitAccessor<From, Edges>;
64
+ };
59
65
  /**
60
66
  * Register a unidirectional converter
61
67
  *
62
- * @param from - Source unit
63
- * @param to - Destination unit
64
- * @param converter - Converter function
68
+ * @param from - Source unit (string name or metadata object)
69
+ * @param to - Destination unit (string name or metadata object)
70
+ * @param converter - Converter function (input is branded, output can be plain or branded)
65
71
  * @returns New registry instance with the converter registered
66
72
  */
67
- register<From extends WithUnits<PrimitiveType, UF>, To extends WithUnits<PrimitiveType, UY>, UF extends string, UY extends string>(from: UF, to: UY, converter: Converter<From, RelaxUnits<To>>): ConverterRegistry<[...Edges, Edge<From, To>]> & ConverterMap<[...Edges, Edge<From, To>]>;
73
+ register<From extends WithTypedUnits<FromMeta>, To extends WithTypedUnits<ToMeta>, FromMeta extends TypedMetadata<SupportedType>, ToMeta extends TypedMetadata<SupportedType>>(from: UnitsFor<From> | FromMeta, to: UnitsFor<To> | ToMeta, converter: RelaxedConverter<From, To>): UnitRegistry<[...Edges, Edge<From, To>]> & UnitMap<[...Edges, Edge<From, To>]>;
68
74
  /**
69
75
  * Register a bidirectional converter (both directions)
70
76
  *
71
- * @param from - First unit
72
- * @param to - Second unit
73
- * @param converter - Bidirectional converter object
77
+ * @param from - First unit (string name or metadata object)
78
+ * @param to - Second unit (string name or metadata object)
79
+ * @param converter - Bidirectional converter object (input branded, output can be plain or branded)
74
80
  * @returns New registry instance with both converters registered
75
81
  */
76
- register<From extends WithUnits<PrimitiveType, UF>, To extends WithUnits<PrimitiveType, UY>, UF extends string, UY extends string>(from: UF, to: UY, converter: BidirectionalConverter<RelaxUnits<From>, RelaxUnits<To>>): ConverterRegistry<[
77
- ...Edges,
78
- Edge<From, To>,
79
- Edge<To, From>
80
- ]> & ConverterMap<[
81
- ...Edges,
82
- Edge<From, To>,
83
- Edge<To, From>
84
- ]>;
82
+ register<From extends WithTypedUnits<FromMeta>, To extends WithTypedUnits<ToMeta>, FromMeta extends TypedMetadata<SupportedType>, ToMeta extends TypedMetadata<SupportedType>>(from: UnitsFor<From> | FromMeta, to: UnitsFor<To> | ToMeta, converter: RelaxedBidirectionalConverter<From, To>): UnitRegistry<[...Edges, Edge<From, To>, Edge<To, From>]> & UnitMap<[...Edges, Edge<From, To>, Edge<To, From>]>;
85
83
  /**
86
84
  * Explicitly allow a conversion path in the type system (for multi-hop conversions)
87
85
  *
88
86
  * This method verifies that a conversion path exists at runtime (via BFS) and adds it
89
87
  * to the type system so it can be used with type-safe accessor syntax.
90
88
  *
91
- * @param from - Source unit string
92
- * @param to - Destination unit string
89
+ * @param from - Source unit (string name or metadata object)
90
+ * @param to - Destination unit (string name or metadata object)
93
91
  * @returns New registry instance with the conversion path enabled in types
94
92
  * @throws ConversionError if no path exists between the units
95
93
  *
@@ -104,7 +102,7 @@ export interface ConverterRegistry<Edges extends Edge[] = []> {
104
102
  * const f = registry.Celsius.to.Fahrenheit(temp);
105
103
  * ```
106
104
  */
107
- allow<From extends WithUnits<PrimitiveType, UF>, To extends WithUnits<PrimitiveType, UY>, UF extends string, UY extends string>(from: UF, to: UY): ConverterRegistry<[...Edges, Edge<From, To>]> & ConverterMap<[...Edges, Edge<From, To>]>;
105
+ allow<From extends WithTypedUnits<FromMeta>, To extends WithTypedUnits<ToMeta>, FromMeta extends TypedMetadata<SupportedType>, ToMeta extends TypedMetadata<SupportedType>>(from: UnitsFor<From> | FromMeta, to: UnitsFor<To> | ToMeta): UnitRegistry<[...Edges, Edge<From, To>]> & UnitMap<[...Edges, Edge<From, To>]>;
108
106
  /**
109
107
  * Get a converter (direct or composed via BFS)
110
108
  *
@@ -112,7 +110,7 @@ export interface ConverterRegistry<Edges extends Edge[] = []> {
112
110
  * @param to - Destination unit
113
111
  * @returns Converter function, or undefined if no path exists
114
112
  */
115
- getConverter<From extends WithUnits<PrimitiveType, string>, To extends WithUnits<PrimitiveType, string>>(from: UnitsFor<From>, to: UnitsFor<To>): Converter<From, To> | undefined;
113
+ getConverter<From extends WithUnits<SupportedType, BaseMetadata>, To extends WithUnits<SupportedType, BaseMetadata>>(from: UnitsFor<From>, to: UnitsFor<To>): Converter<From, To> | undefined;
116
114
  /**
117
115
  * Convert a value using fluent API
118
116
  *
@@ -120,8 +118,8 @@ export interface ConverterRegistry<Edges extends Edge[] = []> {
120
118
  * @param fromUnit - Source unit
121
119
  * @returns Object with to() method for conversion
122
120
  */
123
- convert<From extends WithUnits<PrimitiveType, string>>(value: From, fromUnit: UnitsFor<From>): {
124
- to<To extends WithUnits<PrimitiveType, string>>(unit: UnitsFor<To>): To;
121
+ convert<From extends WithUnits<SupportedType, BaseMetadata>>(value: From, fromUnit: UnitsFor<From>): {
122
+ to<To extends WithUnits<SupportedType, BaseMetadata>>(unit: UnitsFor<To>): To;
125
123
  };
126
124
  }
127
125
  /**
@@ -150,6 +148,6 @@ export interface ConverterRegistry<Edges extends Edge[] = []> {
150
148
  * console.log(fahrenheit); // 77
151
149
  * ```
152
150
  */
153
- export declare function createRegistry<Edges extends readonly Edge[] = []>(): ConverterRegistry<Edges extends readonly (infer E)[] ? E[] : never> & ConverterMap<Edges>;
151
+ export declare function createRegistry<Edges extends readonly Edge[] = []>(): UnitRegistry<Edges extends readonly (infer E)[] ? E[] : never> & UnitMap<Edges>;
154
152
  export {};
155
153
  //# sourceMappingURL=registry.d.ts.map