unacy 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/__tests__/converters.test.d.ts +2 -0
  2. package/dist/__tests__/converters.test.d.ts.map +1 -0
  3. package/dist/__tests__/converters.test.js +128 -0
  4. package/dist/__tests__/converters.test.js.map +1 -0
  5. package/dist/__tests__/errors.test.d.ts +2 -0
  6. package/dist/__tests__/errors.test.d.ts.map +1 -0
  7. package/dist/__tests__/errors.test.js +93 -0
  8. package/dist/__tests__/errors.test.js.map +1 -0
  9. package/dist/__tests__/formatters.test.d.ts +2 -0
  10. package/dist/__tests__/formatters.test.d.ts.map +1 -0
  11. package/dist/__tests__/formatters.test.js +244 -0
  12. package/dist/__tests__/formatters.test.js.map +1 -0
  13. package/dist/__tests__/registry.test.d.ts +2 -0
  14. package/dist/__tests__/registry.test.d.ts.map +1 -0
  15. package/dist/__tests__/registry.test.js +250 -0
  16. package/dist/__tests__/registry.test.js.map +1 -0
  17. package/dist/__tests__/types.test.d.ts +2 -0
  18. package/dist/__tests__/types.test.js.map +1 -1
  19. package/dist/converters.d.ts +53 -0
  20. package/dist/converters.d.ts.map +1 -0
  21. package/dist/converters.js +6 -0
  22. package/dist/converters.js.map +1 -0
  23. package/dist/errors.d.ts +40 -0
  24. package/dist/errors.d.ts.map +1 -0
  25. package/dist/errors.js +70 -0
  26. package/dist/errors.js.map +1 -0
  27. package/dist/formatters.d.ts +82 -0
  28. package/dist/formatters.d.ts.map +1 -0
  29. package/dist/formatters.js +6 -0
  30. package/dist/formatters.js.map +1 -0
  31. package/dist/index.d.ts +12 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +10 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/registry.d.ts +141 -0
  36. package/dist/registry.d.ts.map +1 -0
  37. package/dist/registry.js +212 -0
  38. package/dist/registry.js.map +1 -0
  39. package/dist/types.d.ts +36 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/types.js +6 -0
  42. package/dist/types.js.map +1 -0
  43. package/dist/utils/graph.d.ts +26 -0
  44. package/dist/utils/graph.d.ts.map +1 -0
  45. package/dist/utils/graph.js +91 -0
  46. package/dist/utils/graph.js.map +1 -0
  47. package/dist/utils/validation.d.ts +26 -0
  48. package/dist/utils/validation.d.ts.map +1 -0
  49. package/dist/utils/validation.js +35 -0
  50. package/dist/utils/validation.js.map +1 -0
  51. package/package.json +7 -7
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Converter registry with auto-composition via BFS
3
+ * @packageDocumentation
4
+ */
5
+ import type { Converter, BidirectionalConverter } from './converters';
6
+ import type { RelaxedWithUnits, UnitsFor, WithUnits } from './types';
7
+ /**
8
+ * Represents a conversion edge from one unit to another
9
+ */
10
+ type Edge<From extends string = string, To extends string = string> = readonly [From, To];
11
+ /**
12
+ * Extract all unique 'from' units from a list of edges
13
+ */
14
+ type FromUnits<Edges extends readonly Edge[]> = Edges[number][0];
15
+ /**
16
+ * Extract all 'to' units for a specific 'from' unit string
17
+ */
18
+ type ToUnitsFor<Edges extends readonly Edge[], FromUnit extends string> = Extract<Edges[number], readonly [FromUnit, any]>[1];
19
+ /**
20
+ * Type for unit-based conversion accessors
21
+ * Provides the shape: registry.Celsius.to.Fahrenheit(value)
22
+ * Only allows conversions that have been registered
23
+ */
24
+ export type ConverterMap<Edges extends readonly Edge[]> = {
25
+ [From in FromUnits<Edges>]: {
26
+ to: {
27
+ [To in ToUnitsFor<Edges, From>]: (value: RelaxedWithUnits<number, From>) => WithUnits<number, To>;
28
+ };
29
+ };
30
+ };
31
+ /**
32
+ * Registry for managing and composing unit converters
33
+ */
34
+ export interface ConverterRegistry<Edges extends Edge[] = []> {
35
+ /**
36
+ * Register a unidirectional converter
37
+ *
38
+ * @param from - Source unit
39
+ * @param to - Destination unit
40
+ * @param converter - Converter function
41
+ * @returns New registry instance with the converter registered
42
+ */
43
+ register<From extends WithUnits<number, string>, To extends WithUnits<number, string>>(from: UnitsFor<From>, to: UnitsFor<To>, converter: Converter<From, To>): ConverterRegistry<[...Edges, Edge<UnitsFor<From>, UnitsFor<To>>]> & ConverterMap<[...Edges, Edge<UnitsFor<From>, UnitsFor<To>>]>;
44
+ /**
45
+ * Register a bidirectional converter (both directions)
46
+ *
47
+ * @param from - First unit
48
+ * @param to - Second unit
49
+ * @param converter - Bidirectional converter object
50
+ * @returns New registry instance with both converters registered
51
+ */
52
+ register<From extends WithUnits<number, string>, To extends WithUnits<number, string>>(from: UnitsFor<From>, to: UnitsFor<To>, converter: BidirectionalConverter<From, To>): ConverterRegistry<[
53
+ ...Edges,
54
+ Edge<UnitsFor<From>, UnitsFor<To>>,
55
+ Edge<UnitsFor<To>, UnitsFor<From>>
56
+ ]> & ConverterMap<[
57
+ ...Edges,
58
+ Edge<UnitsFor<From>, UnitsFor<To>>,
59
+ Edge<UnitsFor<To>, UnitsFor<From>>
60
+ ]>;
61
+ /**
62
+ * Register a bidirectional converter (both directions)
63
+ * @deprecated Use register() with BidirectionalConverter instead
64
+ *
65
+ * @param from - First unit
66
+ * @param to - Second unit
67
+ * @param converter - Bidirectional converter object
68
+ * @returns New registry instance with both converters registered
69
+ */
70
+ registerBidirectional<From extends WithUnits<number, string>, To extends WithUnits<number, string>>(from: UnitsFor<From>, to: UnitsFor<To>, converter: BidirectionalConverter<From, To>): ConverterRegistry<[
71
+ ...Edges,
72
+ Edge<UnitsFor<From>, UnitsFor<To>>,
73
+ Edge<UnitsFor<To>, UnitsFor<From>>
74
+ ]> & ConverterMap<[
75
+ ...Edges,
76
+ Edge<UnitsFor<From>, UnitsFor<To>>,
77
+ Edge<UnitsFor<To>, UnitsFor<From>>
78
+ ]>;
79
+ /**
80
+ * Explicitly allow a conversion path in the type system (for multi-hop conversions)
81
+ *
82
+ * This method verifies that a conversion path exists at runtime (via BFS) and adds it
83
+ * to the type system so it can be used with type-safe accessor syntax.
84
+ *
85
+ * @param from - Source unit string
86
+ * @param to - Destination unit string
87
+ * @returns New registry instance with the conversion path enabled in types
88
+ * @throws ConversionError if no path exists between the units
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * const registry = createRegistry()
93
+ * .register('Celsius', 'Kelvin', c => (c + 273.15) as Kelvin)
94
+ * .register('Kelvin', 'Fahrenheit', k => ((k - 273.15) * 9/5 + 32) as Fahrenheit)
95
+ * .allow('Celsius', 'Fahrenheit'); // Enable multi-hop path in types
96
+ *
97
+ * // Now type-safe:
98
+ * const f = registry.Celsius.to.Fahrenheit(temp);
99
+ * ```
100
+ */
101
+ allow<From extends string, To extends string>(from: From, to: To): ConverterRegistry<[...Edges, Edge<From, To>]> & ConverterMap<[...Edges, Edge<From, To>]>;
102
+ /**
103
+ * Get a converter (direct or composed via BFS)
104
+ *
105
+ * @param from - Source unit
106
+ * @param to - Destination unit
107
+ * @returns Converter function, or undefined if no path exists
108
+ */
109
+ getConverter<From extends WithUnits<number, string>, To extends WithUnits<number, string>>(from: UnitsFor<From>, to: UnitsFor<To>): Converter<From, To> | undefined;
110
+ /**
111
+ * Convert a value using fluent API
112
+ *
113
+ * @param value - Value to convert
114
+ * @param fromUnit - Source unit
115
+ * @returns Object with to() method for conversion
116
+ */
117
+ convert<From extends WithUnits<number, string>>(value: From, fromUnit: UnitsFor<From>): {
118
+ to<To extends WithUnits<number, string>>(unit: UnitsFor<To>): To;
119
+ };
120
+ }
121
+ /**
122
+ * Create a new converter registry
123
+ *
124
+ * @returns Empty converter registry with unit-based accessors
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * type Celsius = WithUnits<number, 'Celsius'>;
129
+ * type Fahrenheit = WithUnits<number, 'Fahrenheit'>;
130
+ *
131
+ * const registry = createRegistry()
132
+ * .register('Celsius', 'Fahrenheit', (c: Celsius) => ((c * 9/5) + 32) as Fahrenheit);
133
+ *
134
+ * const temp: Celsius = 25 as Celsius;
135
+ * const fahrenheit = registry.Celsius.to.Fahrenheit(temp);
136
+ * console.log(fahrenheit); // 77
137
+ * ```
138
+ */
139
+ export declare function createRegistry(): ConverterRegistry<[]> & ConverterMap<[]>;
140
+ export {};
141
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAKrE;;GAEG;AACH,KAAK,IAAI,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,EAAE,EAAE,SAAS,MAAM,GAAG,MAAM,IAAI,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAE1F;;GAEG;AACH,KAAK,SAAS,CAAC,KAAK,SAAS,SAAS,IAAI,EAAE,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AAEjE;;GAEG;AACH,KAAK,UAAU,CAAC,KAAK,SAAS,SAAS,IAAI,EAAE,EAAE,QAAQ,SAAS,MAAM,IAAI,OAAO,CAC/E,KAAK,CAAC,MAAM,CAAC,EACb,SAAS,CAAC,QAAQ,EAAE,GAAG,CAAC,CACzB,CAAC,CAAC,CAAC,CAAC;AAEL;;;;GAIG;AACH,MAAM,MAAM,YAAY,CAAC,KAAK,SAAS,SAAS,IAAI,EAAE,IAAI;KACvD,IAAI,IAAI,SAAS,CAAC,KAAK,CAAC,GAAG;QAC1B,EAAE,EAAE;aACD,EAAE,IAAI,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAC/B,KAAK,EAAE,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,KACN,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;SACvD,CAAC;KACH;CACF,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,iBAAiB,CAAC,KAAK,SAAS,IAAI,EAAE,GAAG,EAAE;IAC1D;;;;;;;OAOG;IACH,QAAQ,CAAC,IAAI,SAAS,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EACnF,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,EACpB,EAAE,EAAE,QAAQ,CAAC,EAAE,CAAC,EAChB,SAAS,EAAE,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,GAC7B,iBAAiB,CAAC,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAClE,YAAY,CAAC,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/D;;;;;;;OAOG;IACH,QAAQ,CAAC,IAAI,SAAS,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EACnF,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,EACpB,EAAE,EAAE,QAAQ,CAAC,EAAE,CAAC,EAChB,SAAS,EAAE,sBAAsB,CAAC,IAAI,EAAE,EAAE,CAAC,GAC1C,iBAAiB,CAClB;QAAC,GAAG,KAAK;QAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;QAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;KAAC,CACnF,GACC,YAAY,CACV;QAAC,GAAG,KAAK;QAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;QAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;KAAC,CACnF,CAAC;IAEJ;;;;;;;;OAQG;IACH,qBAAqB,CACnB,IAAI,SAAS,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EACtC,EAAE,SAAS,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EAEpC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,EACpB,EAAE,EAAE,QAAQ,CAAC,EAAE,CAAC,EAChB,SAAS,EAAE,sBAAsB,CAAC,IAAI,EAAE,EAAE,CAAC,GAC1C,iBAAiB,CAClB;QAAC,GAAG,KAAK;QAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;QAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;KAAC,CACnF,GACC,YAAY,CACV;QAAC,GAAG,KAAK;QAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;QAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;KAAC,CACnF,CAAC;IACJ;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,KAAK,CAAC,IAAI,SAAS,MAAM,EAAE,EAAE,SAAS,MAAM,EAC1C,IAAI,EAAE,IAAI,EACV,EAAE,EAAE,EAAE,GACL,iBAAiB,CAAC,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5F;;;;;;OAMG;IACH,YAAY,CAAC,IAAI,SAAS,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EACvF,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,EACpB,EAAE,EAAE,QAAQ,CAAC,EAAE,CAAC,GACf,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,SAAS,CAAC;IACnC;;;;;;OAMG;IACH,OAAO,CAAC,IAAI,SAAS,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5C,KAAK,EAAE,IAAI,EACX,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,GACvB;QACD,EAAE,CAAC,EAAE,SAAS,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC;KAClE,CAAC;CACH;AAoND;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,IAAI,iBAAiB,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,EAAE,CAAC,CAEzE"}
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Converter registry with auto-composition via BFS
3
+ * @packageDocumentation
4
+ */
5
+ import { ConversionError } from './errors';
6
+ import { findShortestPath, composeConverters } from './utils/graph';
7
+ /**
8
+ * Internal implementation of ConverterRegistry
9
+ */
10
+ class ConverterRegistryImpl {
11
+ graph;
12
+ pathCache;
13
+ unitAccessors;
14
+ constructor(graph, pathCache) {
15
+ this.graph = graph || new Map();
16
+ this.pathCache = pathCache || new Map();
17
+ this.unitAccessors = new Map();
18
+ // Build unit accessors dynamically
19
+ this.buildUnitAccessors();
20
+ }
21
+ /**
22
+ * Build dynamic unit accessors for fluent API: registry.Celsius.to.Fahrenheit(value)
23
+ */
24
+ buildUnitAccessors() {
25
+ // Get all unique units from the graph (both from and to)
26
+ const allUnits = new Set();
27
+ for (const from of this.graph.keys()) {
28
+ allUnits.add(from);
29
+ const toMap = this.graph.get(from);
30
+ if (toMap) {
31
+ for (const to of toMap.keys()) {
32
+ allUnits.add(to);
33
+ }
34
+ }
35
+ }
36
+ // For each unit, create a `to` object with converter functions
37
+ for (const fromUnit of allUnits) {
38
+ const toAccessors = {};
39
+ for (const toUnit of allUnits) {
40
+ if (fromUnit !== toUnit) {
41
+ // Create converter function that will look up the converter at call time
42
+ toAccessors[toUnit] = (value) => {
43
+ const converter = this.getConverter(fromUnit, toUnit);
44
+ if (!converter) {
45
+ throw new ConversionError(fromUnit, toUnit, 'No converter found');
46
+ }
47
+ return converter(value);
48
+ };
49
+ }
50
+ }
51
+ // Wrap toAccessors in a Proxy to handle unknown units dynamically
52
+ const toProxy = new Proxy(toAccessors, {
53
+ get: (target, toProp) => {
54
+ if (typeof toProp === 'symbol') {
55
+ return target[toProp];
56
+ }
57
+ // If accessor exists, return it
58
+ if (toProp in target) {
59
+ return target[toProp];
60
+ }
61
+ // Otherwise, create a dynamic converter function
62
+ return (value) => {
63
+ const converter = this.getConverter(fromUnit, toProp);
64
+ if (!converter) {
65
+ throw new ConversionError(fromUnit, toProp, 'No converter found');
66
+ }
67
+ return converter(value);
68
+ };
69
+ }
70
+ });
71
+ this.unitAccessors.set(fromUnit, { to: toProxy });
72
+ }
73
+ }
74
+ register(from, to, converter) {
75
+ // Check if it's a bidirectional converter
76
+ if (typeof converter === 'object' && 'to' in converter && 'from' in converter) {
77
+ // Handle bidirectional converter
78
+ const biConverter = converter;
79
+ return this.register(from, to, biConverter.to).register(to, from, biConverter.from);
80
+ }
81
+ // Handle unidirectional converter
82
+ const newGraph = new Map(this.graph);
83
+ if (!newGraph.has(from)) {
84
+ newGraph.set(from, new Map());
85
+ }
86
+ const fromMap = new Map(newGraph.get(from));
87
+ fromMap.set(to, converter);
88
+ newGraph.set(from, fromMap);
89
+ // Return new registry instance (immutable) with proxy
90
+ return createRegistryFromGraph(newGraph, new Map());
91
+ }
92
+ registerBidirectional(from, to, converter) {
93
+ return this.register(from, to, converter.to).register(to, from, converter.from);
94
+ }
95
+ allow(from, to) {
96
+ // Verify that a conversion path exists at runtime
97
+ const converter = this.getConverter(from, to);
98
+ if (!converter) {
99
+ throw new ConversionError(from, to, 'No conversion path exists');
100
+ }
101
+ // Return the same registry instance with updated type information
102
+ // The actual conversion already works via BFS, we just need to expose it in types
103
+ return this;
104
+ }
105
+ getConverter(from, to) {
106
+ // Check cache first
107
+ const cacheKey = `${String(from)}->${String(to)}`;
108
+ const cached = this.pathCache.get(cacheKey);
109
+ if (cached) {
110
+ return cached;
111
+ }
112
+ // Try direct lookup (O(1))
113
+ const fromMap = this.graph.get(from);
114
+ if (fromMap) {
115
+ const direct = fromMap.get(to);
116
+ if (direct) {
117
+ this.pathCache.set(cacheKey, direct);
118
+ return direct;
119
+ }
120
+ }
121
+ // Fallback to BFS for multi-hop path
122
+ try {
123
+ const path = findShortestPath(from, to, this.graph);
124
+ if (!path) {
125
+ return undefined;
126
+ }
127
+ const composed = composeConverters(path, this.graph);
128
+ this.pathCache.set(cacheKey, composed);
129
+ return composed;
130
+ }
131
+ catch (error) {
132
+ // For cycle and max depth errors, we should propagate them
133
+ // For other errors, return undefined
134
+ if (error instanceof Error &&
135
+ (error.constructor.name === 'CycleError' || error.constructor.name === 'MaxDepthError')) {
136
+ throw error;
137
+ }
138
+ return undefined;
139
+ }
140
+ }
141
+ convert(value, fromUnit) {
142
+ return {
143
+ to: (unit) => {
144
+ const converter = this.getConverter(fromUnit, unit);
145
+ if (!converter) {
146
+ throw new ConversionError(fromUnit, unit, 'No converter found');
147
+ }
148
+ return converter(value);
149
+ }
150
+ };
151
+ }
152
+ }
153
+ /**
154
+ * Create a new converter registry
155
+ *
156
+ * @returns Empty converter registry with unit-based accessors
157
+ *
158
+ * @example
159
+ * ```typescript
160
+ * type Celsius = WithUnits<number, 'Celsius'>;
161
+ * type Fahrenheit = WithUnits<number, 'Fahrenheit'>;
162
+ *
163
+ * const registry = createRegistry()
164
+ * .register('Celsius', 'Fahrenheit', (c: Celsius) => ((c * 9/5) + 32) as Fahrenheit);
165
+ *
166
+ * const temp: Celsius = 25 as Celsius;
167
+ * const fahrenheit = registry.Celsius.to.Fahrenheit(temp);
168
+ * console.log(fahrenheit); // 77
169
+ * ```
170
+ */
171
+ export function createRegistry() {
172
+ return createRegistryFromGraph();
173
+ }
174
+ /**
175
+ * Internal helper to create a registry with proxy from an existing graph
176
+ */
177
+ function createRegistryFromGraph(graph, pathCache) {
178
+ const registryImpl = new ConverterRegistryImpl(graph, pathCache);
179
+ // Create a Proxy to intercept property access for unit accessors
180
+ return new Proxy(registryImpl, {
181
+ get(target, prop) {
182
+ // If the property exists on the registry implementation, return it
183
+ if (prop in target || typeof prop === 'symbol') {
184
+ return target[prop];
185
+ }
186
+ // Otherwise, check if it's a unit accessor
187
+ const unitAccessor = target.unitAccessors.get(prop);
188
+ if (unitAccessor) {
189
+ return unitAccessor;
190
+ }
191
+ // For unknown units, create a dynamic accessor that will throw ConversionError
192
+ // This handles cases where a unit is referenced but not in the registry
193
+ return {
194
+ to: new Proxy({}, {
195
+ get: (_, toProp) => {
196
+ if (typeof toProp === 'symbol') {
197
+ return undefined;
198
+ }
199
+ return (value) => {
200
+ const converter = target.getConverter(prop, toProp);
201
+ if (!converter) {
202
+ throw new ConversionError(prop, toProp, 'No converter found');
203
+ }
204
+ return converter(value);
205
+ };
206
+ }
207
+ })
208
+ };
209
+ }
210
+ });
211
+ }
212
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAkJpE;;GAEG;AACH,MAAM,qBAAqB;IACR,KAAK,CAA0D;IAC/D,SAAS,CAAmC;IAC5C,aAAa,CAAwB;IAEtD,YACE,KAA+D,EAC/D,SAA4C,EAC5C;QACA,IAAI,CAAC,KAAK,GAAG,KAAK,IAAI,IAAI,GAAG,EAAE,CAAC;QAChC,IAAI,CAAC,SAAS,GAAG,SAAS,IAAI,IAAI,GAAG,EAAE,CAAC;QACxC,IAAI,CAAC,aAAa,GAAG,IAAI,GAAG,EAAE,CAAC;QAE/B,mCAAmC;QACnC,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAAA,CAC3B;IAED;;OAEG;IACK,kBAAkB,GAAS;QACjC,yDAAyD;QACzD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAe,CAAC;QACxC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;YACrC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACnB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACnC,IAAI,KAAK,EAAE,CAAC;gBACV,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;oBAC9B,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACnB,CAAC;YACH,CAAC;QACH,CAAC;QAED,+DAA+D;QAC/D,KAAK,MAAM,QAAQ,IAAI,QAAQ,EAAE,CAAC;YAChC,MAAM,WAAW,GAAQ,EAAE,CAAC;YAE5B,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;gBAC9B,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;oBACxB,yEAAyE;oBACzE,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,KAAU,EAAE,EAAE,CAAC;wBACpC,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,QAAe,EAAE,MAAa,CAAC,CAAC;wBACpE,IAAI,CAAC,SAAS,EAAE,CAAC;4BACf,MAAM,IAAI,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,oBAAoB,CAAC,CAAC;wBACpE,CAAC;wBACD,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;oBAAA,CACzB,CAAC;gBACJ,CAAC;YACH,CAAC;YAED,kEAAkE;YAClE,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,WAAW,EAAE;gBACrC,GAAG,EAAE,CAAC,MAAW,EAAE,MAAuB,EAAE,EAAE,CAAC;oBAC7C,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;wBAC/B,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC;oBACxB,CAAC;oBACD,gCAAgC;oBAChC,IAAI,MAAM,IAAI,MAAM,EAAE,CAAC;wBACrB,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC;oBACxB,CAAC;oBACD,iDAAiD;oBACjD,OAAO,CAAC,KAAU,EAAE,EAAE,CAAC;wBACrB,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,QAAe,EAAE,MAAa,CAAC,CAAC;wBACpE,IAAI,CAAC,SAAS,EAAE,CAAC;4BACf,MAAM,IAAI,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,oBAAoB,CAAC,CAAC;wBACpE,CAAC;wBACD,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;oBAAA,CACzB,CAAC;gBAAA,CACH;aACF,CAAC,CAAC;YAEH,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QACpD,CAAC;IAAA,CACF;IAED,QAAQ,CACN,IAAoB,EACpB,EAAgB,EAChB,SAAiE,EACrB;QAC5C,0CAA0C;QAC1C,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,IAAI,IAAI,SAAS,IAAI,MAAM,IAAI,SAAS,EAAE,CAAC;YAC9E,iCAAiC;YACjC,MAAM,WAAW,GAAG,SAA6C,CAAC;YAClE,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CACrD,EAAS,EACT,IAAW,EACX,WAAW,CAAC,IAAW,CACjB,CAAC;QACX,CAAC;QAED,kCAAkC;QAClC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAErC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;QAChC,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,SAAgC,CAAC,CAAC;QAClD,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAE5B,sDAAsD;QACtD,OAAO,uBAAuB,CAC5B,QAAQ,EACR,IAAI,GAAG,EAAE,CACV,CAAC;IAAA,CACH;IAED,qBAAqB,CAInB,IAAoB,EACpB,EAAgB,EAChB,SAA2C,EAMzC;QACF,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC,QAAQ,CACnD,EAAS,EACT,IAAW,EACX,SAAS,CAAC,IAAW,CACf,CAAC;IAAA,CACV;IAED,KAAK,CACH,IAAU,EACV,EAAM,EACoF;QAC1F,kDAAkD;QAClD,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,IAAW,EAAE,EAAS,CAAC,CAAC;QAC5D,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,eAAe,CAAC,IAAI,EAAE,EAAE,EAAE,2BAA2B,CAAC,CAAC;QACnE,CAAC;QAED,kEAAkE;QAClE,kFAAkF;QAClF,OAAO,IAAW,CAAC;IAAA,CACpB;IAED,YAAY,CACV,IAAoB,EACpB,EAAgB,EACiB;QACjC,oBAAoB;QACpB,MAAM,QAAQ,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;QAClD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,2BAA2B;QAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC/B,IAAI,MAAM,EAAE,CAAC;gBACX,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;gBACrC,OAAO,MAAM,CAAC;YAChB,CAAC;QACH,CAAC;QAED,qCAAqC;QACrC,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;YAEpD,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,OAAO,SAAS,CAAC;YACnB,CAAC;YAED,MAAM,QAAQ,GAAG,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;YACrD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACvC,OAAO,QAAQ,CAAC;QAClB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,2DAA2D;YAC3D,qCAAqC;YACrC,IACE,KAAK,YAAY,KAAK;gBACtB,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,KAAK,YAAY,IAAI,KAAK,CAAC,WAAW,CAAC,IAAI,KAAK,eAAe,CAAC,EACvF,CAAC;gBACD,MAAM,KAAK,CAAC;YACd,CAAC;YACD,OAAO,SAAS,CAAC;QACnB,CAAC;IAAA,CACF;IAED,OAAO,CACL,KAAW,EACX,QAAwB,EAGxB;QACA,OAAO;YACL,EAAE,EAAE,CAAuC,IAAkB,EAAM,EAAE,CAAC;gBACpE,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,QAAe,EAAE,IAAW,CAAC,CAAC;gBAClE,IAAI,CAAC,SAAS,EAAE,CAAC;oBACf,MAAM,IAAI,eAAe,CAAC,QAAQ,EAAE,IAAI,EAAE,oBAAoB,CAAC,CAAC;gBAClE,CAAC;gBACD,OAAO,SAAS,CAAC,KAAK,CAAO,CAAC;YAAA,CAC/B;SACF,CAAC;IAAA,CACH;CACF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,cAAc,GAA6C;IACzE,OAAO,uBAAuB,EAAM,CAAC;AAAA,CACtC;AAED;;GAEG;AACH,SAAS,uBAAuB,CAC9B,KAA+D,EAC/D,SAA4C,EACI;IAChD,MAAM,YAAY,GAAG,IAAI,qBAAqB,CAAQ,KAAK,EAAE,SAAS,CAAC,CAAC;IAExE,iEAAiE;IACjE,OAAO,IAAI,KAAK,CAAC,YAAY,EAAE;QAC7B,GAAG,CAAC,MAAW,EAAE,IAAqB,EAAO;YAC3C,mEAAmE;YACnE,IAAI,IAAI,IAAI,MAAM,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC/C,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;YACtB,CAAC;YAED,2CAA2C;YAC3C,MAAM,YAAY,GAAG,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACpD,IAAI,YAAY,EAAE,CAAC;gBACjB,OAAO,YAAY,CAAC;YACtB,CAAC;YAED,+EAA+E;YAC/E,wEAAwE;YACxE,OAAO;gBACL,EAAE,EAAE,IAAI,KAAK,CACX,EAAE,EACF;oBACE,GAAG,EAAE,CAAC,CAAM,EAAE,MAAuB,EAAE,EAAE,CAAC;wBACxC,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;4BAC/B,OAAO,SAAS,CAAC;wBACnB,CAAC;wBACD,OAAO,CAAC,KAAU,EAAE,EAAE,CAAC;4BACrB,MAAM,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,IAAW,EAAE,MAAa,CAAC,CAAC;4BAClE,IAAI,CAAC,SAAS,EAAE,CAAC;gCACf,MAAM,IAAI,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,oBAAoB,CAAC,CAAC;4BAChE,CAAC;4BACD,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;wBAAA,CACzB,CAAC;oBAAA,CACH;iBACF,CACF;aACF,CAAC;QAAA,CACH;KACF,CAAmD,CAAC;AAAA,CACtD"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Core type branding utilities for unit and format safety
3
+ * @packageDocumentation
4
+ */
5
+ import type { GetTagMetadata, Tagged } from 'type-fest';
6
+ export declare const UNITS: unique symbol;
7
+ /**
8
+ * Brand a value with a unit identifier for compile-time unit safety.
9
+ *
10
+ * @template T - Base type (e.g., number, bigint)
11
+ * @template U - Unit identifier (e.g., 'Celsius', 'meters')
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * type Celsius = WithUnits<number, 'Celsius'>;
16
+ * const temp: Celsius = 25 as Celsius;
17
+ * ```
18
+ */
19
+ export type WithUnits<T, U extends string> = Tagged<T, typeof UNITS, U>;
20
+ export type RelaxedWithUnits<T, U extends string> = T | WithUnits<T, U>;
21
+ export type Relax<T> = T extends WithUnits<infer U, string> ? U : T;
22
+ /**
23
+ * Brand a value with a format identifier for compile-time format safety.
24
+ *
25
+ * @template T - Base type (e.g., Date, number, string)
26
+ * @template F - Format identifier (e.g., 'ISO8601', 'UnixTimestamp')
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * type ISO8601 = WithFormat<Date, 'ISO8601'>;
31
+ * const date: ISO8601 = new Date() as ISO8601;
32
+ * ```
33
+ */
34
+ export type WithFormat<T, F extends string> = Tagged<T, typeof UNITS, F>;
35
+ export type UnitsFor<T extends WithUnits<unknown, string>> = GetTagMetadata<T, typeof UNITS>;
36
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAExD,eAAO,MAAM,KAAK,EAAE,OAAO,MAAwB,CAAC;AACpD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,IAAI,MAAM,CAAC,CAAC,EAAE,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC;AAExE,MAAM,MAAM,gBAAgB,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAExE,MAAM,MAAM,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAEpE;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,IAAI,MAAM,CAAC,CAAC,EAAE,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC;AAEzE,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,cAAc,CAAC,CAAC,EAAE,OAAO,KAAK,CAAC,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Core type branding utilities for unit and format safety
3
+ * @packageDocumentation
4
+ */
5
+ export const UNITS = Symbol('UNITS');
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,CAAC,MAAM,KAAK,GAAkB,MAAM,CAAC,OAAO,CAAC,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Graph traversal utilities for converter registry
3
+ * @packageDocumentation
4
+ */
5
+ import type { Converter } from '../converters';
6
+ /**
7
+ * Find the shortest path between two nodes using BFS.
8
+ *
9
+ * @param from - Starting node
10
+ * @param to - Target node
11
+ * @param adjacencyMap - Graph represented as adjacency list
12
+ * @returns Array of nodes representing the shortest path, or null if no path exists
13
+ * @throws {CycleError} If a cycle is detected during traversal
14
+ * @throws {MaxDepthError} If path depth exceeds MAX_DEPTH
15
+ */
16
+ export declare function findShortestPath(from: PropertyKey, to: PropertyKey, adjacencyMap: Map<PropertyKey, Map<PropertyKey, unknown>>): PropertyKey[] | null;
17
+ /**
18
+ * Compose multiple converters along a path into a single converter.
19
+ *
20
+ * @param path - Array of nodes representing the conversion path
21
+ * @param registry - Map of converters keyed by [from, to]
22
+ * @returns Composed converter function
23
+ * @throws {Error} If any converter in the path is missing
24
+ */
25
+ export declare function composeConverters(path: PropertyKey[], registry: Map<PropertyKey, Map<PropertyKey, Converter<any, any>>>): Converter<any, any>;
26
+ //# sourceMappingURL=graph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/utils/graph.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAO/C;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,WAAW,EACjB,EAAE,EAAE,WAAW,EACf,YAAY,EAAE,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,GACxD,WAAW,EAAE,GAAG,IAAI,CA8CtB;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,WAAW,EAAE,EACnB,QAAQ,EAAE,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,GAChE,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,CA4BrB"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Graph traversal utilities for converter registry
3
+ * @packageDocumentation
4
+ */
5
+ import { CycleError, MaxDepthError } from '../errors';
6
+ /**
7
+ * Maximum allowed conversion path depth to prevent infinite loops
8
+ */
9
+ const MAX_DEPTH = 5;
10
+ /**
11
+ * Find the shortest path between two nodes using BFS.
12
+ *
13
+ * @param from - Starting node
14
+ * @param to - Target node
15
+ * @param adjacencyMap - Graph represented as adjacency list
16
+ * @returns Array of nodes representing the shortest path, or null if no path exists
17
+ * @throws {CycleError} If a cycle is detected during traversal
18
+ * @throws {MaxDepthError} If path depth exceeds MAX_DEPTH
19
+ */
20
+ export function findShortestPath(from, to, adjacencyMap) {
21
+ // Handle self-conversion (cycle detection)
22
+ if (from === to) {
23
+ throw new CycleError([from, to]);
24
+ }
25
+ // BFS queue: [currentNode, path]
26
+ const queue = [[from, [from]]];
27
+ const visited = new Set();
28
+ visited.add(from);
29
+ while (queue.length > 0) {
30
+ const [current, path] = queue.shift();
31
+ // Get neighbors
32
+ const neighbors = adjacencyMap.get(current);
33
+ if (!neighbors) {
34
+ continue;
35
+ }
36
+ // Explore neighbors
37
+ for (const neighbor of neighbors.keys()) {
38
+ // Check max depth before extending path
39
+ // path currently has N nodes (N-1 edges); adding neighbor would make N+1 nodes (N edges)
40
+ const numEdges = path.length; // Number of edges after adding neighbor
41
+ if (numEdges > MAX_DEPTH) {
42
+ throw new MaxDepthError(from, to, MAX_DEPTH);
43
+ }
44
+ // Found target
45
+ if (neighbor === to) {
46
+ return [...path, neighbor];
47
+ }
48
+ // Cycle detection: if we've already visited this node in this path, skip it
49
+ if (visited.has(neighbor)) {
50
+ continue;
51
+ }
52
+ visited.add(neighbor);
53
+ queue.push([neighbor, [...path, neighbor]]);
54
+ }
55
+ }
56
+ // No path found
57
+ return null;
58
+ }
59
+ /**
60
+ * Compose multiple converters along a path into a single converter.
61
+ *
62
+ * @param path - Array of nodes representing the conversion path
63
+ * @param registry - Map of converters keyed by [from, to]
64
+ * @returns Composed converter function
65
+ * @throws {Error} If any converter in the path is missing
66
+ */
67
+ export function composeConverters(path, registry) {
68
+ if (path.length < 2) {
69
+ throw new Error('Path must contain at least 2 nodes');
70
+ }
71
+ // Build array of converters
72
+ const converters = [];
73
+ for (let i = 0; i < path.length - 1; i++) {
74
+ const from = path[i];
75
+ const to = path[i + 1];
76
+ const converterMap = registry.get(from);
77
+ if (!converterMap) {
78
+ throw new Error(`No converters registered from ${String(from)}`);
79
+ }
80
+ const converter = converterMap.get(to);
81
+ if (!converter) {
82
+ throw new Error(`No converter registered from ${String(from)} to ${String(to)}`);
83
+ }
84
+ converters.push(converter);
85
+ }
86
+ // Compose converters: apply them left-to-right
87
+ return (input) => {
88
+ return converters.reduce((value, converter) => converter(value), input);
89
+ };
90
+ }
91
+ //# sourceMappingURL=graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph.js","sourceRoot":"","sources":["../../src/utils/graph.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Runtime validation helpers for parsers
3
+ * @packageDocumentation
4
+ */
5
+ import type { ZodSchema } from 'zod';
6
+ import type { Parser } from '../formatters';
7
+ import type { WithFormat } from '../types';
8
+ /**
9
+ * Create a parser with Zod schema validation.
10
+ *
11
+ * @template F - Format identifier
12
+ * @template T - Base type
13
+ * @param schema - Zod schema for validation
14
+ * @param format - Format identifier string
15
+ * @returns Parser function that validates and tags values
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * const parseHex = createParserWithSchema(
20
+ * z.string().regex(/^#[0-9A-Fa-f]{6}$/),
21
+ * 'HexColor'
22
+ * );
23
+ * ```
24
+ */
25
+ export declare function createParserWithSchema<F extends string, T>(schema: ZodSchema<T>, format: F): Parser<WithFormat<T, F>>;
26
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AACrC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAG3C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EACxD,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,EACpB,MAAM,EAAE,CAAC,GACR,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAU1B"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Runtime validation helpers for parsers
3
+ * @packageDocumentation
4
+ */
5
+ import { ParseError } from '../errors';
6
+ /**
7
+ * Create a parser with Zod schema validation.
8
+ *
9
+ * @template F - Format identifier
10
+ * @template T - Base type
11
+ * @param schema - Zod schema for validation
12
+ * @param format - Format identifier string
13
+ * @returns Parser function that validates and tags values
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const parseHex = createParserWithSchema(
18
+ * z.string().regex(/^#[0-9A-Fa-f]{6}$/),
19
+ * 'HexColor'
20
+ * );
21
+ * ```
22
+ */
23
+ export function createParserWithSchema(schema, format) {
24
+ return (input) => {
25
+ try {
26
+ const validated = schema.parse(input);
27
+ return validated;
28
+ }
29
+ catch (err) {
30
+ const message = err instanceof Error ? err.message : String(err);
31
+ throw new ParseError(format, input, message);
32
+ }
33
+ };
34
+ }
35
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,sBAAsB,CACpC,MAAoB,EACpB,MAAS,EACiB;IAC1B,OAAO,CAAC,KAAa,EAAoB,EAAE,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACtC,OAAO,SAA6B,CAAC;QACvC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,MAAM,IAAI,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;QAC/C,CAAC;IAAA,CACF,CAAC;AAAA,CACH"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unacy",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Type-safe unit and format conversion library",
5
5
  "type": "module",
6
6
  "private": false,
@@ -20,21 +20,21 @@
20
20
  "README.md"
21
21
  ],
22
22
  "dependencies": {
23
- "type-fest": "^5.3.1",
24
- "zod": "^3.24.1"
23
+ "type-fest": "^5.3.1"
25
24
  },
26
25
  "devDependencies": {
27
26
  "@types/node": "^25.0.3",
28
- "typescript": "^5.9.3",
29
- "vitest": "^4.0.16"
27
+ "@typescript/native-preview": "7.0.0-dev.20260107.1",
28
+ "vitest": "^4.0.16",
29
+ "zod": "^4.3.5"
30
30
  },
31
31
  "engines": {
32
32
  "node": ">=20.0.0"
33
33
  },
34
34
  "scripts": {
35
- "build": "tsc",
35
+ "build": "tsgo",
36
36
  "clean": "rm -rf dist",
37
- "type-check": "tsc --noEmit",
37
+ "type-check": "tsgo --noEmit",
38
38
  "test": "vitest run",
39
39
  "test:watch": "vitest"
40
40
  }