@useless-engineer/utils 0.1.0 β 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/README.md +203 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,11 +1,210 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
# π οΈ @useless-engineer/utils
|
|
4
|
+
|
|
5
|
+
**Small, typed utilities distilled from real production bugs.**
|
|
6
|
+
One package. Multiple entry points. Only pay for what you import.
|
|
7
|
+
|
|
8
|
+
[](https://www.npmjs.com/package/@useless-engineer/utils)
|
|
9
|
+
[](https://www.npmjs.com/package/@useless-engineer/utils)
|
|
10
|
+
[](./LICENSE)
|
|
11
|
+
|
|
12
|
+

|
|
13
|
+

|
|
14
|
+

|
|
15
|
+

|
|
16
|
+

|
|
17
|
+
|
|
18
|
+
[**π¦ Install**](#-install) Β·
|
|
19
|
+
[**β‘ Quick start**](#-quick-start) Β·
|
|
20
|
+
[**π API**](#-api) Β·
|
|
21
|
+
[**πΊοΈ Roadmap**](#οΈ-roadmap) Β·
|
|
22
|
+
[**πΈ Follow**](https://www.instagram.com/useless_engineer.dev/)
|
|
23
|
+
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## β¨ Why this exists
|
|
29
|
+
|
|
30
|
+
Every utility here started as an [Instagram post](https://www.instagram.com/useless_engineer.dev/) about a bug that actually shipped. The post explains the problem; this package is the fix, typed and tested, so you never copy-paste it again.
|
|
31
|
+
|
|
32
|
+
| | |
|
|
33
|
+
| ------------------------ | ------------------------------------------------------ |
|
|
34
|
+
| π― **Focused** | Each function does one thing and has tests |
|
|
35
|
+
| π² **Tree-shakeable** | `sideEffects: false`, import only what you use |
|
|
36
|
+
| π§© **Split by platform** | Core, React Native, and more, each its own import path |
|
|
37
|
+
| π **Strictly typed** | Written in TypeScript with strict mode on |
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## π¦ Install
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install @useless-engineer/utils
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
<details>
|
|
48
|
+
<summary><b>pnpm / yarn / bun</b></summary>
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pnpm add @useless-engineer/utils
|
|
52
|
+
yarn add @useless-engineer/utils
|
|
53
|
+
bun add @useless-engineer/utils
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
</details>
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## β‘ Quick start
|
|
5
61
|
|
|
6
62
|
```ts
|
|
7
63
|
import { debounce, throttle } from "@useless-engineer/utils";
|
|
64
|
+
|
|
65
|
+
const onSearch = debounce((query: string) => fetchResults(query), 300);
|
|
66
|
+
const onScroll = throttle(() => updateHeader(), 100);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { isIOS, isAndroid } from "@useless-engineer/utils/react-native";
|
|
71
|
+
|
|
72
|
+
const padding = isIOS ? 20 : 16;
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
> π‘ `react` and `react-native` are **optional** peer dependencies. If you only use the core entry, you never need to install either.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## π§ Entry points
|
|
80
|
+
|
|
81
|
+
| Import path | For | Status |
|
|
82
|
+
| -------------------------------------- | ------------------------- | ----------------------------------------------------------------------------- |
|
|
83
|
+
| `@useless-engineer/utils` | Platform-agnostic helpers |  |
|
|
84
|
+
| `@useless-engineer/utils/react-native` | React Native helpers |  |
|
|
85
|
+
| `@useless-engineer/utils/react` | React hooks |  |
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## π API
|
|
90
|
+
|
|
91
|
+
### Core
|
|
92
|
+
|
|
93
|
+
<details>
|
|
94
|
+
<summary><code>debounce(fn, delay)</code>  </summary>
|
|
95
|
+
|
|
96
|
+
<br>
|
|
97
|
+
|
|
98
|
+
Waits for quiet. Every call resets the timer, and only the last call fires. Good for search inputs and autosave.
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
const save = debounce((text: string) => api.save(text), 500);
|
|
102
|
+
|
|
103
|
+
save("h");
|
|
104
|
+
save("he");
|
|
105
|
+
save("hello"); // only this one runs, 500ms after the last call
|
|
106
|
+
|
|
107
|
+
// In a React effect cleanup, so nothing fires after unmount:
|
|
108
|
+
save.cancel();
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
| Param | Type | Description |
|
|
112
|
+
| ----------- | ------------------- | ----------------------------------- |
|
|
113
|
+
| `fn` | `(...args) => void` | Function to debounce |
|
|
114
|
+
| `delay` | `number` | Milliseconds of quiet before firing |
|
|
115
|
+
| **returns** | `Debounced` | Callable with a `.cancel()` method |
|
|
116
|
+
|
|
117
|
+
</details>
|
|
118
|
+
|
|
119
|
+
<details>
|
|
120
|
+
<summary><code>throttle(fn, interval, options?)</code>  </summary>
|
|
121
|
+
|
|
122
|
+
<br>
|
|
123
|
+
|
|
124
|
+
Keeps a steady beat. Runs at most once per interval, however often it's called. Good for scroll, drag, and resize.
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
const onScroll = throttle(() => updateHeader(), 100);
|
|
128
|
+
|
|
129
|
+
// Skip the immediate first call:
|
|
130
|
+
throttle(fn, 100, { leading: false });
|
|
131
|
+
|
|
132
|
+
// Skip the final catch-up call:
|
|
133
|
+
throttle(fn, 100, { trailing: false });
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
| Option | Default | Description |
|
|
137
|
+
| ---------- | ------- | --------------------------------------------------- |
|
|
138
|
+
| `leading` | `true` | Fire right away on the first call of a burst |
|
|
139
|
+
| `trailing` | `true` | Fire once more at the end with the latest arguments |
|
|
140
|
+
|
|
141
|
+
Returns a function with `.cancel()`.
|
|
142
|
+
|
|
143
|
+
</details>
|
|
144
|
+
|
|
145
|
+
<details>
|
|
146
|
+
<summary><b>debounce or throttle?</b></summary>
|
|
147
|
+
|
|
148
|
+
<br>
|
|
149
|
+
|
|
150
|
+
| | `debounce` | `throttle` |
|
|
151
|
+
| ------------ | --------------------------------- | -------------------------------- |
|
|
152
|
+
| Fires | Once, after calls **stop** | On a fixed beat **during** calls |
|
|
153
|
+
| Use for | Search box, autosave, validation | Scroll, drag, resize |
|
|
154
|
+
| Ask yourself | "Do I only need the final state?" | "Does it need to stay live?" |
|
|
155
|
+
|
|
156
|
+
</details>
|
|
157
|
+
|
|
158
|
+
### React Native
|
|
159
|
+
|
|
160
|
+
<details>
|
|
161
|
+
<summary><code>isIOS</code> / <code>isAndroid</code>  </summary>
|
|
162
|
+
|
|
163
|
+
<br>
|
|
164
|
+
|
|
165
|
+
Plain booleans derived from `Platform.OS`.
|
|
166
|
+
|
|
167
|
+
```ts
|
|
8
168
|
import { isIOS, isAndroid } from "@useless-engineer/utils/react-native";
|
|
169
|
+
|
|
170
|
+
const behavior = isIOS ? "padding" : "height";
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
</details>
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## πΊοΈ Roadmap
|
|
178
|
+
|
|
179
|
+
Released one or two at a time, each matching a post that's already live.
|
|
180
|
+
|
|
181
|
+
| Next up | Entry | Origin |
|
|
182
|
+
| ------------------------------------------------------------------------------------------------------------------------ | ------------ | ----------------------------------------- |
|
|
183
|
+
|  `delay`, `withTimeout` | core | Validated timers, `Promise.race` timeouts |
|
|
184
|
+
|  `safeParseJSON`, `safeStringify`, `deepClone` | core | JSON and shallow-copy gotchas |
|
|
185
|
+
|  `getAccessibilityProps`, `createShadow` | react-native | Accessibility and iOS-only shadows |
|
|
186
|
+
|  `useInterval`, `useCountdown`, `useIsMounted` | react | Stale closures and unmounted updates |
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## π€ Contributing
|
|
191
|
+
|
|
192
|
+
Found a bug or have a post idea that deserves a utility? [Open an issue](https://github.com/<your-username>/useless-engineer/issues).
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
npm install
|
|
196
|
+
npm test
|
|
197
|
+
npm run build
|
|
9
198
|
```
|
|
10
199
|
|
|
11
|
-
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
<div align="center">
|
|
203
|
+
|
|
204
|
+
**Built from real bugs, one post at a time.**
|
|
205
|
+
|
|
206
|
+
[](https://www.instagram.com/useless_engineer.dev/)
|
|
207
|
+
|
|
208
|
+
MIT Β© Saurabh Bandkar
|
|
209
|
+
|
|
210
|
+
</div>
|