unacy 0.1.1 → 0.2.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 (46) 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.js.map +1 -1
  11. package/dist/__tests__/registry.test.d.ts +2 -0
  12. package/dist/__tests__/registry.test.d.ts.map +1 -0
  13. package/dist/__tests__/registry.test.js +250 -0
  14. package/dist/__tests__/registry.test.js.map +1 -0
  15. package/dist/__tests__/types.test.d.ts +2 -0
  16. package/dist/__tests__/types.test.js.map +1 -1
  17. package/dist/converters.d.ts +53 -0
  18. package/dist/converters.d.ts.map +1 -0
  19. package/dist/converters.js +6 -0
  20. package/dist/converters.js.map +1 -0
  21. package/dist/errors.d.ts +40 -0
  22. package/dist/errors.d.ts.map +1 -0
  23. package/dist/errors.js +70 -0
  24. package/dist/errors.js.map +1 -0
  25. package/dist/formatters.d.ts +82 -0
  26. package/dist/formatters.d.ts.map +1 -0
  27. package/dist/formatters.js +6 -0
  28. package/dist/formatters.js.map +1 -0
  29. package/dist/index.d.ts +12 -0
  30. package/dist/index.js +10 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/registry.d.ts +141 -0
  33. package/dist/registry.d.ts.map +1 -0
  34. package/dist/registry.js +212 -0
  35. package/dist/registry.js.map +1 -0
  36. package/dist/types.d.ts +36 -0
  37. package/dist/types.d.ts.map +1 -0
  38. package/dist/types.js +6 -0
  39. package/dist/types.js.map +1 -0
  40. package/dist/utils/graph.d.ts +26 -0
  41. package/dist/utils/graph.d.ts.map +1 -0
  42. package/dist/utils/graph.js +91 -0
  43. package/dist/utils/graph.js.map +1 -0
  44. package/dist/utils/validation.d.ts +26 -0
  45. package/dist/utils/validation.js.map +1 -1
  46. package/package.json +2 -2
@@ -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":"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"}
@@ -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
@@ -1 +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;IAET,OAAO,CAAC,KAAa,EAAoB,EAAE;QACzC,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;IACH,CAAC,CAAC;AACJ,CAAC"}
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.1",
3
+ "version": "0.2.0",
4
4
  "description": "Type-safe unit and format conversion library",
5
5
  "type": "module",
6
6
  "private": false,
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/node": "^25.0.3",
27
- "@typescript/native-preview": "7.0.0-dev.20260106.1",
27
+ "@typescript/native-preview": "7.0.0-dev.20260107.1",
28
28
  "vitest": "^4.0.16",
29
29
  "zod": "^4.3.5"
30
30
  },