typetify 0.1.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/LICENSE +21 -0
- package/README.md +327 -0
- package/dist/async/index.d.mts +152 -0
- package/dist/async/index.d.ts +152 -0
- package/dist/async/index.js +49 -0
- package/dist/async/index.js.map +1 -0
- package/dist/async/index.mjs +4 -0
- package/dist/async/index.mjs.map +1 -0
- package/dist/chunk-2LJ6NZ6K.js +108 -0
- package/dist/chunk-2LJ6NZ6K.js.map +1 -0
- package/dist/chunk-44Y5JSGU.js +145 -0
- package/dist/chunk-44Y5JSGU.js.map +1 -0
- package/dist/chunk-4NXETABV.mjs +116 -0
- package/dist/chunk-4NXETABV.mjs.map +1 -0
- package/dist/chunk-6ZBTL74K.js +129 -0
- package/dist/chunk-6ZBTL74K.js.map +1 -0
- package/dist/chunk-CN3GYRJN.mjs +137 -0
- package/dist/chunk-CN3GYRJN.mjs.map +1 -0
- package/dist/chunk-CNTE6ZVH.js +359 -0
- package/dist/chunk-CNTE6ZVH.js.map +1 -0
- package/dist/chunk-DBENOSTA.js +156 -0
- package/dist/chunk-DBENOSTA.js.map +1 -0
- package/dist/chunk-DWIG5GF2.js +135 -0
- package/dist/chunk-DWIG5GF2.js.map +1 -0
- package/dist/chunk-EAUTTWTQ.mjs +231 -0
- package/dist/chunk-EAUTTWTQ.mjs.map +1 -0
- package/dist/chunk-FXWYPHA3.mjs +13 -0
- package/dist/chunk-FXWYPHA3.mjs.map +1 -0
- package/dist/chunk-GS3PP67B.js +200 -0
- package/dist/chunk-GS3PP67B.js.map +1 -0
- package/dist/chunk-J5LGTIGS.mjs +9 -0
- package/dist/chunk-J5LGTIGS.mjs.map +1 -0
- package/dist/chunk-JAOGY4JO.mjs +1007 -0
- package/dist/chunk-JAOGY4JO.mjs.map +1 -0
- package/dist/chunk-JB6UXRKD.mjs +97 -0
- package/dist/chunk-JB6UXRKD.mjs.map +1 -0
- package/dist/chunk-JQHUBZ4M.js +88 -0
- package/dist/chunk-JQHUBZ4M.js.map +1 -0
- package/dist/chunk-L3M7LGKL.mjs +128 -0
- package/dist/chunk-L3M7LGKL.mjs.map +1 -0
- package/dist/chunk-LT7JK7RJ.js +87 -0
- package/dist/chunk-LT7JK7RJ.js.map +1 -0
- package/dist/chunk-OEJK37LO.mjs +328 -0
- package/dist/chunk-OEJK37LO.mjs.map +1 -0
- package/dist/chunk-OPVES6W2.js +16 -0
- package/dist/chunk-OPVES6W2.js.map +1 -0
- package/dist/chunk-OWNUKWXV.js +291 -0
- package/dist/chunk-OWNUKWXV.js.map +1 -0
- package/dist/chunk-PQTXSQ4P.js +369 -0
- package/dist/chunk-PQTXSQ4P.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +11 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-QFR7DVAJ.mjs +63 -0
- package/dist/chunk-QFR7DVAJ.mjs.map +1 -0
- package/dist/chunk-SGQNLTRK.js +73 -0
- package/dist/chunk-SGQNLTRK.js.map +1 -0
- package/dist/chunk-SIA5BSVY.js +1054 -0
- package/dist/chunk-SIA5BSVY.js.map +1 -0
- package/dist/chunk-SRDWUHDY.mjs +188 -0
- package/dist/chunk-SRDWUHDY.mjs.map +1 -0
- package/dist/chunk-TXU7NTT4.js +249 -0
- package/dist/chunk-TXU7NTT4.js.map +1 -0
- package/dist/chunk-TZEWREAC.mjs +277 -0
- package/dist/chunk-TZEWREAC.mjs.map +1 -0
- package/dist/chunk-V6CWFDIJ.mjs +123 -0
- package/dist/chunk-V6CWFDIJ.mjs.map +1 -0
- package/dist/chunk-YBJC5WMX.mjs +341 -0
- package/dist/chunk-YBJC5WMX.mjs.map +1 -0
- package/dist/chunk-YOPAXITF.mjs +75 -0
- package/dist/chunk-YOPAXITF.mjs.map +1 -0
- package/dist/chunk-ZE4FDBRI.mjs +79 -0
- package/dist/chunk-ZE4FDBRI.mjs.map +1 -0
- package/dist/collection/index.d.mts +291 -0
- package/dist/collection/index.d.ts +291 -0
- package/dist/collection/index.js +125 -0
- package/dist/collection/index.js.map +1 -0
- package/dist/collection/index.mjs +4 -0
- package/dist/collection/index.mjs.map +1 -0
- package/dist/core/index.d.mts +85 -0
- package/dist/core/index.d.ts +85 -0
- package/dist/core/index.js +41 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/index.mjs +4 -0
- package/dist/core/index.mjs.map +1 -0
- package/dist/decorator/index.d.mts +165 -0
- package/dist/decorator/index.d.ts +165 -0
- package/dist/decorator/index.js +57 -0
- package/dist/decorator/index.js.map +1 -0
- package/dist/decorator/index.mjs +4 -0
- package/dist/decorator/index.mjs.map +1 -0
- package/dist/dx/index.d.mts +125 -0
- package/dist/dx/index.d.ts +125 -0
- package/dist/dx/index.js +53 -0
- package/dist/dx/index.js.map +1 -0
- package/dist/dx/index.mjs +4 -0
- package/dist/dx/index.mjs.map +1 -0
- package/dist/flow/index.d.mts +165 -0
- package/dist/flow/index.d.ts +165 -0
- package/dist/flow/index.js +50 -0
- package/dist/flow/index.js.map +1 -0
- package/dist/flow/index.mjs +5 -0
- package/dist/flow/index.mjs.map +1 -0
- package/dist/fn/index.d.mts +77 -0
- package/dist/fn/index.d.ts +77 -0
- package/dist/fn/index.js +37 -0
- package/dist/fn/index.js.map +1 -0
- package/dist/fn/index.mjs +4 -0
- package/dist/fn/index.mjs.map +1 -0
- package/dist/guards/index.d.mts +165 -0
- package/dist/guards/index.d.ts +165 -0
- package/dist/guards/index.js +69 -0
- package/dist/guards/index.js.map +1 -0
- package/dist/guards/index.mjs +4 -0
- package/dist/guards/index.mjs.map +1 -0
- package/dist/index.d.mts +228 -0
- package/dist/index.d.ts +228 -0
- package/dist/index.js +775 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +51 -0
- package/dist/index.mjs.map +1 -0
- package/dist/input/index.d.mts +185 -0
- package/dist/input/index.d.ts +185 -0
- package/dist/input/index.js +74 -0
- package/dist/input/index.js.map +1 -0
- package/dist/input/index.mjs +5 -0
- package/dist/input/index.mjs.map +1 -0
- package/dist/iterator/index.d.mts +209 -0
- package/dist/iterator/index.d.ts +209 -0
- package/dist/iterator/index.js +522 -0
- package/dist/iterator/index.js.map +1 -0
- package/dist/iterator/index.mjs +502 -0
- package/dist/iterator/index.mjs.map +1 -0
- package/dist/logic/index.d.mts +531 -0
- package/dist/logic/index.d.ts +531 -0
- package/dist/logic/index.js +416 -0
- package/dist/logic/index.js.map +1 -0
- package/dist/logic/index.mjs +367 -0
- package/dist/logic/index.mjs.map +1 -0
- package/dist/math/index.d.mts +86 -0
- package/dist/math/index.d.ts +86 -0
- package/dist/math/index.js +45 -0
- package/dist/math/index.js.map +1 -0
- package/dist/math/index.mjs +4 -0
- package/dist/math/index.mjs.map +1 -0
- package/dist/narrowing/index.d.mts +429 -0
- package/dist/narrowing/index.d.ts +429 -0
- package/dist/narrowing/index.js +220 -0
- package/dist/narrowing/index.js.map +1 -0
- package/dist/narrowing/index.mjs +186 -0
- package/dist/narrowing/index.mjs.map +1 -0
- package/dist/object/index.d.mts +327 -0
- package/dist/object/index.d.ts +327 -0
- package/dist/object/index.js +113 -0
- package/dist/object/index.js.map +1 -0
- package/dist/object/index.mjs +4 -0
- package/dist/object/index.mjs.map +1 -0
- package/dist/result/index.d.mts +201 -0
- package/dist/result/index.d.ts +201 -0
- package/dist/result/index.js +86 -0
- package/dist/result/index.js.map +1 -0
- package/dist/result/index.mjs +5 -0
- package/dist/result/index.mjs.map +1 -0
- package/dist/schema/index.d.mts +216 -0
- package/dist/schema/index.d.ts +216 -0
- package/dist/schema/index.js +410 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/index.mjs +384 -0
- package/dist/schema/index.mjs.map +1 -0
- package/dist/string/index.d.mts +102 -0
- package/dist/string/index.d.ts +102 -0
- package/dist/string/index.js +49 -0
- package/dist/string/index.js.map +1 -0
- package/dist/string/index.mjs +4 -0
- package/dist/string/index.mjs.map +1 -0
- package/dist/typed/index.d.mts +1962 -0
- package/dist/typed/index.d.ts +1962 -0
- package/dist/typed/index.js +193 -0
- package/dist/typed/index.js.map +1 -0
- package/dist/typed/index.mjs +4 -0
- package/dist/typed/index.mjs.map +1 -0
- package/dist/types-Db0vauC3.d.mts +258 -0
- package/dist/types-Db0vauC3.d.ts +258 -0
- package/dist/types-VsDp2t8s.d.mts +30 -0
- package/dist/types-VsDp2t8s.d.ts +30 -0
- package/package.json +157 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 typetify contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
# typetify
|
|
2
|
+
|
|
3
|
+
> Runtime TypeScript helpers — like Lodash, but TS-first.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/typetify)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
|
|
9
|
+
## Why typetify?
|
|
10
|
+
|
|
11
|
+
TypeScript is powerful, but it doesn't protect you at runtime. Data from APIs, forms, localStorage, or JSON can break your app even when TypeScript says everything is fine.
|
|
12
|
+
|
|
13
|
+
**typetify** fills this gap with:
|
|
14
|
+
|
|
15
|
+
- ✅ **Runtime safety** — Guards and assertions that work at runtime
|
|
16
|
+
- ✅ **Perfect types** — IntelliSense that actually helps
|
|
17
|
+
- ✅ **Zero dependencies** — Lightweight and tree-shakable
|
|
18
|
+
- ✅ **Boring API** — No magic, no config, just functions
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install typetify
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { isDefined, pick, awaitTo, safeJsonParse } from 'typetify'
|
|
30
|
+
|
|
31
|
+
// Filter null/undefined with proper types
|
|
32
|
+
const items = [1, null, 2, undefined, 3]
|
|
33
|
+
const defined = items.filter(isDefined) // number[]
|
|
34
|
+
|
|
35
|
+
// Pick object keys (type-safe)
|
|
36
|
+
const user = { id: 1, name: 'John', password: 'secret' }
|
|
37
|
+
const safe = pick(user, ['id', 'name']) // { id: number, name: string }
|
|
38
|
+
|
|
39
|
+
// Handle async errors without try/catch
|
|
40
|
+
const [error, data] = await awaitTo(fetchUser(id))
|
|
41
|
+
if (error) {
|
|
42
|
+
console.error('Failed:', error)
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
console.log(data.name)
|
|
46
|
+
|
|
47
|
+
// Parse JSON safely
|
|
48
|
+
const result = safeJsonParse<User>(jsonString)
|
|
49
|
+
if (result.ok) {
|
|
50
|
+
console.log(result.data.name)
|
|
51
|
+
} else {
|
|
52
|
+
console.error(result.error)
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Modules
|
|
57
|
+
|
|
58
|
+
### Core — Foundations
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { isDefined, isNil, assert, assertDefined, fail, noop, identity, unreachable } from 'typetify/core'
|
|
62
|
+
|
|
63
|
+
// isDefined — Filter null/undefined
|
|
64
|
+
const items = [1, null, 2].filter(isDefined) // number[]
|
|
65
|
+
|
|
66
|
+
// assert — Fail fast with type narrowing
|
|
67
|
+
const user: User | null = getUser()
|
|
68
|
+
assert(user, 'User not found')
|
|
69
|
+
// user is now User
|
|
70
|
+
|
|
71
|
+
// unreachable — Exhaustive switch statements
|
|
72
|
+
switch (status) {
|
|
73
|
+
case 'pending': return handlePending()
|
|
74
|
+
case 'done': return handleDone()
|
|
75
|
+
default: unreachable(status)
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Guards — Type Guards
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { isObject, isString, isNumber, hasKey, hasKeys, isEmpty } from 'typetify/guards'
|
|
83
|
+
|
|
84
|
+
// isObject — Check for objects (not arrays, not null)
|
|
85
|
+
if (isObject(value)) {
|
|
86
|
+
// value is Record<string, unknown>
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// hasKey — Safe property access
|
|
90
|
+
if (hasKey(response, 'data')) {
|
|
91
|
+
console.log(response.data)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// isEmpty — Check for empty values
|
|
95
|
+
isEmpty('') // true
|
|
96
|
+
isEmpty([]) // true
|
|
97
|
+
isEmpty({}) // true
|
|
98
|
+
isEmpty(null) // true
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Object — Object Manipulation
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import { pick, omit, keysTyped, mapObject, get, set } from 'typetify/object'
|
|
105
|
+
|
|
106
|
+
// pick/omit — Type-safe object manipulation
|
|
107
|
+
const user = { id: 1, name: 'John', password: 'secret' }
|
|
108
|
+
pick(user, ['id', 'name']) // { id: 1, name: 'John' }
|
|
109
|
+
omit(user, ['password']) // { id: 1, name: 'John' }
|
|
110
|
+
|
|
111
|
+
// keysTyped — Object.keys with proper types
|
|
112
|
+
const keys = keysTyped(user) // ('id' | 'name' | 'password')[]
|
|
113
|
+
|
|
114
|
+
// mapObject — Map over object values
|
|
115
|
+
const prices = { apple: 1, banana: 2 }
|
|
116
|
+
mapObject(prices, v => v * 2) // { apple: 2, banana: 4 }
|
|
117
|
+
|
|
118
|
+
// get/set — Safe nested access (immutable)
|
|
119
|
+
get(user, ['profile', 'name'])
|
|
120
|
+
set(user, ['profile', 'age'], 30)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Async — Async Utilities
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { awaitTo, retry, sleep, withTimeout, debounce, throttle, parallel } from 'typetify/async'
|
|
127
|
+
|
|
128
|
+
// awaitTo — No more try/catch
|
|
129
|
+
const [error, user] = await awaitTo(fetchUser(id))
|
|
130
|
+
|
|
131
|
+
// retry — Retry with backoff
|
|
132
|
+
const data = await retry(() => fetchData(), {
|
|
133
|
+
attempts: 3,
|
|
134
|
+
delay: 1000,
|
|
135
|
+
backoff: 2,
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
// withTimeout — Add timeout to any promise
|
|
139
|
+
const result = await withTimeout(fetchData(), 5000)
|
|
140
|
+
|
|
141
|
+
// parallel — Concurrent execution with limit
|
|
142
|
+
const results = await parallel(
|
|
143
|
+
urls.map(url => () => fetch(url)),
|
|
144
|
+
{ concurrency: 3 }
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
// debounce/throttle
|
|
148
|
+
const debouncedSearch = debounce(search, 300)
|
|
149
|
+
const throttledScroll = throttle(onScroll, 100)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Collection — Array Utilities
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import { unique, groupBy, partition, chunk, compact, sortBy, range } from 'typetify/collection'
|
|
156
|
+
|
|
157
|
+
// unique — Remove duplicates
|
|
158
|
+
unique([1, 2, 2, 3]) // [1, 2, 3]
|
|
159
|
+
unique(users, u => u.id) // Unique by key
|
|
160
|
+
|
|
161
|
+
// groupBy — Group by key
|
|
162
|
+
groupBy(users, u => u.role)
|
|
163
|
+
// { admin: [...], user: [...] }
|
|
164
|
+
|
|
165
|
+
// partition — Split by predicate
|
|
166
|
+
const [evens, odds] = partition([1, 2, 3, 4], n => n % 2 === 0)
|
|
167
|
+
|
|
168
|
+
// chunk — Split into chunks
|
|
169
|
+
chunk([1, 2, 3, 4, 5], 2) // [[1, 2], [3, 4], [5]]
|
|
170
|
+
|
|
171
|
+
// compact — Remove null/undefined
|
|
172
|
+
compact([1, null, 2, undefined]) // [1, 2]
|
|
173
|
+
|
|
174
|
+
// sortBy — Sort by key
|
|
175
|
+
sortBy(users, u => u.name)
|
|
176
|
+
|
|
177
|
+
// range — Generate number ranges
|
|
178
|
+
range(0, 5) // [0, 1, 2, 3, 4]
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Input — Parse External Data
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import { safeJsonParse, parseNumber, parseBoolean, parseDate, coerceArray, defaults } from 'typetify/input'
|
|
185
|
+
|
|
186
|
+
// safeJsonParse — No more try/catch for JSON
|
|
187
|
+
const result = safeJsonParse<User>(json)
|
|
188
|
+
if (result.ok) {
|
|
189
|
+
console.log(result.data)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// parseNumber/parseBoolean/parseDate — Safe parsing
|
|
193
|
+
parseNumber('42') // 42
|
|
194
|
+
parseNumber('abc') // undefined
|
|
195
|
+
parseBoolean('yes') // true
|
|
196
|
+
parseDate('2024-01-15') // Date
|
|
197
|
+
|
|
198
|
+
// coerceArray — Ensure array
|
|
199
|
+
coerceArray('hello') // ['hello']
|
|
200
|
+
coerceArray(['a', 'b']) // ['a', 'b']
|
|
201
|
+
coerceArray(null) // []
|
|
202
|
+
|
|
203
|
+
// defaults — With empty string handling
|
|
204
|
+
defaults(null, 'fallback') // 'fallback'
|
|
205
|
+
defaults('', 'fallback') // 'fallback'
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Flow — Functional Utilities
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
import { pipe, tap, when, match, tryCatch, ifElse } from 'typetify/flow'
|
|
212
|
+
|
|
213
|
+
// pipe — Chain transformations
|
|
214
|
+
const result = pipe(
|
|
215
|
+
5,
|
|
216
|
+
n => n * 2,
|
|
217
|
+
n => n + 1,
|
|
218
|
+
n => `Result: ${n}`
|
|
219
|
+
) // 'Result: 11'
|
|
220
|
+
|
|
221
|
+
// tap — Side effects in a chain
|
|
222
|
+
pipe(
|
|
223
|
+
data,
|
|
224
|
+
tap(console.log),
|
|
225
|
+
transform,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
// match — Pattern matching
|
|
229
|
+
const getDiscount = match<number, string>()
|
|
230
|
+
.with(n => n >= 100, () => '20% off')
|
|
231
|
+
.with(n => n >= 50, () => '10% off')
|
|
232
|
+
.otherwise(() => 'No discount')
|
|
233
|
+
|
|
234
|
+
// tryCatch — Safe function execution
|
|
235
|
+
const result = tryCatch(() => JSON.parse(input))
|
|
236
|
+
if (result.ok) {
|
|
237
|
+
console.log(result.value)
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### DX — Developer Experience
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
import { debug, invariant, assertNever, todo, measure } from 'typetify/dx'
|
|
245
|
+
|
|
246
|
+
// debug — Log in a pipe chain
|
|
247
|
+
pipe(data, debug('step 1'), transform, debug('step 2'))
|
|
248
|
+
|
|
249
|
+
// invariant — Assert with descriptive errors
|
|
250
|
+
invariant(user.id > 0, 'User ID must be positive')
|
|
251
|
+
// Throws: Invariant violation: User ID must be positive
|
|
252
|
+
|
|
253
|
+
// assertNever — Exhaustive checks
|
|
254
|
+
function handle(action: Action) {
|
|
255
|
+
switch (action.type) {
|
|
256
|
+
case 'add': return handleAdd()
|
|
257
|
+
case 'remove': return handleRemove()
|
|
258
|
+
default: assertNever(action)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// todo — Mark unimplemented code
|
|
263
|
+
function processPayment() {
|
|
264
|
+
todo('Implement payment processing')
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// measure — Performance measurement
|
|
268
|
+
const { result, duration } = measure(() => heavyComputation())
|
|
269
|
+
console.log(`Took ${duration}ms`)
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Typed — Type Utilities
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
import { defineConst, defineEnum, brand, type DeepPartial, type Merge } from 'typetify/typed'
|
|
276
|
+
|
|
277
|
+
// defineConst — Frozen constants with literal types
|
|
278
|
+
const STATUS = defineConst({
|
|
279
|
+
PENDING: 'pending',
|
|
280
|
+
ACTIVE: 'active',
|
|
281
|
+
})
|
|
282
|
+
// typeof STATUS.PENDING = 'pending' (not string)
|
|
283
|
+
|
|
284
|
+
// defineEnum — Enum-like objects
|
|
285
|
+
const Role = defineEnum(['admin', 'user', 'guest'] as const)
|
|
286
|
+
// Role.admin = 'admin'
|
|
287
|
+
|
|
288
|
+
// brand — Branded types for type safety
|
|
289
|
+
type UserId = Brand<number, 'UserId'>
|
|
290
|
+
type PostId = Brand<number, 'PostId'>
|
|
291
|
+
|
|
292
|
+
function getUser(id: UserId) { ... }
|
|
293
|
+
getUser(1 as UserId) // OK
|
|
294
|
+
getUser(1 as PostId) // Error!
|
|
295
|
+
|
|
296
|
+
// Type utilities
|
|
297
|
+
type PartialUser = DeepPartial<User>
|
|
298
|
+
type MergedConfig = Merge<DefaultConfig, UserConfig>
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Tree Shaking
|
|
302
|
+
|
|
303
|
+
Import only what you need:
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
// Import specific functions
|
|
307
|
+
import { isDefined } from 'typetify/core'
|
|
308
|
+
import { pick } from 'typetify/object'
|
|
309
|
+
|
|
310
|
+
// Or import everything
|
|
311
|
+
import { isDefined, pick, awaitTo } from 'typetify'
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## Philosophy
|
|
315
|
+
|
|
316
|
+
1. **Runtime first** — Types are great, but runtime safety matters more
|
|
317
|
+
2. **No magic** — Every function does exactly what it says
|
|
318
|
+
3. **Composable** — Small functions that work together
|
|
319
|
+
4. **TypeScript-native** — Built for TS, not ported from JS
|
|
320
|
+
|
|
321
|
+
## Contributing
|
|
322
|
+
|
|
323
|
+
Contributions are welcome! Please read our contributing guidelines first.
|
|
324
|
+
|
|
325
|
+
## License
|
|
326
|
+
|
|
327
|
+
MIT © typetify
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wraps a promise to return a tuple of [error, result].
|
|
3
|
+
* Eliminates the need for try/catch blocks.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* const [error, user] = await awaitTo(fetchUser(id))
|
|
7
|
+
* if (error) {
|
|
8
|
+
* console.error('Failed to fetch user:', error)
|
|
9
|
+
* return
|
|
10
|
+
* }
|
|
11
|
+
* console.log(user.name)
|
|
12
|
+
*/
|
|
13
|
+
declare function awaitTo<T, E = Error>(promise: Promise<T>): Promise<[E, null] | [null, T]>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Returns a promise that resolves after the specified milliseconds.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* await sleep(1000) // wait 1 second
|
|
20
|
+
* console.log('Done waiting')
|
|
21
|
+
*/
|
|
22
|
+
declare function sleep(ms: number): Promise<void>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Wraps a promise with a timeout. Rejects if the promise doesn't resolve in time.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* const result = await withTimeout(fetchData(), 5000)
|
|
29
|
+
* // Throws if fetchData takes more than 5 seconds
|
|
30
|
+
*/
|
|
31
|
+
declare function withTimeout<T>(promise: Promise<T>, ms: number, message?: string): Promise<T>;
|
|
32
|
+
|
|
33
|
+
interface RetryOptions {
|
|
34
|
+
/** Maximum number of attempts (default: 3) */
|
|
35
|
+
attempts?: number;
|
|
36
|
+
/** Delay between attempts in ms (default: 1000) */
|
|
37
|
+
delay?: number;
|
|
38
|
+
/** Multiply delay by this factor after each attempt (default: 1) */
|
|
39
|
+
backoff?: number;
|
|
40
|
+
/** Called when an attempt fails */
|
|
41
|
+
onRetry?: (error: Error, attempt: number) => void;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Retries a function until it succeeds or max attempts is reached.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* const data = await retry(() => fetchData(), {
|
|
48
|
+
* attempts: 3,
|
|
49
|
+
* delay: 1000,
|
|
50
|
+
* backoff: 2,
|
|
51
|
+
* })
|
|
52
|
+
*/
|
|
53
|
+
declare function retry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Creates a debounced version of a function.
|
|
57
|
+
* The function will only be called after it stops being called for the specified delay.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* const debouncedSearch = debounce((query: string) => {
|
|
61
|
+
* console.log('Searching:', query)
|
|
62
|
+
* }, 300)
|
|
63
|
+
*
|
|
64
|
+
* debouncedSearch('h')
|
|
65
|
+
* debouncedSearch('he')
|
|
66
|
+
* debouncedSearch('hello') // Only this one executes after 300ms
|
|
67
|
+
*/
|
|
68
|
+
declare function debounce<T extends (...args: Parameters<T>) => void>(fn: T, delay: number): ((...args: Parameters<T>) => void) & {
|
|
69
|
+
cancel: () => void;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Creates a throttled version of a function.
|
|
74
|
+
* The function will only be called at most once per specified interval.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* const throttledScroll = throttle(() => {
|
|
78
|
+
* console.log('Scroll event')
|
|
79
|
+
* }, 100)
|
|
80
|
+
*
|
|
81
|
+
* window.addEventListener('scroll', throttledScroll)
|
|
82
|
+
*/
|
|
83
|
+
declare function throttle<T extends (...args: Parameters<T>) => void>(fn: T, interval: number): ((...args: Parameters<T>) => void) & {
|
|
84
|
+
cancel: () => void;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Creates a function that can only be called once.
|
|
89
|
+
* Subsequent calls return the result of the first call.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* const initialize = once(() => {
|
|
93
|
+
* console.log('Initializing...')
|
|
94
|
+
* return { ready: true }
|
|
95
|
+
* })
|
|
96
|
+
*
|
|
97
|
+
* initialize() // logs 'Initializing...', returns { ready: true }
|
|
98
|
+
* initialize() // returns { ready: true } without logging
|
|
99
|
+
*/
|
|
100
|
+
declare function once<T extends (...args: Parameters<T>) => ReturnType<T>>(fn: T): T;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Creates an async function that can only be called once.
|
|
104
|
+
* Subsequent calls return the same promise.
|
|
105
|
+
* Useful for initialization that should only happen once.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* const loadConfig = onceAsync(async () => {
|
|
109
|
+
* const response = await fetch('/config')
|
|
110
|
+
* return response.json()
|
|
111
|
+
* })
|
|
112
|
+
*
|
|
113
|
+
* // Both calls return the same promise
|
|
114
|
+
* const config1 = await loadConfig()
|
|
115
|
+
* const config2 = await loadConfig()
|
|
116
|
+
*/
|
|
117
|
+
declare function onceAsync<T>(fn: () => Promise<T>): () => Promise<T>;
|
|
118
|
+
|
|
119
|
+
interface Deferred<T> {
|
|
120
|
+
promise: Promise<T>;
|
|
121
|
+
resolve: (value: T | PromiseLike<T>) => void;
|
|
122
|
+
reject: (reason?: unknown) => void;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Creates a deferred promise that can be resolved or rejected externally.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* const deferred = defer<string>()
|
|
129
|
+
*
|
|
130
|
+
* setTimeout(() => {
|
|
131
|
+
* deferred.resolve('Done!')
|
|
132
|
+
* }, 1000)
|
|
133
|
+
*
|
|
134
|
+
* const result = await deferred.promise
|
|
135
|
+
*/
|
|
136
|
+
declare function defer<T>(): Deferred<T>;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Runs async functions in parallel with a concurrency limit.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* const urls = ['url1', 'url2', 'url3', 'url4', 'url5']
|
|
143
|
+
* const results = await parallel(
|
|
144
|
+
* urls.map(url => () => fetch(url)),
|
|
145
|
+
* { concurrency: 2 }
|
|
146
|
+
* )
|
|
147
|
+
*/
|
|
148
|
+
declare function parallel<T>(tasks: readonly (() => Promise<T>)[], options?: {
|
|
149
|
+
concurrency?: number;
|
|
150
|
+
}): Promise<T[]>;
|
|
151
|
+
|
|
152
|
+
export { type Deferred, type RetryOptions, awaitTo, debounce, defer, once, onceAsync, parallel, retry, sleep, throttle, withTimeout };
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wraps a promise to return a tuple of [error, result].
|
|
3
|
+
* Eliminates the need for try/catch blocks.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* const [error, user] = await awaitTo(fetchUser(id))
|
|
7
|
+
* if (error) {
|
|
8
|
+
* console.error('Failed to fetch user:', error)
|
|
9
|
+
* return
|
|
10
|
+
* }
|
|
11
|
+
* console.log(user.name)
|
|
12
|
+
*/
|
|
13
|
+
declare function awaitTo<T, E = Error>(promise: Promise<T>): Promise<[E, null] | [null, T]>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Returns a promise that resolves after the specified milliseconds.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* await sleep(1000) // wait 1 second
|
|
20
|
+
* console.log('Done waiting')
|
|
21
|
+
*/
|
|
22
|
+
declare function sleep(ms: number): Promise<void>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Wraps a promise with a timeout. Rejects if the promise doesn't resolve in time.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* const result = await withTimeout(fetchData(), 5000)
|
|
29
|
+
* // Throws if fetchData takes more than 5 seconds
|
|
30
|
+
*/
|
|
31
|
+
declare function withTimeout<T>(promise: Promise<T>, ms: number, message?: string): Promise<T>;
|
|
32
|
+
|
|
33
|
+
interface RetryOptions {
|
|
34
|
+
/** Maximum number of attempts (default: 3) */
|
|
35
|
+
attempts?: number;
|
|
36
|
+
/** Delay between attempts in ms (default: 1000) */
|
|
37
|
+
delay?: number;
|
|
38
|
+
/** Multiply delay by this factor after each attempt (default: 1) */
|
|
39
|
+
backoff?: number;
|
|
40
|
+
/** Called when an attempt fails */
|
|
41
|
+
onRetry?: (error: Error, attempt: number) => void;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Retries a function until it succeeds or max attempts is reached.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* const data = await retry(() => fetchData(), {
|
|
48
|
+
* attempts: 3,
|
|
49
|
+
* delay: 1000,
|
|
50
|
+
* backoff: 2,
|
|
51
|
+
* })
|
|
52
|
+
*/
|
|
53
|
+
declare function retry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Creates a debounced version of a function.
|
|
57
|
+
* The function will only be called after it stops being called for the specified delay.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* const debouncedSearch = debounce((query: string) => {
|
|
61
|
+
* console.log('Searching:', query)
|
|
62
|
+
* }, 300)
|
|
63
|
+
*
|
|
64
|
+
* debouncedSearch('h')
|
|
65
|
+
* debouncedSearch('he')
|
|
66
|
+
* debouncedSearch('hello') // Only this one executes after 300ms
|
|
67
|
+
*/
|
|
68
|
+
declare function debounce<T extends (...args: Parameters<T>) => void>(fn: T, delay: number): ((...args: Parameters<T>) => void) & {
|
|
69
|
+
cancel: () => void;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Creates a throttled version of a function.
|
|
74
|
+
* The function will only be called at most once per specified interval.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* const throttledScroll = throttle(() => {
|
|
78
|
+
* console.log('Scroll event')
|
|
79
|
+
* }, 100)
|
|
80
|
+
*
|
|
81
|
+
* window.addEventListener('scroll', throttledScroll)
|
|
82
|
+
*/
|
|
83
|
+
declare function throttle<T extends (...args: Parameters<T>) => void>(fn: T, interval: number): ((...args: Parameters<T>) => void) & {
|
|
84
|
+
cancel: () => void;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Creates a function that can only be called once.
|
|
89
|
+
* Subsequent calls return the result of the first call.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* const initialize = once(() => {
|
|
93
|
+
* console.log('Initializing...')
|
|
94
|
+
* return { ready: true }
|
|
95
|
+
* })
|
|
96
|
+
*
|
|
97
|
+
* initialize() // logs 'Initializing...', returns { ready: true }
|
|
98
|
+
* initialize() // returns { ready: true } without logging
|
|
99
|
+
*/
|
|
100
|
+
declare function once<T extends (...args: Parameters<T>) => ReturnType<T>>(fn: T): T;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Creates an async function that can only be called once.
|
|
104
|
+
* Subsequent calls return the same promise.
|
|
105
|
+
* Useful for initialization that should only happen once.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* const loadConfig = onceAsync(async () => {
|
|
109
|
+
* const response = await fetch('/config')
|
|
110
|
+
* return response.json()
|
|
111
|
+
* })
|
|
112
|
+
*
|
|
113
|
+
* // Both calls return the same promise
|
|
114
|
+
* const config1 = await loadConfig()
|
|
115
|
+
* const config2 = await loadConfig()
|
|
116
|
+
*/
|
|
117
|
+
declare function onceAsync<T>(fn: () => Promise<T>): () => Promise<T>;
|
|
118
|
+
|
|
119
|
+
interface Deferred<T> {
|
|
120
|
+
promise: Promise<T>;
|
|
121
|
+
resolve: (value: T | PromiseLike<T>) => void;
|
|
122
|
+
reject: (reason?: unknown) => void;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Creates a deferred promise that can be resolved or rejected externally.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* const deferred = defer<string>()
|
|
129
|
+
*
|
|
130
|
+
* setTimeout(() => {
|
|
131
|
+
* deferred.resolve('Done!')
|
|
132
|
+
* }, 1000)
|
|
133
|
+
*
|
|
134
|
+
* const result = await deferred.promise
|
|
135
|
+
*/
|
|
136
|
+
declare function defer<T>(): Deferred<T>;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Runs async functions in parallel with a concurrency limit.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* const urls = ['url1', 'url2', 'url3', 'url4', 'url5']
|
|
143
|
+
* const results = await parallel(
|
|
144
|
+
* urls.map(url => () => fetch(url)),
|
|
145
|
+
* { concurrency: 2 }
|
|
146
|
+
* )
|
|
147
|
+
*/
|
|
148
|
+
declare function parallel<T>(tasks: readonly (() => Promise<T>)[], options?: {
|
|
149
|
+
concurrency?: number;
|
|
150
|
+
}): Promise<T[]>;
|
|
151
|
+
|
|
152
|
+
export { type Deferred, type RetryOptions, awaitTo, debounce, defer, once, onceAsync, parallel, retry, sleep, throttle, withTimeout };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkGS3PP67B_js = require('../chunk-GS3PP67B.js');
|
|
4
|
+
require('../chunk-PZ5AY32C.js');
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Object.defineProperty(exports, "awaitTo", {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
get: function () { return chunkGS3PP67B_js.awaitTo; }
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(exports, "debounce", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () { return chunkGS3PP67B_js.debounce; }
|
|
15
|
+
});
|
|
16
|
+
Object.defineProperty(exports, "defer", {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
get: function () { return chunkGS3PP67B_js.defer; }
|
|
19
|
+
});
|
|
20
|
+
Object.defineProperty(exports, "once", {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
get: function () { return chunkGS3PP67B_js.once; }
|
|
23
|
+
});
|
|
24
|
+
Object.defineProperty(exports, "onceAsync", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
get: function () { return chunkGS3PP67B_js.onceAsync; }
|
|
27
|
+
});
|
|
28
|
+
Object.defineProperty(exports, "parallel", {
|
|
29
|
+
enumerable: true,
|
|
30
|
+
get: function () { return chunkGS3PP67B_js.parallel; }
|
|
31
|
+
});
|
|
32
|
+
Object.defineProperty(exports, "retry", {
|
|
33
|
+
enumerable: true,
|
|
34
|
+
get: function () { return chunkGS3PP67B_js.retry; }
|
|
35
|
+
});
|
|
36
|
+
Object.defineProperty(exports, "sleep", {
|
|
37
|
+
enumerable: true,
|
|
38
|
+
get: function () { return chunkGS3PP67B_js.sleep; }
|
|
39
|
+
});
|
|
40
|
+
Object.defineProperty(exports, "throttle", {
|
|
41
|
+
enumerable: true,
|
|
42
|
+
get: function () { return chunkGS3PP67B_js.throttle; }
|
|
43
|
+
});
|
|
44
|
+
Object.defineProperty(exports, "withTimeout", {
|
|
45
|
+
enumerable: true,
|
|
46
|
+
get: function () { return chunkGS3PP67B_js.withTimeout; }
|
|
47
|
+
});
|
|
48
|
+
//# sourceMappingURL=index.js.map
|
|
49
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|