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,53 @@
1
+ /**
2
+ * Type-safe converter function signatures
3
+ * @packageDocumentation
4
+ */
5
+ import type { WithUnits } from './types';
6
+ /**
7
+ * Unidirectional converter from one unit to another.
8
+ *
9
+ * @template TInput - Source unit-tagged type
10
+ * @template TOutput - Destination unit-tagged type
11
+ *
12
+ * @param input - Value tagged with source unit
13
+ * @returns Value tagged with destination unit
14
+ *
15
+ * @remarks
16
+ * - Must be a pure function (no side effects)
17
+ * - Should be deterministic (same input → same output)
18
+ * - Document precision loss if applicable
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const c2f: Converter<Celsius, Fahrenheit> = (c) =>
23
+ * ((c * 9/5) + 32) as Fahrenheit;
24
+ * ```
25
+ */
26
+ export type Converter<TInput extends WithUnits<unknown, string>, TOutput extends WithUnits<unknown, string>> = (input: TInput) => TOutput;
27
+ /**
28
+ * Bidirectional converter with forward and reverse transformations.
29
+ *
30
+ * @template TInput - First unit type
31
+ * @template TOutput - Second unit type
32
+ *
33
+ * @property to - Forward converter (TInput → TOutput)
34
+ * @property from - Reverse converter (TOutput → TInput)
35
+ *
36
+ * @remarks
37
+ * - Round-trip conversions should preserve value within acceptable tolerance
38
+ * - Both converters must be deterministic
39
+ * - Use when both conversion directions are commonly needed
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * const meterKilometer: BidirectionalConverter<Meters, Kilometers> = {
44
+ * to: (m) => (m / 1000) as Kilometers,
45
+ * from: (km) => (km * 1000) as Meters
46
+ * };
47
+ * ```
48
+ */
49
+ export type BidirectionalConverter<TInput extends WithUnits<unknown, string>, TOutput extends WithUnits<unknown, string>> = {
50
+ to: Converter<TInput, TOutput>;
51
+ from: Converter<TOutput, TInput>;
52
+ };
53
+ //# sourceMappingURL=converters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"converters.d.ts","sourceRoot":"","sources":["../src/converters.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,SAAS,CACnB,MAAM,SAAS,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,EACzC,OAAO,SAAS,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,IACxC,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;AAE/B;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,sBAAsB,CAChC,MAAM,SAAS,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,EACzC,OAAO,SAAS,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,IACxC;IACF,EAAE,EAAE,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,EAAE,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;CAClC,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Type-safe converter function signatures
3
+ * @packageDocumentation
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=converters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"converters.js","sourceRoot":"","sources":["../src/converters.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Base error class for all Unacy errors
3
+ */
4
+ export declare class UnacyError extends Error {
5
+ constructor(message: string);
6
+ }
7
+ /**
8
+ * Error thrown when a cycle is detected in the conversion graph
9
+ */
10
+ export declare class CycleError extends UnacyError {
11
+ readonly path: PropertyKey[];
12
+ constructor(path: PropertyKey[]);
13
+ }
14
+ /**
15
+ * Error thrown when maximum conversion depth is exceeded
16
+ */
17
+ export declare class MaxDepthError extends UnacyError {
18
+ readonly from: PropertyKey;
19
+ readonly to: PropertyKey;
20
+ readonly maxDepth: number;
21
+ constructor(from: PropertyKey, to: PropertyKey, maxDepth: number);
22
+ }
23
+ /**
24
+ * Error thrown when a conversion cannot be performed
25
+ */
26
+ export declare class ConversionError extends UnacyError {
27
+ readonly from: PropertyKey;
28
+ readonly to: PropertyKey;
29
+ constructor(from: PropertyKey, to: PropertyKey, reason?: string);
30
+ }
31
+ /**
32
+ * Error thrown when parsing a string into a tagged format fails
33
+ */
34
+ export declare class ParseError extends UnacyError {
35
+ readonly format: string;
36
+ readonly input: string;
37
+ readonly reason: string;
38
+ constructor(format: string, input: string, reason: string);
39
+ }
40
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,UAAW,SAAQ,KAAK;IACnC,YAAY,OAAO,EAAE,MAAM,EAM1B;CACF;AAED;;GAEG;AACH,qBAAa,UAAW,SAAQ,UAAU;IACxC,SAAgB,IAAI,EAAE,WAAW,EAAE,CAAC;IAEpC,YAAY,IAAI,EAAE,WAAW,EAAE,EAK9B;CACF;AAED;;GAEG;AACH,qBAAa,aAAc,SAAQ,UAAU;IAC3C,SAAgB,IAAI,EAAE,WAAW,CAAC;IAClC,SAAgB,EAAE,EAAE,WAAW,CAAC;IAChC,SAAgB,QAAQ,EAAE,MAAM,CAAC;IAEjC,YAAY,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAQ/D;CACF;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,UAAU;IAC7C,SAAgB,IAAI,EAAE,WAAW,CAAC;IAClC,SAAgB,EAAE,EAAE,WAAW,CAAC;IAEhC,YAAY,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,MAAM,EAM9D;CACF;AAED;;GAEG;AACH,qBAAa,UAAW,SAAQ,UAAU;IACxC,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,SAAgB,KAAK,EAAE,MAAM,CAAC;IAC9B,SAAgB,MAAM,EAAE,MAAM,CAAC;IAE/B,YAAY,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EASxD;CACF"}
package/dist/errors.js ADDED
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Base error class for all Unacy errors
3
+ */
4
+ export class UnacyError extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = 'UnacyError';
8
+ // Maintain proper prototype chain for instanceof checks
9
+ Object.setPrototypeOf(this, new.target.prototype);
10
+ }
11
+ }
12
+ /**
13
+ * Error thrown when a cycle is detected in the conversion graph
14
+ */
15
+ export class CycleError extends UnacyError {
16
+ path;
17
+ constructor(path) {
18
+ const pathStr = path.map(String).join(' → ');
19
+ super(`Cycle detected in conversion path: ${pathStr}`);
20
+ this.name = 'CycleError';
21
+ this.path = path;
22
+ }
23
+ }
24
+ /**
25
+ * Error thrown when maximum conversion depth is exceeded
26
+ */
27
+ export class MaxDepthError extends UnacyError {
28
+ from;
29
+ to;
30
+ maxDepth;
31
+ constructor(from, to, maxDepth) {
32
+ super(`Maximum conversion depth of ${maxDepth} exceeded when converting from ${String(from)} to ${String(to)}`);
33
+ this.name = 'MaxDepthError';
34
+ this.from = from;
35
+ this.to = to;
36
+ this.maxDepth = maxDepth;
37
+ }
38
+ }
39
+ /**
40
+ * Error thrown when a conversion cannot be performed
41
+ */
42
+ export class ConversionError extends UnacyError {
43
+ from;
44
+ to;
45
+ constructor(from, to, reason) {
46
+ const reasonStr = reason ? `: ${reason}` : '';
47
+ super(`Cannot convert from ${String(from)} to ${String(to)}${reasonStr}`);
48
+ this.name = 'ConversionError';
49
+ this.from = from;
50
+ this.to = to;
51
+ }
52
+ }
53
+ /**
54
+ * Error thrown when parsing a string into a tagged format fails
55
+ */
56
+ export class ParseError extends UnacyError {
57
+ format;
58
+ input;
59
+ reason;
60
+ constructor(format, input, reason) {
61
+ const truncatedInput = input.length > 50 ? `${input.slice(0, 50)}...` : input;
62
+ const displayInput = input === '' ? '""' : truncatedInput;
63
+ super(`Cannot parse "${displayInput}" as ${format}: ${reason}`);
64
+ this.name = 'ParseError';
65
+ this.format = format;
66
+ this.input = input;
67
+ this.reason = reason;
68
+ }
69
+ }
70
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +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"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Formatter and parser types for format-tagged values
3
+ * @packageDocumentation
4
+ */
5
+ import type { WithFormat } from './types';
6
+ /**
7
+ * Formatter converts a format-tagged value to a string representation.
8
+ *
9
+ * @template TInput - Format-tagged type to format
10
+ *
11
+ * @param input - Value tagged with format
12
+ * @returns Plain string representation
13
+ *
14
+ * @remarks
15
+ * - Output string must be parseable by corresponding `Parser`
16
+ * - Should produce human-readable or machine-parseable output
17
+ * - Format tag is lost in the output
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * const formatISO: Formatter<ISO8601> = (date) => date.toISOString();
22
+ * ```
23
+ */
24
+ export type Formatter<TInput extends WithFormat<unknown, string>> = (input: TInput) => string;
25
+ /**
26
+ * Parser converts a string into a format-tagged value with validation.
27
+ *
28
+ * @template TOutput - Format-tagged type to produce
29
+ *
30
+ * @param input - Plain string to parse
31
+ * @returns Value tagged with format
32
+ *
33
+ * @throws {ParseError} When input string is invalid
34
+ *
35
+ * @remarks
36
+ * - Must validate input before tagging
37
+ * - Must throw clear errors (not return invalid tagged values)
38
+ * - Should use Zod or similar for schema validation
39
+ * - Never produces invalid tagged values
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * const parseISO: Parser<ISO8601> = (input) => {
44
+ * const schema = z.string().datetime();
45
+ * const validated = schema.parse(input);
46
+ * return new Date(validated) as ISO8601;
47
+ * };
48
+ * ```
49
+ */
50
+ export type Parser<TOutput extends WithFormat<unknown, string>> = (input: string) => TOutput;
51
+ /**
52
+ * Paired formatter/parser for round-trip format transformations.
53
+ *
54
+ * @template T - Format-tagged type
55
+ *
56
+ * @property format - Converts tagged value → string
57
+ * @property parse - Converts string → tagged value
58
+ *
59
+ * @remarks
60
+ * - Round-trip must succeed for valid values: `parse(format(x)) ≡ x`
61
+ * - Parser must reject invalid strings with clear errors
62
+ * - Use when both formatting and parsing are needed
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * const iso8601: FormatterParser<ISO8601> = {
67
+ * format: (date) => date.toISOString(),
68
+ * parse: (str) => {
69
+ * const date = new Date(str);
70
+ * if (isNaN(date.getTime())) {
71
+ * throw new ParseError('ISO8601', str, 'Invalid date');
72
+ * }
73
+ * return date as ISO8601;
74
+ * }
75
+ * };
76
+ * ```
77
+ */
78
+ export type FormatterParser<T extends WithFormat<unknown, string>> = {
79
+ format: Formatter<T>;
80
+ parse: Parser<T>;
81
+ };
82
+ //# sourceMappingURL=formatters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatters.d.ts","sourceRoot":"","sources":["../src/formatters.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,SAAS,CAAC,MAAM,SAAS,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;AAE9F;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,MAAM,MAAM,CAAC,OAAO,SAAS,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;AAE7F;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI;IACnE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;CAClB,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Formatter and parser types for format-tagged values
3
+ * @packageDocumentation
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=formatters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatters.js","sourceRoot":"","sources":["../src/formatters.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Unacy Core - Type-safe unit and format conversion library
3
+ * @packageDocumentation
4
+ */
5
+ export type { WithUnits, WithFormat } from './types';
6
+ export type { Converter, BidirectionalConverter } from './converters';
7
+ export type { Formatter, Parser, FormatterParser } from './formatters';
8
+ export type { ConverterRegistry } from './registry';
9
+ export { createRegistry } from './registry';
10
+ export { UnacyError, CycleError, MaxDepthError, ConversionError, ParseError } from './errors';
11
+ export { createParserWithSchema } from './utils/validation';
12
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Unacy Core - Type-safe unit and format conversion library
3
+ * @packageDocumentation
4
+ */
5
+ export { createRegistry } from './registry';
6
+ // Errors
7
+ export { UnacyError, CycleError, MaxDepthError, ConversionError, ParseError } from './errors';
8
+ // Utilities
9
+ export { createParserWithSchema } from './utils/validation';
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,SAAS;AACT,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE9F,YAAY;AACZ,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -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":"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"}