@just-io/utils 2.0.0 → 2.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.
Files changed (2) hide show
  1. package/README.md +239 -6
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,12 +1,245 @@
1
- Utils
2
- =====
1
+ @just-io/utils
2
+ ==============
3
3
 
4
- This package contains some utilities.
4
+ Lightweight TypeScript utility functions.
5
5
 
6
6
  ## Installation
7
7
 
8
- This is a Typescript module so you need to use Typescript.
8
+ ```bash
9
+ npm install @just-io/utils
10
+ ```
9
11
 
10
- ## Documentation
12
+ ## Utilities
11
13
 
12
- See tests.
14
+ ### debounce / debounceByKey
15
+
16
+ Debounce function calls with optional key-based grouping.
17
+
18
+ ```typescript
19
+ import { debounce, debounceByKey } from '@just-io/utils';
20
+
21
+ // Basic debounce - executes after delay with last arguments
22
+ function setFilterValue(values: string[]): void {
23
+ console.log('Filter:', values);
24
+ }
25
+
26
+ const debouncedSetFilterValue = debounce(setFilterValue, 1000);
27
+
28
+ debouncedSetFilterValue(['first']);
29
+ // wait 300ms...
30
+ debouncedSetFilterValue(['second']); // Only ['second'] executes after 1000ms
31
+
32
+ // Cancel pending execution
33
+ debouncedSetFilterValue.teardown();
34
+
35
+ // Key-based debounce - separate timers per extracted key
36
+ function notify(event: { type: string; at: number }): void {
37
+ console.log(event.type, event.at);
38
+ }
39
+
40
+ const debouncedNotify = debounceByKey(notify, (event) => event.type, 1000);
41
+
42
+ debouncedNotify({ type: 'change', at: 0 });
43
+ debouncedNotify({ type: 'update', at: 0 }); // Both execute - different keys
44
+ debouncedNotify({ type: 'change', at: 10 }); // Replaces first 'change', only at:10 executes
45
+
46
+ // Cancel by key or all
47
+ debouncedNotify.teardown({ type: 'change', at: 0 });
48
+ debouncedNotify.teardownAll();
49
+ ```
50
+
51
+ ### memo / memoByKey
52
+
53
+ Memoize function calls - skip execution if arguments haven't changed.
54
+
55
+ ```typescript
56
+ import { memo, memoByKey } from '@just-io/utils';
57
+
58
+ // Basic memo - only calls when extracted values change
59
+ function notify(event: { type: string; at: number }): void {
60
+ console.log(event.type, event.at);
61
+ }
62
+
63
+ const memoNotify = memo(notify, (event) => [event.type, event.at]);
64
+
65
+ memoNotify({ type: 'change', at: 0 }); // Executes
66
+ memoNotify({ type: 'change', at: 0 }); // Skipped - same values
67
+ memoNotify({ type: 'change', at: 1 }); // Executes - 'at' changed
68
+
69
+ // Only trigger on changes (skip first call)
70
+ function setFilterValue(values: string[]): void {
71
+ console.log('Filters changed:', values);
72
+ }
73
+
74
+ const memoSetFilterValue = memo(setFilterValue, (values) => values, true);
75
+
76
+ memoSetFilterValue([]); // Skipped (initial call)
77
+ memoSetFilterValue(['first']); // Executes (values changed)
78
+
79
+ // Key-based memo - separate memo state per key
80
+ function notifyChannel(channel: string, event: { type: string; at: number }): void {
81
+ console.log(channel, event);
82
+ }
83
+
84
+ const memoNotifyChannel = memoByKey(
85
+ notifyChannel,
86
+ (channel) => channel,
87
+ (channel, event) => [event.type, event.at],
88
+ );
89
+
90
+ memoNotifyChannel('channel1', { type: 'change', at: 0 }); // Executes
91
+ memoNotifyChannel('channel2', { type: 'change', at: 0 }); // Executes (different key)
92
+ memoNotifyChannel('channel1', { type: 'change', at: 0 }); // Skipped
93
+ memoNotifyChannel('channel1', { type: 'change', at: 10 }); // Executes (values changed)
94
+
95
+ // Key-based with onlyChanges
96
+ function setFilter(filter: string, values: string[]): void {
97
+ console.log(filter, values);
98
+ }
99
+
100
+ const memoSetFilter = memoByKey(
101
+ setFilter,
102
+ (filter) => filter,
103
+ (filter, values) => values,
104
+ true,
105
+ );
106
+
107
+ memoSetFilter('filter1', []); // Skipped (initial)
108
+ memoSetFilter('filter1', ['1']); // Executes (changed)
109
+ ```
110
+
111
+ ### DeepMap
112
+
113
+ Map with array keys (nested key paths). Useful for hierarchical data.
114
+
115
+ ```typescript
116
+ import { DeepMap } from '@just-io/utils';
117
+
118
+ // Create empty or with initial entries
119
+ const deepMap = new DeepMap<string, string>();
120
+ const withEntries = new DeepMap<string, string>([
121
+ [['one'], 'str'],
122
+ [['one', 'two'], 'str'],
123
+ ]);
124
+
125
+ // Copy from another DeepMap
126
+ const copy = new DeepMap(withEntries);
127
+
128
+ // Set and get with array keys
129
+ deepMap.set([], 'root'); // Empty key path
130
+ deepMap.set(['one'], 'first');
131
+ deepMap.set(['one', 'two'], 'nested');
132
+
133
+ deepMap.get(['one']); // 'first'
134
+ deepMap.get(['one', 'two']); // 'nested'
135
+ deepMap.get(['missing']); // undefined
136
+
137
+ // Check existence
138
+ deepMap.has(['one']); // true
139
+ deepMap.has(['missing']); // false
140
+
141
+ // Delete entries
142
+ deepMap.delete(['one', 'two']); // returns true
143
+ deepMap.delete(['missing']); // returns false
144
+
145
+ // Take: get and delete in one operation
146
+ const value = deepMap.take(['one']); // 'first', now deleted
147
+ deepMap.take(['missing']); // undefined
148
+
149
+ // Size and clear
150
+ deepMap.size; // number of entries
151
+ deepMap.clear(); // remove all
152
+
153
+ // Iteration
154
+ deepMap.forEach((value, key) => console.log(key, value));
155
+
156
+ for (const [key, value] of deepMap) {
157
+ console.log(key, value);
158
+ }
159
+
160
+ Array.from(deepMap.entries()); // [[key, value], ...]
161
+ Array.from(deepMap.keys()); // [key, ...]
162
+ Array.from(deepMap.values()); // [value, ...]
163
+
164
+ // Clone subtree (non-destructive)
165
+ deepMap.set(['one'], 'str');
166
+ deepMap.set(['one', 'two'], 'str');
167
+ const cloned = deepMap.clone(['one']);
168
+ // cloned: [[], 'str'], [['two'], 'str']
169
+
170
+ // Extract subtree (removes from original)
171
+ const extracted = deepMap.extract(['one']);
172
+ // extracted has the subtree, deepMap no longer has it
173
+
174
+ // Append entries under a prefix
175
+ deepMap.append(['one'], [[['two'], 'str']]);
176
+ // Result: [['one', 'two'], 'str']
177
+ ```
178
+
179
+ ### EventEmitter / Notifier
180
+
181
+ Type-safe event handling.
182
+
183
+ ```typescript
184
+ import { EventEmitter, EventTuple, Notifier } from '@just-io/utils';
185
+
186
+ // Notifier - single event type
187
+ type Event = [first: string, second: string];
188
+
189
+ const notifier = new Notifier<Event>();
190
+
191
+ const handler = (first: string, second: string) => console.log(first, second);
192
+ notifier.subscribe(handler);
193
+ notifier.notify('a', 'b'); // logs: a b
194
+
195
+ // One-time subscription
196
+ notifier.subscribe((a, b) => console.log('once:', a, b), { once: true });
197
+ notifier.notify('x', 'y'); // Both handlers called
198
+ notifier.notify('x', 'y'); // Only permanent handler
199
+
200
+ notifier.unsubscribe(handler); // Remove specific
201
+ notifier.unsubscribeAll(); // Remove all
202
+
203
+ // EventEmitter - multiple named events
204
+ type EventMap = {
205
+ one: [first: number];
206
+ two: [first: string, second: number];
207
+ };
208
+
209
+ const emitter = new EventEmitter<EventMap>();
210
+
211
+ emitter.on('one', (first) => console.log('one:', first));
212
+ emitter.on('two', (first, second) => console.log('two:', first, second));
213
+
214
+ emitter.emit('one', 1);
215
+ emitter.emit('two', '1', 2);
216
+
217
+ // One-time listener
218
+ emitter.once('one', (first) => console.log('once:', first));
219
+
220
+ // Emit from tuple (useful for batching)
221
+ type ETuple = EventTuple<EventMap>;
222
+ const events: ETuple[] = [
223
+ ['one', 1],
224
+ ['two', '2', 1],
225
+ ];
226
+ for (const event of events) {
227
+ emitter.emit(...event);
228
+ }
229
+
230
+ // Unsubscribe
231
+ emitter.off('one', handler);
232
+ emitter.unsubscribeAll('one'); // Remove all 'one' handlers
233
+ emitter.unsubscribeAll(); // Remove all handlers
234
+
235
+ // Event store - batch events and emit later
236
+ const store = emitter.makeStore();
237
+ store.add('one', 1);
238
+ store.add('one', 2);
239
+ store.emit(); // Emits both events
240
+ store.emit(); // Does nothing (already emitted)
241
+ ```
242
+
243
+ ## License
244
+
245
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@just-io/utils",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Utility functional",
5
5
  "types": "dist/types",
6
6
  "main": "dist/cjs/index.js",