@penner/smart-primitive 0.0.1 → 0.1.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.
- package/README.md +215 -180
- package/dist/SmartPrimitive.d.ts +87 -37
- package/dist/SmartPrimitive.d.ts.map +1 -1
- package/dist/SmartPrimitive.test-d.d.ts.map +1 -0
- package/dist/SmartPrimitiveConfig.test-d.d.ts +2 -0
- package/dist/SmartPrimitiveConfig.test-d.d.ts.map +1 -0
- package/dist/convert.cjs.js +2 -0
- package/dist/convert.cjs.js.map +1 -0
- package/dist/convert.d.ts +136 -0
- package/dist/convert.d.ts.map +1 -0
- package/dist/convert.es.js +51 -0
- package/dist/convert.es.js.map +1 -0
- package/dist/convert.test-d.d.ts +2 -0
- package/dist/convert.test-d.d.ts.map +1 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.es.js +65 -2
- package/dist/index.es.js.map +1 -1
- package/dist/strings/css.d.ts +143 -0
- package/dist/strings/css.d.ts.map +1 -0
- package/dist/strings/data.d.ts +140 -0
- package/dist/strings/data.d.ts.map +1 -0
- package/dist/strings/index.cjs.js +2 -0
- package/dist/strings/index.cjs.js.map +1 -0
- package/dist/strings/index.d.ts +14 -0
- package/dist/strings/index.d.ts.map +1 -0
- package/dist/strings/index.es.js +2 -0
- package/dist/strings/index.es.js.map +1 -0
- package/dist/strings/web.d.ts +55 -0
- package/dist/strings/web.d.ts.map +1 -0
- package/dist/trait-utils.d.ts +107 -0
- package/dist/trait-utils.d.ts.map +1 -0
- package/dist/traits.d.ts +14 -0
- package/dist/traits.d.ts.map +1 -0
- package/dist/traits.examples.d.ts +7 -0
- package/dist/traits.examples.d.ts.map +1 -0
- package/dist/traits.test-d.d.ts +5 -0
- package/dist/traits.test-d.d.ts.map +1 -0
- package/dist/units/angle.d.ts +43 -0
- package/dist/units/angle.d.ts.map +1 -0
- package/dist/units/angle.test-d.d.ts +2 -0
- package/dist/units/angle.test-d.d.ts.map +1 -0
- package/dist/units/color.d.ts +35 -0
- package/dist/units/color.d.ts.map +1 -0
- package/dist/units/color.test-d.d.ts +2 -0
- package/dist/units/color.test-d.d.ts.map +1 -0
- package/dist/units/index.cjs.js +2 -0
- package/dist/units/index.cjs.js.map +1 -0
- package/dist/units/index.d.ts +19 -0
- package/dist/units/index.d.ts.map +1 -0
- package/dist/units/index.es.js +86 -0
- package/dist/units/index.es.js.map +1 -0
- package/dist/units/integer.d.ts +12 -0
- package/dist/units/integer.d.ts.map +1 -0
- package/dist/units/integer.test-d.d.ts +2 -0
- package/dist/units/integer.test-d.d.ts.map +1 -0
- package/dist/units/length.d.ts +101 -0
- package/dist/units/length.d.ts.map +1 -0
- package/dist/units/length.test-d.d.ts +2 -0
- package/dist/units/length.test-d.d.ts.map +1 -0
- package/dist/units/normalized.d.ts +111 -0
- package/dist/units/normalized.d.ts.map +1 -0
- package/dist/units/normalized.test-d.d.ts +2 -0
- package/dist/units/normalized.test-d.d.ts.map +1 -0
- package/dist/units/temperature.d.ts +16 -0
- package/dist/units/temperature.d.ts.map +1 -0
- package/dist/units/temperature.test-d.d.ts +2 -0
- package/dist/units/temperature.test-d.d.ts.map +1 -0
- package/dist/units/time.d.ts +38 -0
- package/dist/units/time.d.ts.map +1 -0
- package/dist/units/time.test-d.d.ts +2 -0
- package/dist/units/time.test-d.d.ts.map +1 -0
- package/dist/units/velocity.d.ts +78 -0
- package/dist/units/velocity.d.ts.map +1 -0
- package/dist/units/velocity.test-d.d.ts +2 -0
- package/dist/units/velocity.test-d.d.ts.map +1 -0
- package/package.json +39 -21
- package/dist/__tests__/SmartPrimitive.test-d.d.ts.map +0 -1
- /package/dist/{__tests__/SmartPrimitive.test-d.d.ts → SmartPrimitive.test-d.d.ts} +0 -0
package/dist/SmartPrimitive.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 🔧
|
|
3
|
-
*
|
|
4
|
-
* When
|
|
2
|
+
* 🔧 CONFIGURATION: Toggle smart primitive type checking
|
|
3
|
+
*
|
|
4
|
+
* When `usePlainPrimitives` is false (default): SmartPrimitive types provide type safety
|
|
5
|
+
* When `usePlainPrimitives` is true: All SmartNumber, SmartString, etc. become plain primitives
|
|
5
6
|
*
|
|
6
7
|
* Use cases:
|
|
7
8
|
* - Performance testing (eliminate type overhead)
|
|
@@ -9,26 +10,39 @@
|
|
|
9
10
|
* - Gradual migration to/from smart primitives
|
|
10
11
|
* - Bundle size optimization
|
|
11
12
|
*
|
|
12
|
-
* How to
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
13
|
+
* How to configure (module augmentation):
|
|
14
|
+
* ```ts
|
|
15
|
+
* // In a .d.ts file in your project (e.g., smart-primitive.d.ts):
|
|
16
|
+
* declare module '@penner/smart-primitive' {
|
|
17
|
+
* interface SmartPrimitiveConfig {
|
|
18
|
+
* usePlainPrimitives: true;
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* This uses TypeScript's declaration merging so consumers can override the
|
|
24
|
+
* default without modifying the library source.
|
|
18
25
|
*/
|
|
19
|
-
export declare const USE_PLAIN_PRIMITIVES: false;
|
|
20
|
-
export type USE_PLAIN_PRIMITIVES_TYPE = typeof USE_PLAIN_PRIMITIVES;
|
|
21
26
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
27
|
+
* Configuration interface for SmartPrimitive behavior.
|
|
28
|
+
* Empty by default — consumers augment this to add properties.
|
|
29
|
+
*
|
|
30
|
+
* To disable branded types, augment with `usePlainPrimitives: true`.
|
|
31
|
+
* The interface is intentionally empty so that declaration merging
|
|
32
|
+
* can add the property (TypeScript doesn't allow changing a property's type).
|
|
24
33
|
*/
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
export interface SmartPrimitiveConfig {
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Resolves to `true` when SmartPrimitiveConfig has been augmented with
|
|
38
|
+
* `usePlainPrimitives: true`, otherwise defaults to `false`.
|
|
39
|
+
*/
|
|
40
|
+
export type UsePlainPrimitives = SmartPrimitiveConfig extends {
|
|
41
|
+
usePlainPrimitives: infer V;
|
|
42
|
+
} ? V : false;
|
|
43
|
+
interface Brand<BrandName extends string> {
|
|
44
|
+
readonly _brand?: BrandName;
|
|
45
|
+
}
|
|
32
46
|
/**
|
|
33
47
|
* Generic smart primitive type that provides **opt-in type safety** while staying flexible.
|
|
34
48
|
*
|
|
@@ -36,7 +50,7 @@ type BaseOf<T> = USE_PLAIN_PRIMITIVES_TYPE extends true ? T : T extends number &
|
|
|
36
50
|
* - ✅ **Accepts plain values**: You can use regular numbers, strings, etc. directly
|
|
37
51
|
* - ✅ **Prevents cross-domain mixing**: TypeScript stops you from using pixels where milliseconds are expected
|
|
38
52
|
* - ✅ **Zero runtime cost**: No performance impact - it's just TypeScript magic
|
|
39
|
-
* - ✅ **Easy to disable**:
|
|
53
|
+
* - ✅ **Easy to disable**: Augment `SmartPrimitiveConfig` to turn off all smart typing
|
|
40
54
|
*
|
|
41
55
|
* **Example:**
|
|
42
56
|
* ```ts
|
|
@@ -51,9 +65,7 @@ type BaseOf<T> = USE_PLAIN_PRIMITIVES_TYPE extends true ? T : T extends number &
|
|
|
51
65
|
* This prevents bugs like accidentally using milliseconds where pixels are expected,
|
|
52
66
|
* while keeping your code simple and readable.
|
|
53
67
|
*/
|
|
54
|
-
export type SmartPrimitive<Base extends string | number | boolean | bigint | symbol, BrandName extends string> =
|
|
55
|
-
readonly __brand?: BrandName;
|
|
56
|
-
};
|
|
68
|
+
export type SmartPrimitive<Base extends string | number | boolean | bigint | symbol, BrandName extends string> = UsePlainPrimitives extends true ? Base : Base & Brand<BrandName>;
|
|
57
69
|
/**
|
|
58
70
|
* A smart number type for domain-specific numeric values like pixels, milliseconds, etc.
|
|
59
71
|
*
|
|
@@ -67,7 +79,7 @@ export type SmartPrimitive<Base extends string | number | boolean | bigint | sym
|
|
|
67
79
|
* - ✅ Catches domain mix-ups: `let width: Pixels = duration` → TypeScript error
|
|
68
80
|
* - ✅ Zero runtime cost: Just TypeScript checking, no JavaScript overhead
|
|
69
81
|
*
|
|
70
|
-
* Built on `SmartPrimitive` - respects
|
|
82
|
+
* Built on `SmartPrimitive` - respects `SmartPrimitiveConfig` for easy toggling.
|
|
71
83
|
*
|
|
72
84
|
* **Example:**
|
|
73
85
|
* ```ts
|
|
@@ -79,16 +91,16 @@ export type SmartPrimitive<Base extends string | number | boolean | bigint | sym
|
|
|
79
91
|
* let badAssign: Pixels = delay; // ❌ type error - caught the mistake!
|
|
80
92
|
* ```
|
|
81
93
|
*
|
|
82
|
-
* When
|
|
94
|
+
* When `usePlainPrimitives` is false (default):
|
|
83
95
|
* - Full smart number system with type safety between different units
|
|
84
96
|
* - Plain numbers accepted seamlessly
|
|
85
97
|
* - Clean tooltip display
|
|
86
98
|
*
|
|
87
|
-
* When
|
|
99
|
+
* When `usePlainPrimitives` is true:
|
|
88
100
|
* - All smart number types collapse to plain `number`
|
|
89
101
|
* - Zero runtime overhead, maximum performance
|
|
90
102
|
*
|
|
91
|
-
* Usage remains the same regardless of
|
|
103
|
+
* Usage remains the same regardless of config:
|
|
92
104
|
* ```ts
|
|
93
105
|
* let distance: Pixels = 300; // ✅ always works
|
|
94
106
|
* let branded: Pixels = 300 as Pixels; // ✅ always works
|
|
@@ -125,20 +137,58 @@ export type SmartString<BrandName extends string> = SmartPrimitive<string, Brand
|
|
|
125
137
|
* ```
|
|
126
138
|
*/
|
|
127
139
|
export type SmartBoolean<BrandName extends string> = SmartPrimitive<boolean, BrandName>;
|
|
140
|
+
/**
|
|
141
|
+
* A smart bigint type that accepts plain `bigint` values but maintains
|
|
142
|
+
* a distinct branded identity.
|
|
143
|
+
*
|
|
144
|
+
* Useful for domain-specific big integers such as IDs or counters where
|
|
145
|
+
* accidental mixing with other numeric types should be prevented.
|
|
146
|
+
*
|
|
147
|
+
* Usage:
|
|
148
|
+
* ```ts
|
|
149
|
+
* type EntityId = SmartBigInt<'EntityId'>;
|
|
150
|
+
* type UserId = SmartBigInt<'UserId'>;
|
|
151
|
+
* let id: EntityId = 123n; // ✅ works
|
|
152
|
+
* let user: UserId = 456n; // ✅ works
|
|
153
|
+
* let badAssign: EntityId = user; // ❌ type error
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
export type SmartBigInt<BrandName extends string> = SmartPrimitive<bigint, BrandName>;
|
|
157
|
+
/**
|
|
158
|
+
* A smart symbol type that accepts plain `symbol` values but maintains
|
|
159
|
+
* a distinct branded identity.
|
|
160
|
+
*
|
|
161
|
+
* Useful for domain-specific symbol tokens (e.g. event types, internal
|
|
162
|
+
* keys) where mixing distinct tokens should be prevented by the type system.
|
|
163
|
+
*
|
|
164
|
+
* Usage:
|
|
165
|
+
* ```ts
|
|
166
|
+
* type EventType = SmartSymbol<'EventType'>;
|
|
167
|
+
* type Token = SmartSymbol<'Token'>;
|
|
168
|
+
* let e: EventType = Symbol('click'); // ✅ works
|
|
169
|
+
* let t: Token = Symbol('auth'); // ✅ works
|
|
170
|
+
* let badAssign: EventType = t; // ❌ type error
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
export type SmartSymbol<BrandName extends string> = SmartPrimitive<symbol, BrandName>;
|
|
128
174
|
/**
|
|
129
175
|
* Simplified Unbrand using the generic brand pattern.
|
|
130
|
-
* Also respects the
|
|
176
|
+
* Also respects the `SmartPrimitiveConfig` toggle.
|
|
131
177
|
*
|
|
132
|
-
* When
|
|
133
|
-
* When
|
|
178
|
+
* When `usePlainPrimitives` is true: This becomes a no-op (types pass through unchanged)
|
|
179
|
+
* When `usePlainPrimitives` is false (default): Full unbranding to base types
|
|
134
180
|
*/
|
|
135
|
-
export type Unbrand<T> =
|
|
136
|
-
readonly
|
|
181
|
+
export type Unbrand<T> = UsePlainPrimitives extends true ? T : T extends number & {
|
|
182
|
+
readonly _brand?: unknown;
|
|
137
183
|
} ? number : T extends string & {
|
|
138
|
-
readonly
|
|
184
|
+
readonly _brand?: unknown;
|
|
139
185
|
} ? string : T extends boolean & {
|
|
140
|
-
readonly
|
|
141
|
-
} ? boolean : T extends
|
|
186
|
+
readonly _brand?: unknown;
|
|
187
|
+
} ? boolean : T extends bigint & {
|
|
188
|
+
readonly _brand?: unknown;
|
|
189
|
+
} ? bigint : T extends symbol & {
|
|
190
|
+
readonly _brand?: unknown;
|
|
191
|
+
} ? symbol : T extends Record<string, unknown> ? {
|
|
142
192
|
[K in keyof T]: Unbrand<T[K]>;
|
|
143
193
|
} : T;
|
|
144
194
|
/**
|
|
@@ -149,5 +199,5 @@ export type Unbrand<T> = USE_PLAIN_PRIMITIVES_TYPE extends true ? T : T extends
|
|
|
149
199
|
export type UnbrandFn<F, UnbrandReturn extends boolean = false> = F extends (...args: infer A) => infer R ? A extends readonly unknown[] ? (...args: {
|
|
150
200
|
[K in keyof A]: Unbrand<A[K]>;
|
|
151
201
|
}) => UnbrandReturn extends true ? Unbrand<R> : R : never : never;
|
|
152
|
-
export
|
|
202
|
+
export {};
|
|
153
203
|
//# sourceMappingURL=SmartPrimitive.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SmartPrimitive.d.ts","sourceRoot":"","sources":["../src/SmartPrimitive.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"SmartPrimitive.d.ts","sourceRoot":"","sources":["../src/SmartPrimitive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH;;;;;;;GAOG;AAEH,MAAM,WAAW,oBAAoB;CAAG;AAExC;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,oBAAoB,SAAS;IAC5D,kBAAkB,EAAE,MAAM,CAAC,CAAC;CAC7B,GACG,CAAC,GACD,KAAK,CAAC;AAGV,UAAU,KAAK,CAAC,SAAS,SAAS,MAAM;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,cAAc,CACxB,IAAI,SAAS,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,EACxD,SAAS,SAAS,MAAM,IACtB,kBAAkB,SAAS,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC;AAErE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAM,MAAM,WAAW,CAAC,SAAS,SAAS,MAAM,IAAI,cAAc,CAChE,MAAM,EACN,SAAS,CACV,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,WAAW,CAAC,SAAS,SAAS,MAAM,IAAI,cAAc,CAChE,MAAM,EACN,SAAS,CACV,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,YAAY,CAAC,SAAS,SAAS,MAAM,IAAI,cAAc,CACjE,OAAO,EACP,SAAS,CACV,CAAC;AACF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,WAAW,CAAC,SAAS,SAAS,MAAM,IAAI,cAAc,CAChE,MAAM,EACN,SAAS,CACV,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,WAAW,CAAC,SAAS,SAAS,MAAM,IAAI,cAAc,CAChE,MAAM,EACN,SAAS,CACV,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,OAAO,CAAC,CAAC,IAAI,kBAAkB,SAAS,IAAI,GACpD,CAAC,GAGD,CAAC,SAAS,MAAM,GAAG;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9C,MAAM,GACN,CAAC,SAAS,MAAM,GAAG;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9C,MAAM,GACN,CAAC,SAAS,OAAO,GAAG;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/C,OAAO,GACP,CAAC,SAAS,MAAM,GAAG;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9C,MAAM,GACN,CAAC,SAAS,MAAM,GAAG;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9C,MAAM,GAEN,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GAEjC,CAAC,CAAC;AAElB;;;;GAIG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,EAAE,aAAa,SAAS,OAAO,GAAG,KAAK,IAAI,CAAC,SAAS,CAC1E,GAAG,IAAI,EAAE,MAAM,CAAC,KACb,MAAM,CAAC,GACR,CAAC,SAAS,SAAS,OAAO,EAAE,GAC1B,CACE,GAAG,IAAI,EAAE;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,KACvC,aAAa,SAAS,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAChD,KAAK,GACP,KAAK,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SmartPrimitive.test-d.d.ts","sourceRoot":"","sources":["../src/SmartPrimitive.test-d.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SmartPrimitiveConfig.test-d.d.ts","sourceRoot":"","sources":["../src/SmartPrimitiveConfig.test-d.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function n(e){return(r=>e(r))}function i(e,r){return{to:n(e),from:n(r)}}function c(e){return i(r=>r*e,r=>r/e)}function s(e){return(r=>(e&&e(r),r))}function u(e,r){return(t=>r(e(t)))}function v(){return(e=>e)}class a{constructor(){this.converters=new Map}register(r,t){return this.converters.set(r,t),this}convert(r,t){const o=this.converters.get(t);if(!o)throw new Error(`No converter registered for target: ${t}`);return o(r)}has(r){return this.converters.has(r)}}exports.ConverterRegistry=a;exports.chain=u;exports.createBiConverter=i;exports.createConverter=n;exports.createLinearConverter=c;exports.createStringConverter=s;exports.identity=v;
|
|
2
|
+
//# sourceMappingURL=convert.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convert.cjs.js","sources":["../src/convert.ts"],"sourcesContent":["/**\n * 🔄 Conversion Utilities for Smart Primitives\n *\n * Type-safe conversion functions between branded types with a clean factory pattern.\n * Supports both one-way and bidirectional conversions.\n */\n\nimport type { SmartNumber, SmartString } from './SmartPrimitive';\n\n/* ═══════════════════════════════════════════════════════════════\n CORE CONVERSION TYPES\n ═══════════════════════════════════════════════════════════════ */\n\n/**\n * A type-safe conversion function from one branded type to another.\n *\n * @example\n * ```ts\n * const degreesToRadians: Converter<Degrees, Radians> = (deg) => (deg * Math.PI / 180) as Radians;\n * ```\n */\nexport type Converter<From, To> = (value: From) => To;\n\n/**\n * A bidirectional converter with forward and reverse operations.\n *\n * @example\n * ```ts\n * const degreesRadians: BiConverter<Degrees, Radians> = {\n * to: (deg) => (deg * Math.PI / 180) as Radians,\n * from: (rad) => (rad * 180 / Math.PI) as Degrees,\n * };\n * ```\n */\nexport interface BiConverter<A, B> {\n /** Convert from A to B */\n to: Converter<A, B>;\n /** Convert from B to A */\n from: Converter<B, A>;\n}\n\n/* ═══════════════════════════════════════════════════════════════\n CONVERSION FACTORY\n ═══════════════════════════════════════════════════════════════ */\n\n/**\n * Create a type-safe converter between two branded number types.\n *\n * @param transform - The mathematical transformation function\n * @returns A typed converter function\n *\n * @example\n * ```ts\n * type Celsius = SmartNumber<'Celsius'>;\n * type Fahrenheit = SmartNumber<'Fahrenheit'>;\n *\n * const celsiusToFahrenheit = createConverter<Celsius, Fahrenheit>(c => c * 9/5 + 32);\n * const temp: Fahrenheit = celsiusToFahrenheit(100 as Celsius); // 212\n * ```\n */\nexport function createConverter<\n From extends SmartNumber<string>,\n To extends SmartNumber<string>,\n>(transform: (value: number) => number): Converter<From, To> {\n return ((value: From) => transform(value) as To) as Converter<From, To>;\n}\n\n/**\n * Create a bidirectional converter between two branded number types.\n *\n * @param toTransform - Transform from A to B\n * @param fromTransform - Transform from B to A\n * @returns A BiConverter with both directions\n *\n * @example\n * ```ts\n * const celsiusFahrenheit = createBiConverter<Celsius, Fahrenheit>(\n * c => c * 9/5 + 32,\n * f => (f - 32) * 5/9\n * );\n *\n * celsiusFahrenheit.to(0 as Celsius); // 32 as Fahrenheit\n * celsiusFahrenheit.from(32 as Fahrenheit); // 0 as Celsius\n * ```\n */\nexport function createBiConverter<\n A extends SmartNumber<string>,\n B extends SmartNumber<string>,\n>(\n toTransform: (value: number) => number,\n fromTransform: (value: number) => number,\n): BiConverter<A, B> {\n return {\n to: createConverter<A, B>(toTransform),\n from: createConverter<B, A>(fromTransform),\n };\n}\n\n/**\n * Create a linear converter (multiply by factor).\n * Useful for unit conversions with simple ratios.\n *\n * @param factor - The multiplication factor (A * factor = B)\n * @returns A BiConverter using the linear relationship\n *\n * @example\n * ```ts\n * // 1 meter = 100 centimeters\n * const metersCentimeters = createLinearConverter<Meters, Centimeters>(100);\n *\n * metersCentimeters.to(2 as Meters); // 200 as Centimeters\n * metersCentimeters.from(50 as Centimeters); // 0.5 as Meters\n * ```\n */\nexport function createLinearConverter<\n A extends SmartNumber<string>,\n B extends SmartNumber<string>,\n>(factor: number): BiConverter<A, B> {\n return createBiConverter<A, B>(\n a => a * factor,\n b => b / factor,\n );\n}\n\n/**\n * Create a converter for string types with validation.\n *\n * @param validate - Optional validation function (throws on invalid input)\n * @returns A converter that casts strings to the target type\n *\n * @example\n * ```ts\n * type URL = SmartString<'URL'>;\n *\n * const toURL = createStringConverter<string, URL>((s) => {\n * if (!s.startsWith('http')) throw new Error('Invalid URL');\n * });\n *\n * const url: URL = toURL('https://example.com'); // ✅\n * const bad: URL = toURL('not-a-url'); // ❌ throws\n * ```\n */\nexport function createStringConverter<\n From extends string,\n To extends SmartString<string>,\n>(validate?: (value: From) => void): Converter<From, To> {\n return ((value: From) => {\n if (validate) validate(value);\n return value as unknown as To;\n }) as Converter<From, To>;\n}\n\n/* ═══════════════════════════════════════════════════════════════\n CHAINING & COMPOSITION\n ═══════════════════════════════════════════════════════════════ */\n\n/**\n * Chain two converters together: A → B → C\n *\n * @example\n * ```ts\n * const turnsToRadians = chain(turnsToDegrees, degreesToRadians);\n * ```\n */\nexport function chain<A, B, C>(\n first: Converter<A, B>,\n second: Converter<B, C>,\n): Converter<A, C> {\n return ((value: A) => second(first(value))) as Converter<A, C>;\n}\n\n/**\n * Create an identity converter (no-op, for type coercion).\n *\n * @example\n * ```ts\n * const pixelsIdentity = identity<Pixels>();\n * ```\n */\nexport function identity<T>(): Converter<T, T> {\n return ((value: T) => value) as Converter<T, T>;\n}\n\n/* ═══════════════════════════════════════════════════════════════\n CONVERSION REGISTRY (Optional Pattern)\n ═══════════════════════════════════════════════════════════════ */\n\n/**\n * A registry of converters for runtime lookup.\n * Useful when converter selection depends on runtime values.\n *\n * @example\n * ```ts\n * const angleConverters = new ConverterRegistry<Degrees>();\n * angleConverters.register('radians', degreesToRadians);\n * angleConverters.register('turns', degreesToTurns);\n *\n * const converted = angleConverters.convert(90 as Degrees, 'radians');\n * ```\n */\nexport class ConverterRegistry<From> {\n private converters = new Map<string, Converter<From, unknown>>();\n\n register<To>(name: string, converter: Converter<From, To>): this {\n this.converters.set(name, converter as Converter<From, unknown>);\n return this;\n }\n\n convert<To>(value: From, targetName: string): To {\n const converter = this.converters.get(targetName);\n if (!converter) {\n throw new Error(`No converter registered for target: ${targetName}`);\n }\n return converter(value) as To;\n }\n\n has(targetName: string): boolean {\n return this.converters.has(targetName);\n }\n}\n"],"names":["createConverter","transform","value","createBiConverter","toTransform","fromTransform","createLinearConverter","factor","a","b","createStringConverter","validate","chain","first","second","identity","ConverterRegistry","name","converter","targetName"],"mappings":"gFA4DO,SAASA,EAGdC,EAA2D,CAC3D,OAASC,GAAgBD,EAAUC,CAAK,EAC1C,CAoBO,SAASC,EAIdC,EACAC,EACmB,CACnB,MAAO,CACL,GAAIL,EAAsBI,CAAW,EACrC,KAAMJ,EAAsBK,CAAa,CAAA,CAE7C,CAkBO,SAASC,EAGdC,EAAmC,CACnC,OAAOJ,KACAK,EAAID,KACJE,EAAIF,CAAA,CAEb,CAoBO,SAASG,EAGdC,EAAuD,CACvD,OAAST,IACHS,KAAmBT,CAAK,EACrBA,GAEX,CAcO,SAASU,EACdC,EACAC,EACiB,CACjB,OAASZ,GAAaY,EAAOD,EAAMX,CAAK,CAAC,EAC3C,CAUO,SAASa,GAA+B,CAC7C,OAASb,GAAaA,EACxB,CAmBO,MAAMc,CAAwB,CAA9B,aAAA,CACL,KAAQ,eAAiB,GAAsC,CAE/D,SAAaC,EAAcC,EAAsC,CAC/D,YAAK,WAAW,IAAID,EAAMC,CAAqC,EACxD,IACT,CAEA,QAAYhB,EAAaiB,EAAwB,CAC/C,MAAMD,EAAY,KAAK,WAAW,IAAIC,CAAU,EAChD,GAAI,CAACD,EACH,MAAM,IAAI,MAAM,uCAAuCC,CAAU,EAAE,EAErE,OAAOD,EAAUhB,CAAK,CACxB,CAEA,IAAIiB,EAA6B,CAC/B,OAAO,KAAK,WAAW,IAAIA,CAAU,CACvC,CACF"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { SmartNumber, SmartString } from './SmartPrimitive';
|
|
2
|
+
/**
|
|
3
|
+
* A type-safe conversion function from one branded type to another.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* const degreesToRadians: Converter<Degrees, Radians> = (deg) => (deg * Math.PI / 180) as Radians;
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
10
|
+
export type Converter<From, To> = (value: From) => To;
|
|
11
|
+
/**
|
|
12
|
+
* A bidirectional converter with forward and reverse operations.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* const degreesRadians: BiConverter<Degrees, Radians> = {
|
|
17
|
+
* to: (deg) => (deg * Math.PI / 180) as Radians,
|
|
18
|
+
* from: (rad) => (rad * 180 / Math.PI) as Degrees,
|
|
19
|
+
* };
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export interface BiConverter<A, B> {
|
|
23
|
+
/** Convert from A to B */
|
|
24
|
+
to: Converter<A, B>;
|
|
25
|
+
/** Convert from B to A */
|
|
26
|
+
from: Converter<B, A>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Create a type-safe converter between two branded number types.
|
|
30
|
+
*
|
|
31
|
+
* @param transform - The mathematical transformation function
|
|
32
|
+
* @returns A typed converter function
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* type Celsius = SmartNumber<'Celsius'>;
|
|
37
|
+
* type Fahrenheit = SmartNumber<'Fahrenheit'>;
|
|
38
|
+
*
|
|
39
|
+
* const celsiusToFahrenheit = createConverter<Celsius, Fahrenheit>(c => c * 9/5 + 32);
|
|
40
|
+
* const temp: Fahrenheit = celsiusToFahrenheit(100 as Celsius); // 212
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare function createConverter<From extends SmartNumber<string>, To extends SmartNumber<string>>(transform: (value: number) => number): Converter<From, To>;
|
|
44
|
+
/**
|
|
45
|
+
* Create a bidirectional converter between two branded number types.
|
|
46
|
+
*
|
|
47
|
+
* @param toTransform - Transform from A to B
|
|
48
|
+
* @param fromTransform - Transform from B to A
|
|
49
|
+
* @returns A BiConverter with both directions
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* const celsiusFahrenheit = createBiConverter<Celsius, Fahrenheit>(
|
|
54
|
+
* c => c * 9/5 + 32,
|
|
55
|
+
* f => (f - 32) * 5/9
|
|
56
|
+
* );
|
|
57
|
+
*
|
|
58
|
+
* celsiusFahrenheit.to(0 as Celsius); // 32 as Fahrenheit
|
|
59
|
+
* celsiusFahrenheit.from(32 as Fahrenheit); // 0 as Celsius
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export declare function createBiConverter<A extends SmartNumber<string>, B extends SmartNumber<string>>(toTransform: (value: number) => number, fromTransform: (value: number) => number): BiConverter<A, B>;
|
|
63
|
+
/**
|
|
64
|
+
* Create a linear converter (multiply by factor).
|
|
65
|
+
* Useful for unit conversions with simple ratios.
|
|
66
|
+
*
|
|
67
|
+
* @param factor - The multiplication factor (A * factor = B)
|
|
68
|
+
* @returns A BiConverter using the linear relationship
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* // 1 meter = 100 centimeters
|
|
73
|
+
* const metersCentimeters = createLinearConverter<Meters, Centimeters>(100);
|
|
74
|
+
*
|
|
75
|
+
* metersCentimeters.to(2 as Meters); // 200 as Centimeters
|
|
76
|
+
* metersCentimeters.from(50 as Centimeters); // 0.5 as Meters
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export declare function createLinearConverter<A extends SmartNumber<string>, B extends SmartNumber<string>>(factor: number): BiConverter<A, B>;
|
|
80
|
+
/**
|
|
81
|
+
* Create a converter for string types with validation.
|
|
82
|
+
*
|
|
83
|
+
* @param validate - Optional validation function (throws on invalid input)
|
|
84
|
+
* @returns A converter that casts strings to the target type
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```ts
|
|
88
|
+
* type URL = SmartString<'URL'>;
|
|
89
|
+
*
|
|
90
|
+
* const toURL = createStringConverter<string, URL>((s) => {
|
|
91
|
+
* if (!s.startsWith('http')) throw new Error('Invalid URL');
|
|
92
|
+
* });
|
|
93
|
+
*
|
|
94
|
+
* const url: URL = toURL('https://example.com'); // ✅
|
|
95
|
+
* const bad: URL = toURL('not-a-url'); // ❌ throws
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
export declare function createStringConverter<From extends string, To extends SmartString<string>>(validate?: (value: From) => void): Converter<From, To>;
|
|
99
|
+
/**
|
|
100
|
+
* Chain two converters together: A → B → C
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* const turnsToRadians = chain(turnsToDegrees, degreesToRadians);
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
export declare function chain<A, B, C>(first: Converter<A, B>, second: Converter<B, C>): Converter<A, C>;
|
|
108
|
+
/**
|
|
109
|
+
* Create an identity converter (no-op, for type coercion).
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```ts
|
|
113
|
+
* const pixelsIdentity = identity<Pixels>();
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
export declare function identity<T>(): Converter<T, T>;
|
|
117
|
+
/**
|
|
118
|
+
* A registry of converters for runtime lookup.
|
|
119
|
+
* Useful when converter selection depends on runtime values.
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```ts
|
|
123
|
+
* const angleConverters = new ConverterRegistry<Degrees>();
|
|
124
|
+
* angleConverters.register('radians', degreesToRadians);
|
|
125
|
+
* angleConverters.register('turns', degreesToTurns);
|
|
126
|
+
*
|
|
127
|
+
* const converted = angleConverters.convert(90 as Degrees, 'radians');
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
export declare class ConverterRegistry<From> {
|
|
131
|
+
private converters;
|
|
132
|
+
register<To>(name: string, converter: Converter<From, To>): this;
|
|
133
|
+
convert<To>(value: From, targetName: string): To;
|
|
134
|
+
has(targetName: string): boolean;
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=convert.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAMjE;;;;;;;GAOG;AACH,MAAM,MAAM,SAAS,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,KAAK,EAAE,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,EAAE,CAAC;IAC/B,0BAA0B;IAC1B,EAAE,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACpB,0BAA0B;IAC1B,IAAI,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CACvB;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAC7B,IAAI,SAAS,WAAW,CAAC,MAAM,CAAC,EAChC,EAAE,SAAS,WAAW,CAAC,MAAM,CAAC,EAC9B,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,CAE3D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAC/B,CAAC,SAAS,WAAW,CAAC,MAAM,CAAC,EAC7B,CAAC,SAAS,WAAW,CAAC,MAAM,CAAC,EAE7B,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,EACtC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GACvC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAKnB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CACnC,CAAC,SAAS,WAAW,CAAC,MAAM,CAAC,EAC7B,CAAC,SAAS,WAAW,CAAC,MAAM,CAAC,EAC7B,MAAM,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAKnC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,SAAS,MAAM,EACnB,EAAE,SAAS,WAAW,CAAC,MAAM,CAAC,EAC9B,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,CAKvD;AAMD;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAC3B,KAAK,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EACtB,MAAM,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,GACtB,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAEjB;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAE7C;AAMD;;;;;;;;;;;;GAYG;AACH,qBAAa,iBAAiB,CAAC,IAAI;IACjC,OAAO,CAAC,UAAU,CAA+C;IAEjE,QAAQ,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI;IAKhE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,EAAE;IAQhD,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;CAGjC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
function o(e) {
|
|
2
|
+
return ((r) => e(r));
|
|
3
|
+
}
|
|
4
|
+
function c(e, r) {
|
|
5
|
+
return {
|
|
6
|
+
to: o(e),
|
|
7
|
+
from: o(r)
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function i(e) {
|
|
11
|
+
return c(
|
|
12
|
+
(r) => r * e,
|
|
13
|
+
(r) => r / e
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
function s(e) {
|
|
17
|
+
return ((r) => (e && e(r), r));
|
|
18
|
+
}
|
|
19
|
+
function u(e, r) {
|
|
20
|
+
return ((t) => r(e(t)));
|
|
21
|
+
}
|
|
22
|
+
function v() {
|
|
23
|
+
return ((e) => e);
|
|
24
|
+
}
|
|
25
|
+
class f {
|
|
26
|
+
constructor() {
|
|
27
|
+
this.converters = /* @__PURE__ */ new Map();
|
|
28
|
+
}
|
|
29
|
+
register(r, t) {
|
|
30
|
+
return this.converters.set(r, t), this;
|
|
31
|
+
}
|
|
32
|
+
convert(r, t) {
|
|
33
|
+
const n = this.converters.get(t);
|
|
34
|
+
if (!n)
|
|
35
|
+
throw new Error(`No converter registered for target: ${t}`);
|
|
36
|
+
return n(r);
|
|
37
|
+
}
|
|
38
|
+
has(r) {
|
|
39
|
+
return this.converters.has(r);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export {
|
|
43
|
+
f as ConverterRegistry,
|
|
44
|
+
u as chain,
|
|
45
|
+
c as createBiConverter,
|
|
46
|
+
o as createConverter,
|
|
47
|
+
i as createLinearConverter,
|
|
48
|
+
s as createStringConverter,
|
|
49
|
+
v as identity
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=convert.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convert.es.js","sources":["../src/convert.ts"],"sourcesContent":["/**\n * 🔄 Conversion Utilities for Smart Primitives\n *\n * Type-safe conversion functions between branded types with a clean factory pattern.\n * Supports both one-way and bidirectional conversions.\n */\n\nimport type { SmartNumber, SmartString } from './SmartPrimitive';\n\n/* ═══════════════════════════════════════════════════════════════\n CORE CONVERSION TYPES\n ═══════════════════════════════════════════════════════════════ */\n\n/**\n * A type-safe conversion function from one branded type to another.\n *\n * @example\n * ```ts\n * const degreesToRadians: Converter<Degrees, Radians> = (deg) => (deg * Math.PI / 180) as Radians;\n * ```\n */\nexport type Converter<From, To> = (value: From) => To;\n\n/**\n * A bidirectional converter with forward and reverse operations.\n *\n * @example\n * ```ts\n * const degreesRadians: BiConverter<Degrees, Radians> = {\n * to: (deg) => (deg * Math.PI / 180) as Radians,\n * from: (rad) => (rad * 180 / Math.PI) as Degrees,\n * };\n * ```\n */\nexport interface BiConverter<A, B> {\n /** Convert from A to B */\n to: Converter<A, B>;\n /** Convert from B to A */\n from: Converter<B, A>;\n}\n\n/* ═══════════════════════════════════════════════════════════════\n CONVERSION FACTORY\n ═══════════════════════════════════════════════════════════════ */\n\n/**\n * Create a type-safe converter between two branded number types.\n *\n * @param transform - The mathematical transformation function\n * @returns A typed converter function\n *\n * @example\n * ```ts\n * type Celsius = SmartNumber<'Celsius'>;\n * type Fahrenheit = SmartNumber<'Fahrenheit'>;\n *\n * const celsiusToFahrenheit = createConverter<Celsius, Fahrenheit>(c => c * 9/5 + 32);\n * const temp: Fahrenheit = celsiusToFahrenheit(100 as Celsius); // 212\n * ```\n */\nexport function createConverter<\n From extends SmartNumber<string>,\n To extends SmartNumber<string>,\n>(transform: (value: number) => number): Converter<From, To> {\n return ((value: From) => transform(value) as To) as Converter<From, To>;\n}\n\n/**\n * Create a bidirectional converter between two branded number types.\n *\n * @param toTransform - Transform from A to B\n * @param fromTransform - Transform from B to A\n * @returns A BiConverter with both directions\n *\n * @example\n * ```ts\n * const celsiusFahrenheit = createBiConverter<Celsius, Fahrenheit>(\n * c => c * 9/5 + 32,\n * f => (f - 32) * 5/9\n * );\n *\n * celsiusFahrenheit.to(0 as Celsius); // 32 as Fahrenheit\n * celsiusFahrenheit.from(32 as Fahrenheit); // 0 as Celsius\n * ```\n */\nexport function createBiConverter<\n A extends SmartNumber<string>,\n B extends SmartNumber<string>,\n>(\n toTransform: (value: number) => number,\n fromTransform: (value: number) => number,\n): BiConverter<A, B> {\n return {\n to: createConverter<A, B>(toTransform),\n from: createConverter<B, A>(fromTransform),\n };\n}\n\n/**\n * Create a linear converter (multiply by factor).\n * Useful for unit conversions with simple ratios.\n *\n * @param factor - The multiplication factor (A * factor = B)\n * @returns A BiConverter using the linear relationship\n *\n * @example\n * ```ts\n * // 1 meter = 100 centimeters\n * const metersCentimeters = createLinearConverter<Meters, Centimeters>(100);\n *\n * metersCentimeters.to(2 as Meters); // 200 as Centimeters\n * metersCentimeters.from(50 as Centimeters); // 0.5 as Meters\n * ```\n */\nexport function createLinearConverter<\n A extends SmartNumber<string>,\n B extends SmartNumber<string>,\n>(factor: number): BiConverter<A, B> {\n return createBiConverter<A, B>(\n a => a * factor,\n b => b / factor,\n );\n}\n\n/**\n * Create a converter for string types with validation.\n *\n * @param validate - Optional validation function (throws on invalid input)\n * @returns A converter that casts strings to the target type\n *\n * @example\n * ```ts\n * type URL = SmartString<'URL'>;\n *\n * const toURL = createStringConverter<string, URL>((s) => {\n * if (!s.startsWith('http')) throw new Error('Invalid URL');\n * });\n *\n * const url: URL = toURL('https://example.com'); // ✅\n * const bad: URL = toURL('not-a-url'); // ❌ throws\n * ```\n */\nexport function createStringConverter<\n From extends string,\n To extends SmartString<string>,\n>(validate?: (value: From) => void): Converter<From, To> {\n return ((value: From) => {\n if (validate) validate(value);\n return value as unknown as To;\n }) as Converter<From, To>;\n}\n\n/* ═══════════════════════════════════════════════════════════════\n CHAINING & COMPOSITION\n ═══════════════════════════════════════════════════════════════ */\n\n/**\n * Chain two converters together: A → B → C\n *\n * @example\n * ```ts\n * const turnsToRadians = chain(turnsToDegrees, degreesToRadians);\n * ```\n */\nexport function chain<A, B, C>(\n first: Converter<A, B>,\n second: Converter<B, C>,\n): Converter<A, C> {\n return ((value: A) => second(first(value))) as Converter<A, C>;\n}\n\n/**\n * Create an identity converter (no-op, for type coercion).\n *\n * @example\n * ```ts\n * const pixelsIdentity = identity<Pixels>();\n * ```\n */\nexport function identity<T>(): Converter<T, T> {\n return ((value: T) => value) as Converter<T, T>;\n}\n\n/* ═══════════════════════════════════════════════════════════════\n CONVERSION REGISTRY (Optional Pattern)\n ═══════════════════════════════════════════════════════════════ */\n\n/**\n * A registry of converters for runtime lookup.\n * Useful when converter selection depends on runtime values.\n *\n * @example\n * ```ts\n * const angleConverters = new ConverterRegistry<Degrees>();\n * angleConverters.register('radians', degreesToRadians);\n * angleConverters.register('turns', degreesToTurns);\n *\n * const converted = angleConverters.convert(90 as Degrees, 'radians');\n * ```\n */\nexport class ConverterRegistry<From> {\n private converters = new Map<string, Converter<From, unknown>>();\n\n register<To>(name: string, converter: Converter<From, To>): this {\n this.converters.set(name, converter as Converter<From, unknown>);\n return this;\n }\n\n convert<To>(value: From, targetName: string): To {\n const converter = this.converters.get(targetName);\n if (!converter) {\n throw new Error(`No converter registered for target: ${targetName}`);\n }\n return converter(value) as To;\n }\n\n has(targetName: string): boolean {\n return this.converters.has(targetName);\n }\n}\n"],"names":["createConverter","transform","value","createBiConverter","toTransform","fromTransform","createLinearConverter","factor","a","b","createStringConverter","validate","chain","first","second","identity","ConverterRegistry","name","converter","targetName"],"mappings":"AA4DO,SAASA,EAGdC,GAA2D;AAC3D,UAAQ,CAACC,MAAgBD,EAAUC,CAAK;AAC1C;AAoBO,SAASC,EAIdC,GACAC,GACmB;AACnB,SAAO;AAAA,IACL,IAAIL,EAAsBI,CAAW;AAAA,IACrC,MAAMJ,EAAsBK,CAAa;AAAA,EAAA;AAE7C;AAkBO,SAASC,EAGdC,GAAmC;AACnC,SAAOJ;AAAA,IACL,OAAKK,IAAID;AAAA,IACT,OAAKE,IAAIF;AAAA,EAAA;AAEb;AAoBO,SAASG,EAGdC,GAAuD;AACvD,UAAQ,CAACT,OACHS,OAAmBT,CAAK,GACrBA;AAEX;AAcO,SAASU,EACdC,GACAC,GACiB;AACjB,UAAQ,CAACZ,MAAaY,EAAOD,EAAMX,CAAK,CAAC;AAC3C;AAUO,SAASa,IAA+B;AAC7C,UAAQ,CAACb,MAAaA;AACxB;AAmBO,MAAMc,EAAwB;AAAA,EAA9B,cAAA;AACL,SAAQ,iCAAiB,IAAA;AAAA,EAAsC;AAAA,EAE/D,SAAaC,GAAcC,GAAsC;AAC/D,gBAAK,WAAW,IAAID,GAAMC,CAAqC,GACxD;AAAA,EACT;AAAA,EAEA,QAAYhB,GAAaiB,GAAwB;AAC/C,UAAMD,IAAY,KAAK,WAAW,IAAIC,CAAU;AAChD,QAAI,CAACD;AACH,YAAM,IAAI,MAAM,uCAAuCC,CAAU,EAAE;AAErE,WAAOD,EAAUhB,CAAK;AAAA,EACxB;AAAA,EAEA,IAAIiB,GAA6B;AAC/B,WAAO,KAAK,WAAW,IAAIA,CAAU;AAAA,EACvC;AACF;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convert.test-d.d.ts","sourceRoot":"","sources":["../src/convert.test-d.ts"],"names":[],"mappings":""}
|
package/dist/index.cjs.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("./convert.cjs.js"),e=require("./units/index.cjs.js");function t(i,o,r){return Math.min(Math.max(i,o),r)}function l(i,o,r){const n=r-o;return((i-o)%n+n)%n+o}exports.ConverterRegistry=s.ConverterRegistry;exports.chain=s.chain;exports.createBiConverter=s.createBiConverter;exports.createConverter=s.createConverter;exports.createLinearConverter=s.createLinearConverter;exports.createStringConverter=s.createStringConverter;exports.identity=s.identity;exports.applyVelocity=e.applyVelocity;exports.celsiusToFahrenheit=e.celsiusToFahrenheit;exports.centimetersToInches=e.centimetersToInches;exports.centimetersToMeters=e.centimetersToMeters;exports.centimetersToMillimeters=e.centimetersToMillimeters;exports.clampAlpha=e.clampAlpha;exports.clampNormalized=e.clampNormalized;exports.clampPercentage=e.clampPercentage;exports.clampSignedNormalized=e.clampSignedNormalized;exports.degreesToRadians=e.degreesToRadians;exports.degreesToTurns=e.degreesToTurns;exports.emsToPixels=e.emsToPixels;exports.fahrenheitToCelsius=e.fahrenheitToCelsius;exports.hoursToMinutes=e.hoursToMinutes;exports.inchesToCentimeters=e.inchesToCentimeters;exports.inchesToPixels=e.inchesToPixels;exports.inchesToPoints=e.inchesToPoints;exports.metersToCentimeters=e.metersToCentimeters;exports.millimetersToCentimeters=e.millimetersToCentimeters;exports.millisecondsToSeconds=e.millisecondsToSeconds;exports.minutesToHours=e.minutesToHours;exports.minutesToSeconds=e.minutesToSeconds;exports.normalizeDegrees=e.normalizeDegrees;exports.normalizeRadians=e.normalizeRadians;exports.normalizeTurns=e.normalizeTurns;exports.normalizedProgressToPercent=e.normalizedProgressToPercent;exports.normalizedToPercentage=e.normalizedToPercentage;exports.percentToNormalizedProgress=e.percentToNormalizedProgress;exports.percentageToNormalized=e.percentageToNormalized;exports.pixelsToEms=e.pixelsToEms;exports.pixelsToInches=e.pixelsToInches;exports.pixelsToRems=e.pixelsToRems;exports.pixelsToVh=e.pixelsToVh;exports.pixelsToVw=e.pixelsToVw;exports.pointsToInches=e.pointsToInches;exports.radiansToDegrees=e.radiansToDegrees;exports.radiansToTurns=e.radiansToTurns;exports.remsToPixels=e.remsToPixels;exports.secondsToMilliseconds=e.secondsToMilliseconds;exports.secondsToMinutes=e.secondsToMinutes;exports.turnsToDegrees=e.turnsToDegrees;exports.turnsToRadians=e.turnsToRadians;exports.velocityFromValues=e.velocityFromValues;exports.velocityMillisecondsToSeconds=e.velocityMillisecondsToSeconds;exports.velocitySecondsToMilliseconds=e.velocitySecondsToMilliseconds;exports.vhToPixels=e.vhToPixels;exports.vwToPixels=e.vwToPixels;exports.clamp=t;exports.wrap=l;
|
|
2
2
|
//# sourceMappingURL=index.cjs.js.map
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../src/
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../src/trait-utils.ts"],"sourcesContent":["/**\n * 🏷️ Trait System Utilities\n *\n * Core trait interfaces and wrapper types for the orthogonal trait system.\n * These are used across all unit type definitions to provide composable,\n * flag-aware type annotations.\n */\n\nimport type { UsePlainPrimitives } from './SmartPrimitive';\n\n/* ═══════════════════════════════════════════════════════════════\n CORE TRAIT INTERFACES (Internal)\n ═══════════════════════════════════════════════════════════════ */\n\ninterface Unit<U extends string> {\n readonly _unit?: U;\n}\n\ninterface Range<Min extends number, Max extends number> {\n readonly _range?: { readonly min: Min; readonly max: Max };\n}\n\ninterface Clamped {\n readonly _clamped?: true;\n}\n\ninterface Periodic {\n readonly _periodic?: true;\n}\n\ninterface Kind<K extends string> {\n readonly _kind?: K;\n}\n\ninterface Integer {\n readonly _integer?: true;\n}\n\n/* ═══════════════════════════════════════════════════════════════\n EXPORTED TRAIT WRAPPERS (Flag-Aware)\n ═══════════════════════════════════════════════════════════════ */\n\n/**\n * Marks a number with measurement units.\n */\nexport type WithUnit<U extends string> = number &\n (UsePlainPrimitives extends true ? {} : Unit<U>);\n\n/**\n * Marks a number as having a reference range [Min, Max].\n */\nexport type WithRange<Min extends number, Max extends number> = number &\n (UsePlainPrimitives extends true ? {} : Range<Min, Max>);\n\n/**\n * Marks a number as being clamped (constrained) to its range.\n */\nexport type WithClamped = number &\n (UsePlainPrimitives extends true ? {} : Clamped);\n\n/**\n * Marks a number as periodic (wrapping around at boundaries).\n */\nexport type WithPeriodic = number &\n (UsePlainPrimitives extends true ? {} : Periodic);\n\n/**\n * Groups related unit types under a common archetype/kind.\n */\nexport type WithKind<K extends string> = number &\n (UsePlainPrimitives extends true ? {} : Kind<K>);\n\n/**\n * Marks a number as an integer (whole number).\n */\nexport type WithInteger = number &\n (UsePlainPrimitives extends true ? {} : Integer);\n\n/* ═══════════════════════════════════════════════════════════════\n TYPE EXTRACTION UTILITIES\n ═══════════════════════════════════════════════════════════════ */\n\n/**\n * Extract units from a type.\n * @returns The unit string, or never if type has no Units trait.\n */\nexport type UnitOf<T> = T extends Unit<infer U> ? U : never;\n\n/**\n * Extract range from a type as a tuple [Min, Max].\n * @returns [Min, Max] tuple, or never if type has no Range trait.\n */\nexport type RangeOf<T> =\n T extends Range<infer Min, infer Max> ? readonly [Min, Max] : never;\n\n/**\n * Extract min value from range trait.\n * @returns Min value, or never if type has no Range trait.\n */\nexport type MinOf<T> = T extends Range<infer Min, infer _Max> ? Min : never;\n\n/**\n * Extract max value from range trait.\n * @returns Max value, or never if type has no Range trait.\n */\nexport type MaxOf<T> = T extends Range<infer _Min, infer Max> ? Max : never;\n\n/**\n * Check if a type has the Clamped trait.\n * @returns true if clamped, false if not.\n */\nexport type IsClamped<T> = T extends Clamped ? true : false;\n\n/**\n * Check if a type has the Periodic trait.\n * @returns true if periodic, false if not.\n */\nexport type IsPeriodic<T> = T extends Periodic ? true : false;\n\n/**\n * Extract kind/archetype from a type.\n * @returns The kind string, or never if type has no Kind trait.\n */\nexport type KindOf<T> = T extends Kind<infer K> ? K : never;\n\n/* ═══════════════════════════════════════════════════════════════\n TRAIT MODIFICATION UTILITIES\n ═══════════════════════════════════════════════════════════════ */\n\n/**\n * Remove the Clamped trait from a type (return to unclamped).\n */\nexport type WithoutClamped<T> = T extends Clamped ? Omit<T, '_clamped'> : T;\n\n/**\n * Remove the Periodic trait from a type.\n */\nexport type WithoutPeriodic<T> = T extends Periodic ? Omit<T, '_periodic'> : T;\n\n/**\n * Change the units of a type (unit conversion).\n */\nexport type ChangeUnits<T, U extends string> = Omit<T, '_unit'> & WithUnit<U>;\n\n/**\n * Change the range of a type.\n */\nexport type ChangeRange<T, Min extends number, Max extends number> = Omit<\n T,\n '_range'\n> &\n WithRange<Min, Max>;\n\n/* ═══════════════════════════════════════════════════════════════\n RUNTIME UTILITIES\n ═══════════════════════════════════════════════════════════════ */\n\n/**\n * Clamp a value to specified bounds, preserve orthogonal traits, and mark as clamped.\n */\nexport function clamp<T extends number, Min extends number, Max extends number>(\n value: T,\n min: Min,\n max: Max,\n): WithRange<Min, Max> & WithClamped {\n return Math.min(Math.max(value, min), max);\n}\n\n/**\n * Wrap a value to specified bounds, preserve orthogonal traits, and mark as periodic.\n */\nexport function wrap<T, Min extends number, Max extends number>(\n value: T,\n min: Min,\n max: Max,\n): Omit<T, '_range' | '_periodic'> & WithRange<Min, Max> & WithPeriodic {\n const range = max - min;\n const normalized = ((((value as any) - min) % range) + range) % range;\n return (normalized + min) as any;\n}\n"],"names":["clamp","value","min","max","wrap","range"],"mappings":"sJAgKO,SAASA,EACdC,EACAC,EACAC,EACmC,CACnC,OAAO,KAAK,IAAI,KAAK,IAAIF,EAAOC,CAAG,EAAGC,CAAG,CAC3C,CAKO,SAASC,EACdH,EACAC,EACAC,EACsE,CACtE,MAAME,EAAQF,EAAMD,EAEpB,QADuBD,EAAgBC,GAAOG,EAASA,GAASA,EAC3CH,CACvB"}
|
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,11 @@
|
|
|
17
17
|
* let oops: Pixels = delay; // ❌ type error - caught!
|
|
18
18
|
* ```
|
|
19
19
|
*/
|
|
20
|
-
export type { SmartPrimitive, SmartNumber, SmartString, SmartBoolean, Unbrand, UnbrandFn,
|
|
21
|
-
export {
|
|
20
|
+
export type { SmartPrimitive, SmartPrimitiveConfig, SmartNumber, SmartString, SmartBoolean, SmartBigInt, SmartSymbol, Unbrand, UnbrandFn, UsePlainPrimitives, } from './SmartPrimitive';
|
|
21
|
+
export type { Converter, BiConverter } from './convert';
|
|
22
|
+
export { createConverter, createBiConverter, createLinearConverter, createStringConverter, chain, identity, ConverterRegistry, } from './convert';
|
|
23
|
+
export * from './units';
|
|
24
|
+
export type { WithUnit, WithRange, WithClamped, WithPeriodic, WithKind, WithInteger, UnitOf, RangeOf, MinOf, MaxOf, IsClamped, IsPeriodic, KindOf, WithoutClamped, WithoutPeriodic, ChangeUnits, ChangeRange, } from './trait-utils';
|
|
25
|
+
export { clamp, wrap } from './trait-utils';
|
|
26
|
+
export type { CSSEasing } from './strings/css';
|
|
22
27
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,YAAY,EACV,cAAc,EACd,WAAW,EACX,WAAW,EACX,YAAY,EACZ,OAAO,EACP,SAAS,EACT,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,YAAY,EACV,cAAc,EACd,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,YAAY,EACZ,WAAW,EACX,WAAW,EACX,OAAO,EACP,SAAS,EACT,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAG1B,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,KAAK,EACL,QAAQ,EACR,iBAAiB,GAClB,MAAM,WAAW,CAAC;AAInB,cAAc,SAAS,CAAC;AAGxB,YAAY,EACV,QAAQ,EACR,SAAS,EACT,WAAW,EACX,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,MAAM,EACN,OAAO,EACP,KAAK,EACL,KAAK,EACL,SAAS,EACT,UAAU,EACV,MAAM,EACN,cAAc,EACd,eAAe,EACf,WAAW,EACX,WAAW,GACZ,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAG5C,YAAY,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC"}
|