@penner/smart-primitive 0.0.1
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 +267 -0
- package/dist/SmartPrimitive.d.ts +153 -0
- package/dist/SmartPrimitive.d.ts.map +1 -0
- package/dist/__tests__/SmartPrimitive.test-d.d.ts +2 -0
- package/dist/__tests__/SmartPrimitive.test-d.d.ts.map +1 -0
- package/dist/index.cjs.js +2 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.es.js +5 -0
- package/dist/index.es.js.map +1 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# @penner/smart-primitive
|
|
2
|
+
|
|
3
|
+
Type-safe branded primitives with zero runtime overhead. Prevent bugs by distinguishing different kinds of numbers, strings, and booleans at compile time.
|
|
4
|
+
|
|
5
|
+
## Why Smart Primitives?
|
|
6
|
+
|
|
7
|
+
Ever accidentally used milliseconds where pixels were expected? Or passed a URL to a function expecting a CSS selector? **Smart primitives** catch these mistakes at compile time, with zero runtime cost.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { SmartNumber, SmartString } from '@penner/smart-primitive';
|
|
11
|
+
|
|
12
|
+
type Pixels = SmartNumber<'Pixels'>;
|
|
13
|
+
type Milliseconds = SmartNumber<'Milliseconds'>;
|
|
14
|
+
type URL = SmartString<'URL'>;
|
|
15
|
+
type CSSSelector = SmartString<'CSSSelector'>;
|
|
16
|
+
|
|
17
|
+
// ✅ Works perfectly
|
|
18
|
+
let width: Pixels = 300;
|
|
19
|
+
let delay: Milliseconds = 500;
|
|
20
|
+
let link: URL = 'https://example.com';
|
|
21
|
+
let selector: CSSSelector = '.button';
|
|
22
|
+
|
|
23
|
+
// ❌ TypeScript catches the mistake!
|
|
24
|
+
let oops: Pixels = delay; // Error: Type 'Milliseconds' is not assignable to type 'Pixels'
|
|
25
|
+
let wrong: URL = selector; // Error: Type 'CSSSelector' is not assignable to type 'URL'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Features
|
|
29
|
+
|
|
30
|
+
- ✅ **Zero runtime overhead** - Pure TypeScript, no JavaScript generated
|
|
31
|
+
- ✅ **Works with plain values** - No wrapping or conversion needed
|
|
32
|
+
- ✅ **Prevents cross-domain mixing** - TypeScript stops you from mixing incompatible types
|
|
33
|
+
- ✅ **Toggleable type safety** - Turn off all smart typing with one flag
|
|
34
|
+
- ✅ **Utility functions** - `Unbrand`, `BaseOf`, `UnbrandFn` for working with branded types
|
|
35
|
+
- ✅ **Clean tooltips** - TypeScript shows clean type names, not implementation details
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install @penner/smart-primitive
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
### Smart Numbers
|
|
46
|
+
|
|
47
|
+
Perfect for units, measurements, or any numeric domain:
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { SmartNumber } from '@penner/smart-primitive';
|
|
51
|
+
|
|
52
|
+
type Pixels = SmartNumber<'Pixels'>;
|
|
53
|
+
type Milliseconds = SmartNumber<'Milliseconds'>;
|
|
54
|
+
type Degrees = SmartNumber<'Degrees'>;
|
|
55
|
+
|
|
56
|
+
function animate(distance: Pixels, duration: Milliseconds, rotation: Degrees) {
|
|
57
|
+
console.log(`Move ${distance}px over ${duration}ms, rotate ${rotation}°`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
animate(100, 500, 90); // ✅ works
|
|
61
|
+
animate(100, 500, 500); // ✅ works (but is it degrees or milliseconds? TypeScript doesn't know yet)
|
|
62
|
+
|
|
63
|
+
// But if you assign first:
|
|
64
|
+
let delay: Milliseconds = 500;
|
|
65
|
+
animate(100, delay, delay); // ❌ Error! Can't use Milliseconds where Degrees expected
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Smart Strings
|
|
69
|
+
|
|
70
|
+
Distinguish between different kinds of strings:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { SmartString } from '@penner/smart-primitive';
|
|
74
|
+
|
|
75
|
+
type URL = SmartString<'URL'>;
|
|
76
|
+
type EmailAddress = SmartString<'EmailAddress'>;
|
|
77
|
+
type CSSSelector = SmartString<'CSSSelector'>;
|
|
78
|
+
|
|
79
|
+
function fetchData(endpoint: URL) {
|
|
80
|
+
// implementation
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function sendEmail(address: EmailAddress) {
|
|
84
|
+
// implementation
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let api: URL = 'https://api.example.com';
|
|
88
|
+
let email: EmailAddress = 'user@example.com';
|
|
89
|
+
|
|
90
|
+
fetchData(api); // ✅ works
|
|
91
|
+
fetchData(email); // ❌ Error! EmailAddress is not a URL
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Smart Booleans
|
|
95
|
+
|
|
96
|
+
Even booleans can benefit from type safety:
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
import { SmartBoolean } from '@penner/smart-primitive';
|
|
100
|
+
|
|
101
|
+
type IsVisible = SmartBoolean<'IsVisible'>;
|
|
102
|
+
type IsEnabled = SmartBoolean<'IsEnabled'>;
|
|
103
|
+
|
|
104
|
+
function toggleVisibility(visible: IsVisible) {
|
|
105
|
+
// implementation
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let visible: IsVisible = true;
|
|
109
|
+
let enabled: IsEnabled = true;
|
|
110
|
+
|
|
111
|
+
toggleVisibility(visible); // ✅ works
|
|
112
|
+
toggleVisibility(enabled); // ❌ Error! IsEnabled is not IsVisible
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Advanced Usage
|
|
116
|
+
|
|
117
|
+
### Utility Types
|
|
118
|
+
|
|
119
|
+
#### `Unbrand<T>`
|
|
120
|
+
|
|
121
|
+
Remove branding from types, converting them back to primitives:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import { Unbrand } from '@penner/smart-primitive';
|
|
125
|
+
|
|
126
|
+
type Config = {
|
|
127
|
+
width: Pixels;
|
|
128
|
+
duration: Milliseconds;
|
|
129
|
+
url: URL;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
type PlainConfig = Unbrand<Config>;
|
|
133
|
+
// Result: { width: number; duration: number; url: string; }
|
|
134
|
+
|
|
135
|
+
const config: PlainConfig = {
|
|
136
|
+
width: 100,
|
|
137
|
+
duration: 500,
|
|
138
|
+
url: 'https://example.com',
|
|
139
|
+
};
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
#### `BaseOf<T>`
|
|
143
|
+
|
|
144
|
+
Extract the base primitive type:
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import { BaseOf } from '@penner/smart-primitive';
|
|
148
|
+
|
|
149
|
+
type PixelsBase = BaseOf<Pixels>; // number
|
|
150
|
+
type URLBase = BaseOf<URL>; // string
|
|
151
|
+
type IsVisibleBase = BaseOf<IsVisible>; // boolean
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
#### `UnbrandFn<F>`
|
|
155
|
+
|
|
156
|
+
Unbrand function parameters:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import { UnbrandFn } from '@penner/smart-primitive';
|
|
160
|
+
|
|
161
|
+
function animate(distance: Pixels, duration: Milliseconds): void {
|
|
162
|
+
// implementation
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
type PlainAnimate = UnbrandFn<typeof animate>;
|
|
166
|
+
// Result: (distance: number, duration: number) => void
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Feature Flag: Toggle Type Safety
|
|
170
|
+
|
|
171
|
+
You can disable all smart type checking with a single flag. This is useful for:
|
|
172
|
+
|
|
173
|
+
- Performance testing
|
|
174
|
+
- Debugging type issues
|
|
175
|
+
- Gradual migration
|
|
176
|
+
- Bundle size optimization
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
// In your SmartPrimitive.ts file
|
|
180
|
+
export const USE_PLAIN_PRIMITIVES = true as const; // 👈 Change to true
|
|
181
|
+
|
|
182
|
+
// Now ALL smart types become plain primitives
|
|
183
|
+
type Pixels = SmartNumber<'Pixels'>; // becomes: number
|
|
184
|
+
type URL = SmartString<'URL'>; // becomes: string
|
|
185
|
+
type IsVisible = SmartBoolean<'IsVisible'>; // becomes: boolean
|
|
186
|
+
|
|
187
|
+
// All cross-brand assignments are now allowed
|
|
188
|
+
let width: Pixels = 100;
|
|
189
|
+
let delay: Milliseconds = 200;
|
|
190
|
+
width = delay; // ✅ Now allowed! (when flag is true)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## How It Works
|
|
194
|
+
|
|
195
|
+
Smart primitives use TypeScript's brand pattern (also called phantom types or nominal typing). The implementation is remarkably simple:
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
export type SmartPrimitive<
|
|
199
|
+
Base extends string | number | boolean | bigint | symbol,
|
|
200
|
+
BrandName extends string,
|
|
201
|
+
> = Base & { readonly __brand?: BrandName };
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
The `__brand` property is:
|
|
205
|
+
|
|
206
|
+
- **Optional** - so plain primitives are assignable
|
|
207
|
+
- **Readonly** - prevents accidental modification
|
|
208
|
+
- **Never actually exists at runtime** - TypeScript-only, zero overhead
|
|
209
|
+
|
|
210
|
+
## TypeScript Compatibility
|
|
211
|
+
|
|
212
|
+
Requires TypeScript 4.5 or higher.
|
|
213
|
+
|
|
214
|
+
## Examples
|
|
215
|
+
|
|
216
|
+
### Complex Object Structures
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
type AnimationConfig = {
|
|
220
|
+
timing: {
|
|
221
|
+
duration: Milliseconds;
|
|
222
|
+
delay: Milliseconds;
|
|
223
|
+
};
|
|
224
|
+
position: {
|
|
225
|
+
start: Pixels;
|
|
226
|
+
end: Pixels;
|
|
227
|
+
};
|
|
228
|
+
rotation: Degrees;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const config: AnimationConfig = {
|
|
232
|
+
timing: { duration: 1000, delay: 200 },
|
|
233
|
+
position: { start: 0, end: 500 },
|
|
234
|
+
rotation: 180,
|
|
235
|
+
};
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Function Safety
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
function moveElement(
|
|
242
|
+
element: HTMLElement,
|
|
243
|
+
distance: Pixels,
|
|
244
|
+
duration: Milliseconds,
|
|
245
|
+
): void {
|
|
246
|
+
// TypeScript ensures you can't accidentally swap parameters
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
let dist: Pixels = 100;
|
|
250
|
+
let time: Milliseconds = 500;
|
|
251
|
+
|
|
252
|
+
moveElement(element, dist, time); // ✅ correct
|
|
253
|
+
moveElement(element, time, dist); // ❌ Error! Parameters swapped
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## License
|
|
257
|
+
|
|
258
|
+
MIT
|
|
259
|
+
|
|
260
|
+
## Related Packages
|
|
261
|
+
|
|
262
|
+
- [`@penner/easing`](https://www.npmjs.com/package/@penner/easing) - Modern Penner easing functions
|
|
263
|
+
- [`@penner/responsive-easing`](https://www.npmjs.com/package/@penner/responsive-easing) - Responsive motion design system
|
|
264
|
+
|
|
265
|
+
## Contributing
|
|
266
|
+
|
|
267
|
+
Contributions welcome! Please read the [contributing guidelines](../../CONTRIBUTING.md) first.
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🔧 FEATURE FLAG: Toggle smart primitive type checking
|
|
3
|
+
* When false (default): SmartPrimitive types provide type safety
|
|
4
|
+
* When true: All SmartNumber, SmartString, etc. become plain primitives
|
|
5
|
+
*
|
|
6
|
+
* Use cases:
|
|
7
|
+
* - Performance testing (eliminate type overhead)
|
|
8
|
+
* - Debugging type issues
|
|
9
|
+
* - Gradual migration to/from smart primitives
|
|
10
|
+
* - Bundle size optimization
|
|
11
|
+
*
|
|
12
|
+
* How to use:
|
|
13
|
+
* 1. Change `false` to `true` below
|
|
14
|
+
* 2. Your entire codebase now treats Pixels, URLs, etc. as plain primitives
|
|
15
|
+
* 3. Cross-brand assignments that were errors become allowed
|
|
16
|
+
* 4. All type extraction utilities become no-ops
|
|
17
|
+
* 5. Change back to `false` to re-enable smart primitive type safety
|
|
18
|
+
*/
|
|
19
|
+
export declare const USE_PLAIN_PRIMITIVES: false;
|
|
20
|
+
export type USE_PLAIN_PRIMITIVES_TYPE = typeof USE_PLAIN_PRIMITIVES;
|
|
21
|
+
/**
|
|
22
|
+
* Clean type extraction using the brand pattern
|
|
23
|
+
* Respects the USE_PLAIN_PRIMITIVES flag.
|
|
24
|
+
*/
|
|
25
|
+
type BaseOf<T> = USE_PLAIN_PRIMITIVES_TYPE extends true ? T : T extends number & {
|
|
26
|
+
readonly __brand?: unknown;
|
|
27
|
+
} ? number : T extends string & {
|
|
28
|
+
readonly __brand?: unknown;
|
|
29
|
+
} ? string : T extends boolean & {
|
|
30
|
+
readonly __brand?: unknown;
|
|
31
|
+
} ? boolean : T;
|
|
32
|
+
/**
|
|
33
|
+
* Generic smart primitive type that provides **opt-in type safety** while staying flexible.
|
|
34
|
+
*
|
|
35
|
+
* **How it works:**
|
|
36
|
+
* - ✅ **Accepts plain values**: You can use regular numbers, strings, etc. directly
|
|
37
|
+
* - ✅ **Prevents cross-domain mixing**: TypeScript stops you from using pixels where milliseconds are expected
|
|
38
|
+
* - ✅ **Zero runtime cost**: No performance impact - it's just TypeScript magic
|
|
39
|
+
* - ✅ **Easy to disable**: Toggle `USE_PLAIN_PRIMITIVES` to turn off all smart typing
|
|
40
|
+
*
|
|
41
|
+
* **Example:**
|
|
42
|
+
* ```ts
|
|
43
|
+
* type Pixels = SmartPrimitive<number, 'Pixels'>;
|
|
44
|
+
* type Milliseconds = SmartPrimitive<number, 'Milliseconds'>;
|
|
45
|
+
*
|
|
46
|
+
* let width: Pixels = 300; // ✅ Plain number works
|
|
47
|
+
* let delay: Milliseconds = 500; // ✅ Plain number works
|
|
48
|
+
* let oops: Pixels = delay; // ❌ TypeScript error - caught the mistake!
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* This prevents bugs like accidentally using milliseconds where pixels are expected,
|
|
52
|
+
* while keeping your code simple and readable.
|
|
53
|
+
*/
|
|
54
|
+
export type SmartPrimitive<Base extends string | number | boolean | bigint | symbol, BrandName extends string> = USE_PLAIN_PRIMITIVES_TYPE extends true ? Base : Base & {
|
|
55
|
+
readonly __brand?: BrandName;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* A smart number type for domain-specific numeric values like pixels, milliseconds, etc.
|
|
59
|
+
*
|
|
60
|
+
* **Why use this?** Prevents common bugs like mixing up different kinds of numbers:
|
|
61
|
+
* - Using milliseconds where pixels are expected
|
|
62
|
+
* - Passing a width value to a duration parameter
|
|
63
|
+
* - Confusing degrees with radians
|
|
64
|
+
*
|
|
65
|
+
* **How it works:**
|
|
66
|
+
* - ✅ Works with plain numbers: `let width: Pixels = 300`
|
|
67
|
+
* - ✅ Catches domain mix-ups: `let width: Pixels = duration` → TypeScript error
|
|
68
|
+
* - ✅ Zero runtime cost: Just TypeScript checking, no JavaScript overhead
|
|
69
|
+
*
|
|
70
|
+
* Built on `SmartPrimitive` - respects the USE_PLAIN_PRIMITIVES flag for easy toggling.
|
|
71
|
+
*
|
|
72
|
+
* **Example:**
|
|
73
|
+
* ```ts
|
|
74
|
+
* type Pixels = SmartNumber<'Pixels'>;
|
|
75
|
+
* type Milliseconds = SmartNumber<'Milliseconds'>;
|
|
76
|
+
*
|
|
77
|
+
* let width: Pixels = 300; // ✅ works
|
|
78
|
+
* let delay: Milliseconds = 500; // ✅ works
|
|
79
|
+
* let badAssign: Pixels = delay; // ❌ type error - caught the mistake!
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* When USE_PLAIN_PRIMITIVES = false (default):
|
|
83
|
+
* - Full smart number system with type safety between different units
|
|
84
|
+
* - Plain numbers accepted seamlessly
|
|
85
|
+
* - Clean tooltip display
|
|
86
|
+
*
|
|
87
|
+
* When USE_PLAIN_PRIMITIVES = true:
|
|
88
|
+
* - All smart number types collapse to plain `number`
|
|
89
|
+
* - Zero runtime overhead, maximum performance
|
|
90
|
+
*
|
|
91
|
+
* Usage remains the same regardless of flag state:
|
|
92
|
+
* ```ts
|
|
93
|
+
* let distance: Pixels = 300; // ✅ always works
|
|
94
|
+
* let branded: Pixels = 300 as Pixels; // ✅ always works
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export type SmartNumber<BrandName extends string> = SmartPrimitive<number, BrandName>;
|
|
98
|
+
/**
|
|
99
|
+
* A smart string type that accepts plain strings but maintains type identity.
|
|
100
|
+
* Perfect for things like URLs, CSS selectors, or other domain-specific strings.
|
|
101
|
+
*
|
|
102
|
+
* Usage:
|
|
103
|
+
* ```ts
|
|
104
|
+
* type URL = SmartString<'URL'>;
|
|
105
|
+
* type CSSSelector = SmartString<'CSSSelector'>;
|
|
106
|
+
*
|
|
107
|
+
* let url: URL = "https://example.com"; // ✅ works
|
|
108
|
+
* let selector: CSSSelector = ".my-class"; // ✅ works
|
|
109
|
+
* let badAssign: URL = selector; // ❌ type error
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
export type SmartString<BrandName extends string> = SmartPrimitive<string, BrandName>;
|
|
113
|
+
/**
|
|
114
|
+
* A smart boolean type that accepts plain booleans but maintains type identity.
|
|
115
|
+
* Useful for domain-specific flags or state indicators.
|
|
116
|
+
*
|
|
117
|
+
* Usage:
|
|
118
|
+
* ```ts
|
|
119
|
+
* type IsVisible = SmartBoolean<'IsVisible'>;
|
|
120
|
+
* type IsEnabled = SmartBoolean<'IsEnabled'>;
|
|
121
|
+
*
|
|
122
|
+
* let visible: IsVisible = true; // ✅ works
|
|
123
|
+
* let enabled: IsEnabled = false; // ✅ works
|
|
124
|
+
* let badAssign: IsVisible = enabled; // ❌ type error
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export type SmartBoolean<BrandName extends string> = SmartPrimitive<boolean, BrandName>;
|
|
128
|
+
/**
|
|
129
|
+
* Simplified Unbrand using the generic brand pattern.
|
|
130
|
+
* Also respects the USE_PLAIN_PRIMITIVES flag.
|
|
131
|
+
*
|
|
132
|
+
* When USE_PLAIN_PRIMITIVES = true: This becomes a no-op (types pass through unchanged)
|
|
133
|
+
* When USE_PLAIN_PRIMITIVES = false: Full unbranding to base types
|
|
134
|
+
*/
|
|
135
|
+
export type Unbrand<T> = USE_PLAIN_PRIMITIVES_TYPE extends true ? T : T extends number & {
|
|
136
|
+
readonly __brand?: unknown;
|
|
137
|
+
} ? number : T extends string & {
|
|
138
|
+
readonly __brand?: unknown;
|
|
139
|
+
} ? string : T extends boolean & {
|
|
140
|
+
readonly __brand?: unknown;
|
|
141
|
+
} ? boolean : T extends Record<string, unknown> ? {
|
|
142
|
+
[K in keyof T]: Unbrand<T[K]>;
|
|
143
|
+
} : T;
|
|
144
|
+
/**
|
|
145
|
+
* Given any function type F, produce a new function type whose
|
|
146
|
+
* arguments have been passed through Unbrand<…>, and whose return
|
|
147
|
+
* type you can also choose to unbrand (or leave alone).
|
|
148
|
+
*/
|
|
149
|
+
export type UnbrandFn<F, UnbrandReturn extends boolean = false> = F extends (...args: infer A) => infer R ? A extends readonly unknown[] ? (...args: {
|
|
150
|
+
[K in keyof A]: Unbrand<A[K]>;
|
|
151
|
+
}) => UnbrandReturn extends true ? Unbrand<R> : R : never : never;
|
|
152
|
+
export type { BaseOf };
|
|
153
|
+
//# sourceMappingURL=SmartPrimitive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SmartPrimitive.d.ts","sourceRoot":"","sources":["../src/SmartPrimitive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,eAAO,MAAM,oBAAoB,EAAG,KAAc,CAAC;AACnD,MAAM,MAAM,yBAAyB,GAAG,OAAO,oBAAoB,CAAC;AAEpE;;;GAGG;AACH,KAAK,MAAM,CAAC,CAAC,IAAI,yBAAyB,SAAS,IAAI,GACnD,CAAC,GACD,CAAC,SAAS,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/C,MAAM,GACN,CAAC,SAAS,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/C,MAAM,GACN,CAAC,SAAS,OAAO,GAAG;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAChD,OAAO,GACP,CAAC,CAAC;AAEZ;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,cAAc,CACxB,IAAI,SAAS,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,EACxD,SAAS,SAAS,MAAM,IACtB,yBAAyB,SAAS,IAAI,GACtC,IAAI,GACJ,IAAI,GAAG;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,CAAA;CAAE,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,OAAO,CAAC,CAAC,IAAI,yBAAyB,SAAS,IAAI,GAC3D,CAAC,GAGD,CAAC,SAAS,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/C,MAAM,GACN,CAAC,SAAS,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/C,MAAM,GACN,CAAC,SAAS,OAAO,GAAG;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAChD,OAAO,GAEP,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;AAEd;;;;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;AAGV,YAAY,EAAE,MAAM,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SmartPrimitive.test-d.d.ts","sourceRoot":"","sources":["../../src/__tests__/SmartPrimitive.test-d.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../src/SmartPrimitive.ts"],"sourcesContent":["/**\n * 🔧 FEATURE FLAG: Toggle smart primitive type checking\n * When false (default): SmartPrimitive types provide type safety\n * When true: All SmartNumber, SmartString, etc. become plain primitives\n *\n * Use cases:\n * - Performance testing (eliminate type overhead)\n * - Debugging type issues\n * - Gradual migration to/from smart primitives\n * - Bundle size optimization\n *\n * How to use:\n * 1. Change `false` to `true` below\n * 2. Your entire codebase now treats Pixels, URLs, etc. as plain primitives\n * 3. Cross-brand assignments that were errors become allowed\n * 4. All type extraction utilities become no-ops\n * 5. Change back to `false` to re-enable smart primitive type safety\n */\n\n// 🔧 FEATURE FLAG: Both runtime constant AND compile-time type\nexport const USE_PLAIN_PRIMITIVES = false as const; // 👈 Change to `true` for plain primitives, `false` for branded types\nexport type USE_PLAIN_PRIMITIVES_TYPE = typeof USE_PLAIN_PRIMITIVES;\n\n/**\n * Clean type extraction using the brand pattern\n * Respects the USE_PLAIN_PRIMITIVES flag.\n */\ntype BaseOf<T> = USE_PLAIN_PRIMITIVES_TYPE extends true\n ? T // 🚫 Smart types disabled: type is already the base\n : T extends number & { readonly __brand?: unknown }\n ? number\n : T extends string & { readonly __brand?: unknown }\n ? string\n : T extends boolean & { readonly __brand?: unknown }\n ? boolean\n : T; // 🎯 Extract base type or return as-is\n\n/**\n * Generic smart primitive type that provides **opt-in type safety** while staying flexible.\n *\n * **How it works:**\n * - ✅ **Accepts plain values**: You can use regular numbers, strings, etc. directly\n * - ✅ **Prevents cross-domain mixing**: TypeScript stops you from using pixels where milliseconds are expected\n * - ✅ **Zero runtime cost**: No performance impact - it's just TypeScript magic\n * - ✅ **Easy to disable**: Toggle `USE_PLAIN_PRIMITIVES` to turn off all smart typing\n *\n * **Example:**\n * ```ts\n * type Pixels = SmartPrimitive<number, 'Pixels'>;\n * type Milliseconds = SmartPrimitive<number, 'Milliseconds'>;\n *\n * let width: Pixels = 300; // ✅ Plain number works\n * let delay: Milliseconds = 500; // ✅ Plain number works\n * let oops: Pixels = delay; // ❌ TypeScript error - caught the mistake!\n * ```\n *\n * This prevents bugs like accidentally using milliseconds where pixels are expected,\n * while keeping your code simple and readable.\n */\nexport type SmartPrimitive<\n Base extends string | number | boolean | bigint | symbol,\n BrandName extends string,\n> = USE_PLAIN_PRIMITIVES_TYPE extends true\n ? Base\n : Base & { readonly __brand?: BrandName };\n\n/**\n * A smart number type for domain-specific numeric values like pixels, milliseconds, etc.\n *\n * **Why use this?** Prevents common bugs like mixing up different kinds of numbers:\n * - Using milliseconds where pixels are expected\n * - Passing a width value to a duration parameter\n * - Confusing degrees with radians\n *\n * **How it works:**\n * - ✅ Works with plain numbers: `let width: Pixels = 300`\n * - ✅ Catches domain mix-ups: `let width: Pixels = duration` → TypeScript error\n * - ✅ Zero runtime cost: Just TypeScript checking, no JavaScript overhead\n *\n * Built on `SmartPrimitive` - respects the USE_PLAIN_PRIMITIVES flag for easy toggling.\n *\n * **Example:**\n * ```ts\n * type Pixels = SmartNumber<'Pixels'>;\n * type Milliseconds = SmartNumber<'Milliseconds'>;\n *\n * let width: Pixels = 300; // ✅ works\n * let delay: Milliseconds = 500; // ✅ works\n * let badAssign: Pixels = delay; // ❌ type error - caught the mistake!\n * ```\n *\n * When USE_PLAIN_PRIMITIVES = false (default):\n * - Full smart number system with type safety between different units\n * - Plain numbers accepted seamlessly\n * - Clean tooltip display\n *\n * When USE_PLAIN_PRIMITIVES = true:\n * - All smart number types collapse to plain `number`\n * - Zero runtime overhead, maximum performance\n *\n * Usage remains the same regardless of flag state:\n * ```ts\n * let distance: Pixels = 300; // ✅ always works\n * let branded: Pixels = 300 as Pixels; // ✅ always works\n * ```\n */\nexport type SmartNumber<BrandName extends string> = SmartPrimitive<\n number,\n BrandName\n>;\n\n/**\n * A smart string type that accepts plain strings but maintains type identity.\n * Perfect for things like URLs, CSS selectors, or other domain-specific strings.\n *\n * Usage:\n * ```ts\n * type URL = SmartString<'URL'>;\n * type CSSSelector = SmartString<'CSSSelector'>;\n *\n * let url: URL = \"https://example.com\"; // ✅ works\n * let selector: CSSSelector = \".my-class\"; // ✅ works\n * let badAssign: URL = selector; // ❌ type error\n * ```\n */\nexport type SmartString<BrandName extends string> = SmartPrimitive<\n string,\n BrandName\n>;\n\n/**\n * A smart boolean type that accepts plain booleans but maintains type identity.\n * Useful for domain-specific flags or state indicators.\n *\n * Usage:\n * ```ts\n * type IsVisible = SmartBoolean<'IsVisible'>;\n * type IsEnabled = SmartBoolean<'IsEnabled'>;\n *\n * let visible: IsVisible = true; // ✅ works\n * let enabled: IsEnabled = false; // ✅ works\n * let badAssign: IsVisible = enabled; // ❌ type error\n * ```\n */\nexport type SmartBoolean<BrandName extends string> = SmartPrimitive<\n boolean,\n BrandName\n>;\n\n/**\n * Simplified Unbrand using the generic brand pattern.\n * Also respects the USE_PLAIN_PRIMITIVES flag.\n *\n * When USE_PLAIN_PRIMITIVES = true: This becomes a no-op (types pass through unchanged)\n * When USE_PLAIN_PRIMITIVES = false: Full unbranding to base types\n */\nexport type Unbrand<T> = USE_PLAIN_PRIMITIVES_TYPE extends true\n ? T // 🚫 Smart types disabled: types pass through unchanged\n : // 🎯 Smart types enabled: extract base types and recurse\n // 1️⃣ If T has a brand, extract the base type (could be number, string, etc.)\n T extends number & { readonly __brand?: unknown }\n ? number\n : T extends string & { readonly __brand?: unknown }\n ? string\n : T extends boolean & { readonly __brand?: unknown }\n ? boolean\n : // 2️⃣ If T is an object (e.g. your params bag), recurse each field\n T extends Record<string, unknown>\n ? { [K in keyof T]: Unbrand<T[K]> }\n : // 3️⃣ Everything else stays the same\n T;\n\n/**\n * Given any function type F, produce a new function type whose\n * arguments have been passed through Unbrand<…>, and whose return\n * type you can also choose to unbrand (or leave alone).\n */\nexport type UnbrandFn<F, UnbrandReturn extends boolean = false> = F extends (\n ...args: infer A\n) => infer R\n ? A extends readonly unknown[]\n ? (\n ...args: { [K in keyof A]: Unbrand<A[K]> }\n ) => UnbrandReturn extends true ? Unbrand<R> : R\n : never\n : never;\n\n// Export the BaseOf utility for advanced users\nexport type { BaseOf };\n"],"names":["USE_PLAIN_PRIMITIVES"],"mappings":"gFAoBO,MAAMA,EAAuB"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @penner/smart-primitive - Type-safe branded primitives with zero runtime overhead
|
|
3
|
+
*
|
|
4
|
+
* Prevent bugs by distinguishing different kinds of numbers, strings, and booleans
|
|
5
|
+
* at compile time, with no runtime cost.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { SmartNumber, SmartString } from '@penner/smart-primitive';
|
|
10
|
+
*
|
|
11
|
+
* type Pixels = SmartNumber<'Pixels'>;
|
|
12
|
+
* type Milliseconds = SmartNumber<'Milliseconds'>;
|
|
13
|
+
* type URL = SmartString<'URL'>;
|
|
14
|
+
*
|
|
15
|
+
* let width: Pixels = 300; // ✅ works
|
|
16
|
+
* let delay: Milliseconds = 500; // ✅ works
|
|
17
|
+
* let oops: Pixels = delay; // ❌ type error - caught!
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export type { SmartPrimitive, SmartNumber, SmartString, SmartBoolean, Unbrand, UnbrandFn, USE_PLAIN_PRIMITIVES_TYPE, BaseOf, } from './SmartPrimitive';
|
|
21
|
+
export { USE_PLAIN_PRIMITIVES } from './SmartPrimitive';
|
|
22
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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,yBAAyB,EACzB,MAAM,GACP,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/index.es.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.es.js","sources":["../src/SmartPrimitive.ts"],"sourcesContent":["/**\n * 🔧 FEATURE FLAG: Toggle smart primitive type checking\n * When false (default): SmartPrimitive types provide type safety\n * When true: All SmartNumber, SmartString, etc. become plain primitives\n *\n * Use cases:\n * - Performance testing (eliminate type overhead)\n * - Debugging type issues\n * - Gradual migration to/from smart primitives\n * - Bundle size optimization\n *\n * How to use:\n * 1. Change `false` to `true` below\n * 2. Your entire codebase now treats Pixels, URLs, etc. as plain primitives\n * 3. Cross-brand assignments that were errors become allowed\n * 4. All type extraction utilities become no-ops\n * 5. Change back to `false` to re-enable smart primitive type safety\n */\n\n// 🔧 FEATURE FLAG: Both runtime constant AND compile-time type\nexport const USE_PLAIN_PRIMITIVES = false as const; // 👈 Change to `true` for plain primitives, `false` for branded types\nexport type USE_PLAIN_PRIMITIVES_TYPE = typeof USE_PLAIN_PRIMITIVES;\n\n/**\n * Clean type extraction using the brand pattern\n * Respects the USE_PLAIN_PRIMITIVES flag.\n */\ntype BaseOf<T> = USE_PLAIN_PRIMITIVES_TYPE extends true\n ? T // 🚫 Smart types disabled: type is already the base\n : T extends number & { readonly __brand?: unknown }\n ? number\n : T extends string & { readonly __brand?: unknown }\n ? string\n : T extends boolean & { readonly __brand?: unknown }\n ? boolean\n : T; // 🎯 Extract base type or return as-is\n\n/**\n * Generic smart primitive type that provides **opt-in type safety** while staying flexible.\n *\n * **How it works:**\n * - ✅ **Accepts plain values**: You can use regular numbers, strings, etc. directly\n * - ✅ **Prevents cross-domain mixing**: TypeScript stops you from using pixels where milliseconds are expected\n * - ✅ **Zero runtime cost**: No performance impact - it's just TypeScript magic\n * - ✅ **Easy to disable**: Toggle `USE_PLAIN_PRIMITIVES` to turn off all smart typing\n *\n * **Example:**\n * ```ts\n * type Pixels = SmartPrimitive<number, 'Pixels'>;\n * type Milliseconds = SmartPrimitive<number, 'Milliseconds'>;\n *\n * let width: Pixels = 300; // ✅ Plain number works\n * let delay: Milliseconds = 500; // ✅ Plain number works\n * let oops: Pixels = delay; // ❌ TypeScript error - caught the mistake!\n * ```\n *\n * This prevents bugs like accidentally using milliseconds where pixels are expected,\n * while keeping your code simple and readable.\n */\nexport type SmartPrimitive<\n Base extends string | number | boolean | bigint | symbol,\n BrandName extends string,\n> = USE_PLAIN_PRIMITIVES_TYPE extends true\n ? Base\n : Base & { readonly __brand?: BrandName };\n\n/**\n * A smart number type for domain-specific numeric values like pixels, milliseconds, etc.\n *\n * **Why use this?** Prevents common bugs like mixing up different kinds of numbers:\n * - Using milliseconds where pixels are expected\n * - Passing a width value to a duration parameter\n * - Confusing degrees with radians\n *\n * **How it works:**\n * - ✅ Works with plain numbers: `let width: Pixels = 300`\n * - ✅ Catches domain mix-ups: `let width: Pixels = duration` → TypeScript error\n * - ✅ Zero runtime cost: Just TypeScript checking, no JavaScript overhead\n *\n * Built on `SmartPrimitive` - respects the USE_PLAIN_PRIMITIVES flag for easy toggling.\n *\n * **Example:**\n * ```ts\n * type Pixels = SmartNumber<'Pixels'>;\n * type Milliseconds = SmartNumber<'Milliseconds'>;\n *\n * let width: Pixels = 300; // ✅ works\n * let delay: Milliseconds = 500; // ✅ works\n * let badAssign: Pixels = delay; // ❌ type error - caught the mistake!\n * ```\n *\n * When USE_PLAIN_PRIMITIVES = false (default):\n * - Full smart number system with type safety between different units\n * - Plain numbers accepted seamlessly\n * - Clean tooltip display\n *\n * When USE_PLAIN_PRIMITIVES = true:\n * - All smart number types collapse to plain `number`\n * - Zero runtime overhead, maximum performance\n *\n * Usage remains the same regardless of flag state:\n * ```ts\n * let distance: Pixels = 300; // ✅ always works\n * let branded: Pixels = 300 as Pixels; // ✅ always works\n * ```\n */\nexport type SmartNumber<BrandName extends string> = SmartPrimitive<\n number,\n BrandName\n>;\n\n/**\n * A smart string type that accepts plain strings but maintains type identity.\n * Perfect for things like URLs, CSS selectors, or other domain-specific strings.\n *\n * Usage:\n * ```ts\n * type URL = SmartString<'URL'>;\n * type CSSSelector = SmartString<'CSSSelector'>;\n *\n * let url: URL = \"https://example.com\"; // ✅ works\n * let selector: CSSSelector = \".my-class\"; // ✅ works\n * let badAssign: URL = selector; // ❌ type error\n * ```\n */\nexport type SmartString<BrandName extends string> = SmartPrimitive<\n string,\n BrandName\n>;\n\n/**\n * A smart boolean type that accepts plain booleans but maintains type identity.\n * Useful for domain-specific flags or state indicators.\n *\n * Usage:\n * ```ts\n * type IsVisible = SmartBoolean<'IsVisible'>;\n * type IsEnabled = SmartBoolean<'IsEnabled'>;\n *\n * let visible: IsVisible = true; // ✅ works\n * let enabled: IsEnabled = false; // ✅ works\n * let badAssign: IsVisible = enabled; // ❌ type error\n * ```\n */\nexport type SmartBoolean<BrandName extends string> = SmartPrimitive<\n boolean,\n BrandName\n>;\n\n/**\n * Simplified Unbrand using the generic brand pattern.\n * Also respects the USE_PLAIN_PRIMITIVES flag.\n *\n * When USE_PLAIN_PRIMITIVES = true: This becomes a no-op (types pass through unchanged)\n * When USE_PLAIN_PRIMITIVES = false: Full unbranding to base types\n */\nexport type Unbrand<T> = USE_PLAIN_PRIMITIVES_TYPE extends true\n ? T // 🚫 Smart types disabled: types pass through unchanged\n : // 🎯 Smart types enabled: extract base types and recurse\n // 1️⃣ If T has a brand, extract the base type (could be number, string, etc.)\n T extends number & { readonly __brand?: unknown }\n ? number\n : T extends string & { readonly __brand?: unknown }\n ? string\n : T extends boolean & { readonly __brand?: unknown }\n ? boolean\n : // 2️⃣ If T is an object (e.g. your params bag), recurse each field\n T extends Record<string, unknown>\n ? { [K in keyof T]: Unbrand<T[K]> }\n : // 3️⃣ Everything else stays the same\n T;\n\n/**\n * Given any function type F, produce a new function type whose\n * arguments have been passed through Unbrand<…>, and whose return\n * type you can also choose to unbrand (or leave alone).\n */\nexport type UnbrandFn<F, UnbrandReturn extends boolean = false> = F extends (\n ...args: infer A\n) => infer R\n ? A extends readonly unknown[]\n ? (\n ...args: { [K in keyof A]: Unbrand<A[K]> }\n ) => UnbrandReturn extends true ? Unbrand<R> : R\n : never\n : never;\n\n// Export the BaseOf utility for advanced users\nexport type { BaseOf };\n"],"names":["USE_PLAIN_PRIMITIVES"],"mappings":"AAoBO,MAAMA,IAAuB;"}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@penner/smart-primitive",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Type-safe branded primitives with zero runtime overhead - prevent bugs by distinguishing different kinds of numbers, strings, and booleans",
|
|
5
|
+
"author": "Robert Penner <robert@robertpenner.com> (https://robertpenner.com)",
|
|
6
|
+
"homepage": "https://github.com/robertpenner/penner",
|
|
7
|
+
"repository": "github:robertpenner/penner",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=20.17"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"typescript",
|
|
17
|
+
"branded-types",
|
|
18
|
+
"type-safety",
|
|
19
|
+
"smart-types",
|
|
20
|
+
"phantom-types",
|
|
21
|
+
"zero-runtime",
|
|
22
|
+
"compile-time",
|
|
23
|
+
"units",
|
|
24
|
+
"type-checking",
|
|
25
|
+
"developer-experience"
|
|
26
|
+
],
|
|
27
|
+
"main": "dist/index.cjs.js",
|
|
28
|
+
"module": "dist/index.es.js",
|
|
29
|
+
"types": "dist/index.d.ts",
|
|
30
|
+
"type": "module",
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"README.md",
|
|
34
|
+
"LICENSE"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"dev": "tsc && vite build --watch",
|
|
38
|
+
"build": "tsc && vite build",
|
|
39
|
+
"build:types": "dts-bundle-generator --config ./dts-bundle-generator.config.ts",
|
|
40
|
+
"lint:scripts": "eslint ./src --ext .ts",
|
|
41
|
+
"format:scripts": "prettier ./src --write",
|
|
42
|
+
"test": "vitest run --typecheck",
|
|
43
|
+
"test:watch": "vitest"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/node": "^22.8.1",
|
|
48
|
+
"@typescript-eslint/eslint-plugin": "^8.11.0",
|
|
49
|
+
"@typescript-eslint/parser": "^8.11.0",
|
|
50
|
+
"@vitest/coverage-v8": "^3.2.2",
|
|
51
|
+
"dts-bundle-generator": "^9.5.1",
|
|
52
|
+
"eslint": "^9.13.0",
|
|
53
|
+
"eslint-config-prettier": "^9.1.0",
|
|
54
|
+
"eslint-plugin-prettier": "^5.2.1",
|
|
55
|
+
"prettier": "^3.3.3",
|
|
56
|
+
"tslib": "^2.8.0",
|
|
57
|
+
"tsx": "^4.20.3",
|
|
58
|
+
"typescript": "^5.8.3",
|
|
59
|
+
"vite": "^5.4.10",
|
|
60
|
+
"vite-plugin-dts": "^4.3.0",
|
|
61
|
+
"vite-tsconfig-paths": "^5.1.4",
|
|
62
|
+
"vitest": "^3.2.2"
|
|
63
|
+
}
|
|
64
|
+
}
|