@furystack/utils 8.1.6 → 8.1.8

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/CHANGELOG.md CHANGED
@@ -1,42 +1,15 @@
1
- # Change Log
1
+ # Changelog
2
2
 
3
- All notable changes to this project will be documented in this file.
4
- See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
3
+ ## [8.1.8] - 2026-01-22
5
4
 
6
- ### [2.0.3](https://github.com/furystack/furystack/compare/@furystack/utils@2.0.2...@furystack/utils@2.0.3) (2022-02-16)
5
+ ### ⬆️ Dependencies
7
6
 
8
- **Note:** Version bump only for package @furystack/utils
7
+ - Dependency updates
9
8
 
10
- ### [2.0.2](https://github.com/furystack/furystack/compare/@furystack/utils@2.0.0...@furystack/utils@2.0.2) (2022-01-10)
9
+ ### 📚 Documentation
11
10
 
12
- **Note:** Version bump only for package @furystack/utils
11
+ - Revamped README with improved utility documentation and examples
13
12
 
14
- ### [2.0.1](https://github.com/furystack/furystack/compare/@furystack/utils@2.0.0...@furystack/utils@2.0.1) (2022-01-10)
13
+ ### 🔧 Chores
15
14
 
16
- **Note:** Version bump only for package @furystack/utils
17
-
18
- ## [2.0.0](https://github.com/furystack/furystack/compare/@furystack/utils@1.2.46...@furystack/utils@2.0.0) (2021-12-08)
19
-
20
- ### ⚠ BREAKING CHANGES
21
-
22
- - **@furystack/shades:** Disposable resources on Shade Components (#165)
23
-
24
- ### 🚀 What's new
25
-
26
- - **@furystack/shades:** Disposable resources on Shade Components ([#165](https://github.com/furystack/furystack/issues/165)) ([8567e7e](https://github.com/furystack/furystack/commit/8567e7e2e01cec232a5f4448dfc0833c1f183229))
27
-
28
- ### [1.2.46](https://github.com/furystack/furystack/compare/@furystack/utils@1.2.45...@furystack/utils@1.2.46) (2021-11-19)
29
-
30
- **Note:** Version bump only for package @furystack/utils
31
-
32
- ### [1.2.45](https://github.com/furystack/furystack/compare/@furystack/utils@1.2.44...@furystack/utils@1.2.45) (2021-10-05)
33
-
34
- **Note:** Version bump only for package @furystack/utils
35
-
36
- ### [1.2.44](https://github.com/furystack/furystack/compare/@furystack/utils@1.2.14...@furystack/utils@1.2.44) (2021-08-19)
37
-
38
- **Note:** Version bump only for package @furystack/utils
39
-
40
- ### [1.2.43](https://github.com/furystack/furystack/compare/@furystack/utils@1.2.14...@furystack/utils@1.2.43) (2021-07-30)
41
-
42
- **Note:** Version bump only for package @furystack/utils
15
+ - Migrated to centralized changelog management system
package/README.md CHANGED
@@ -2,13 +2,23 @@
2
2
 
3
3
  General utilities for FuryStack.
4
4
 
5
- ### Disposable
5
+ ## Installation
6
6
 
7
- You can implement disposable resources and use them with `using()` or `usingAsync()` syntax. Example:
7
+ ```bash
8
+ npm install @furystack/utils
9
+ # or
10
+ yarn add @furystack/utils
11
+ ```
12
+
13
+ ## Disposable
14
+
15
+ You can implement disposable resources and use them with `using()` or `usingAsync()` syntax:
8
16
 
9
17
  ```ts
10
- class Resource implements IDisposable {
11
- dispose() {
18
+ import { using, usingAsync } from '@furystack/utils'
19
+
20
+ class Resource implements Disposable {
21
+ [Symbol.dispose]() {
12
22
  // cleanup logic
13
23
  }
14
24
  }
@@ -17,18 +27,18 @@ using(new Resource(), (resource) => {
17
27
  // do something with the resource
18
28
  })
19
29
 
20
- usingAsync(new Resource(), async (resource) => {
30
+ await usingAsync(new Resource(), async (resource) => {
21
31
  // do something with the resource, allows awaiting promises
22
32
  })
23
33
  ```
24
34
 
25
- ### ObservableValue and ValueObservers
26
-
27
- You can track value changes using this simple Observable implementation.
35
+ ## ObservableValue
28
36
 
29
- Example:
37
+ You can track value changes using this simple Observable implementation:
30
38
 
31
39
  ```ts
40
+ import { ObservableValue } from '@furystack/utils'
41
+
32
42
  const observableValue = new ObservableValue<number>(0)
33
43
  const observer = observableValue.subscribe((newValue) => {
34
44
  console.log('Value changed:', newValue)
@@ -42,51 +52,157 @@ observer[Symbol.dispose]()
42
52
  observableValue[Symbol.dispose]()
43
53
  ```
44
54
 
45
- ### PathHelper
55
+ ## PathHelper
56
+
57
+ Helper class for path-related functions and methods:
58
+
59
+ ```ts
60
+ import { PathHelper } from '@furystack/utils'
61
+
62
+ PathHelper.joinPaths('api', 'users', '123') // 'api/users/123'
63
+ PathHelper.getSegments('/api/users/123') // ['api', 'users', '123']
64
+ PathHelper.getParentPath('/api/users/123') // 'api/users'
65
+ PathHelper.trimSlashes('/api/') // 'api'
66
+ PathHelper.joinUrl('http://example.com', '/api') // 'http://example.com/api'
67
+ ```
68
+
69
+ ## deepMerge
46
70
 
47
- This class contains helper methods for path transformation and manipulation.
71
+ Deep merge objects together:
48
72
 
49
- ### Retrier
73
+ ```ts
74
+ import { deepMerge } from '@furystack/utils'
50
75
 
51
- Retrier is a utility that can keep trying an operation until it succeeds, times out or reach a specified retry limit.
76
+ const target = { a: 1, b: { c: 2 } }
77
+ const source = { b: { d: 3 } }
78
+ const result = deepMerge(target, source) // { a: 1, b: { c: 2, d: 3 } }
79
+ ```
80
+
81
+ ## sleepAsync
82
+
83
+ Simple promise-based sleep utility:
52
84
 
53
85
  ```ts
54
- const funcToRetry: () => Promise<boolean> = async () => {
55
- const hasSucceeded = false
56
- // ...
57
- // custom logic
58
- // ...
59
- return hasSucceeded
60
- }
61
- const retrierSuccess = await Retrier.create(funcToRetry)
62
- .setup({
63
- Retries: 3,
64
- RetryIntervalMs: 1,
65
- timeoutMs: 1000,
66
- })
67
- .run()
86
+ import { sleepAsync } from '@furystack/utils'
87
+
88
+ await sleepAsync(1000) // wait 1 second
68
89
  ```
69
90
 
70
- ### Trace
91
+ ## debounce
71
92
 
72
- Trace is an utility that can be used to track method calls, method returns and errors
93
+ Debounce a function to limit how often it can be called:
73
94
 
74
95
  ```ts
75
- const methodTracer: IDisposable = Trace.method({
76
- object: myObjectInstance, // You can define an object constructor for static methods as well
77
- method: myObjectInstance.method, // The method to be tracked
78
- isAsync: true, // if you set to async, method finished will be *await*-ed
79
- onCalled: (traceData) => {
80
- console.log('Method called:', traceData)
81
- },
82
- onFinished: (traceData) => {
83
- console.log('Method call finished:', traceData)
84
- },
85
- onError: (traceData) => {
86
- console.log('Method throwed an error:', traceData)
87
- },
96
+ import { debounce } from '@furystack/utils'
97
+
98
+ const debouncedSearch = debounce((query: string) => {
99
+ console.log('Searching for:', query)
100
+ }, 300)
101
+
102
+ debouncedSearch('hello') // Will only execute after 300ms of no calls
103
+ ```
104
+
105
+ ## EventHub
106
+
107
+ A typed event emitter:
108
+
109
+ ```ts
110
+ import { EventHub } from '@furystack/utils'
111
+
112
+ type MyEvents = {
113
+ userLoggedIn: { userId: string }
114
+ userLoggedOut: { userId: string }
115
+ }
116
+
117
+ const hub = new EventHub<MyEvents>()
118
+
119
+ hub.subscribe('userLoggedIn', (event) => {
120
+ console.log('User logged in:', event.userId)
88
121
  })
89
122
 
90
- // if you want to stop receiving events
91
- methodTracer[Symbol.dispose]()
123
+ hub.emit('userLoggedIn', { userId: '123' })
124
+ ```
125
+
126
+ ## sortBy
127
+
128
+ Array prototype extension for convenient sorting by field name:
129
+
130
+ ```ts
131
+ import '@furystack/utils' // Adds sortBy to Array prototype
132
+
133
+ const users = [
134
+ { name: 'Charlie', age: 30 },
135
+ { name: 'Alice', age: 25 },
136
+ { name: 'Bob', age: 35 },
137
+ ]
138
+
139
+ // Sort by name ascending (default)
140
+ const byName = users.sortBy('name')
141
+ // [{ name: 'Alice', ... }, { name: 'Bob', ... }, { name: 'Charlie', ... }]
142
+
143
+ // Sort by age descending
144
+ const byAgeDesc = users.sortBy('age', 'desc')
145
+ // [{ name: 'Bob', age: 35 }, { name: 'Charlie', age: 30 }, { name: 'Alice', age: 25 }]
146
+ ```
147
+
148
+ You can also use the `compareBy` function directly for custom sorting:
149
+
150
+ ```ts
151
+ import { compareBy } from '@furystack/utils'
152
+
153
+ const users = [{ name: 'Charlie' }, { name: 'Alice' }]
154
+ users.sort((a, b) => compareBy(a, b, 'name', 'asc'))
155
+ ```
156
+
157
+ ## tuple
158
+
159
+ Helper function for creating typed tuples from string literals:
160
+
161
+ ```ts
162
+ import { tuple } from '@furystack/utils'
163
+
164
+ // Creates a tuple type ['admin', 'user', 'guest'] instead of string[]
165
+ const roles = tuple('admin', 'user', 'guest')
166
+
167
+ // TypeScript knows the exact values
168
+ type Role = (typeof roles)[number] // 'admin' | 'user' | 'guest'
169
+ ```
170
+
171
+ ## Type Guards
172
+
173
+ Check if a value is disposable:
174
+
175
+ ```ts
176
+ import { isDisposable, isAsyncDisposable } from '@furystack/utils'
177
+
178
+ if (isDisposable(value)) {
179
+ value[Symbol.dispose]()
180
+ }
181
+
182
+ if (isAsyncDisposable(value)) {
183
+ await value[Symbol.asyncDispose]()
184
+ }
185
+ ```
186
+
187
+ ## DeepPartial
188
+
189
+ A utility type for deep partial objects:
190
+
191
+ ```ts
192
+ import type { DeepPartial } from '@furystack/utils'
193
+
194
+ type Config = {
195
+ server: {
196
+ host: string
197
+ port: number
198
+ }
199
+ database: {
200
+ url: string
201
+ }
202
+ }
203
+
204
+ // All nested properties are optional
205
+ const partialConfig: DeepPartial<Config> = {
206
+ server: { port: 8080 }, // host is optional
207
+ }
92
208
  ```
@@ -1,4 +1,31 @@
1
1
  type ListenerFunction<EventTypeMap extends object, T extends keyof EventTypeMap> = (arg: EventTypeMap[T]) => void | PromiseLike<void>;
2
+ /**
3
+ * A typed event emitter that provides type-safe event subscription and emission.
4
+ * Use this to create strongly-typed pub/sub patterns in your application.
5
+ *
6
+ * @typeParam EventTypeMap - An object type where keys are event names and values are event payload types
7
+ * @example
8
+ * ```ts
9
+ * type MyEvents = {
10
+ * userLoggedIn: { userId: string }
11
+ * userLoggedOut: { userId: string }
12
+ * dataUpdated: { items: string[] }
13
+ * }
14
+ *
15
+ * const hub = new EventHub<MyEvents>()
16
+ *
17
+ * // Subscribe to events
18
+ * hub.subscribe('userLoggedIn', (event) => {
19
+ * console.log('User logged in:', event.userId)
20
+ * })
21
+ *
22
+ * // Emit events
23
+ * hub.emit('userLoggedIn', { userId: '123' })
24
+ *
25
+ * // Clean up when done
26
+ * hub[Symbol.dispose]()
27
+ * ```
28
+ */
2
29
  export declare class EventHub<EventTypeMap extends object> implements Disposable {
3
30
  private listeners;
4
31
  addListener<TEvent extends keyof EventTypeMap>(event: TEvent, listener: ListenerFunction<EventTypeMap, TEvent>): void;
@@ -1 +1 @@
1
- {"version":3,"file":"event-hub.d.ts","sourceRoot":"","sources":["../src/event-hub.ts"],"names":[],"mappings":"AAAA,KAAK,gBAAgB,CAAC,YAAY,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,YAAY,IAAI,CACjF,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,KACjB,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAA;AAE7B,qBAAa,QAAQ,CAAC,YAAY,SAAS,MAAM,CAAE,YAAW,UAAU;IACtE,OAAO,CAAC,SAAS,CAAyF;IAEnG,WAAW,CAAC,MAAM,SAAS,MAAM,YAAY,EAClD,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,gBAAgB,CAAC,YAAY,EAAE,MAAM,CAAC;IAQ3C,cAAc,CAAC,MAAM,SAAS,MAAM,YAAY,EACrD,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,gBAAgB,CAAC,YAAY,EAAE,MAAM,CAAC;IAO3C,SAAS,CAAC,MAAM,SAAS,MAAM,YAAY,EAChD,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,gBAAgB,CAAC,YAAY,EAAE,MAAM,CAAC,GAC/C,UAAU;IAKN,IAAI,CAAC,MAAM,SAAS,MAAM,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,CAAC,MAAM,CAAC;IAMhF,CAAC,MAAM,CAAC,OAAO,CAAC;CAGxB"}
1
+ {"version":3,"file":"event-hub.d.ts","sourceRoot":"","sources":["../src/event-hub.ts"],"names":[],"mappings":"AAAA,KAAK,gBAAgB,CAAC,YAAY,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,YAAY,IAAI,CACjF,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,KACjB,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAA;AAE7B;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,QAAQ,CAAC,YAAY,SAAS,MAAM,CAAE,YAAW,UAAU;IACtE,OAAO,CAAC,SAAS,CAAyF;IAEnG,WAAW,CAAC,MAAM,SAAS,MAAM,YAAY,EAClD,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,gBAAgB,CAAC,YAAY,EAAE,MAAM,CAAC;IAQ3C,cAAc,CAAC,MAAM,SAAS,MAAM,YAAY,EACrD,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,gBAAgB,CAAC,YAAY,EAAE,MAAM,CAAC;IAO3C,SAAS,CAAC,MAAM,SAAS,MAAM,YAAY,EAChD,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,gBAAgB,CAAC,YAAY,EAAE,MAAM,CAAC,GAC/C,UAAU;IAKN,IAAI,CAAC,MAAM,SAAS,MAAM,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,CAAC,MAAM,CAAC;IAMhF,CAAC,MAAM,CAAC,OAAO,CAAC;CAGxB"}
package/esm/event-hub.js CHANGED
@@ -1,3 +1,30 @@
1
+ /**
2
+ * A typed event emitter that provides type-safe event subscription and emission.
3
+ * Use this to create strongly-typed pub/sub patterns in your application.
4
+ *
5
+ * @typeParam EventTypeMap - An object type where keys are event names and values are event payload types
6
+ * @example
7
+ * ```ts
8
+ * type MyEvents = {
9
+ * userLoggedIn: { userId: string }
10
+ * userLoggedOut: { userId: string }
11
+ * dataUpdated: { items: string[] }
12
+ * }
13
+ *
14
+ * const hub = new EventHub<MyEvents>()
15
+ *
16
+ * // Subscribe to events
17
+ * hub.subscribe('userLoggedIn', (event) => {
18
+ * console.log('User logged in:', event.userId)
19
+ * })
20
+ *
21
+ * // Emit events
22
+ * hub.emit('userLoggedIn', { userId: '123' })
23
+ *
24
+ * // Clean up when done
25
+ * hub[Symbol.dispose]()
26
+ * ```
27
+ */
1
28
  export class EventHub {
2
29
  listeners = new Map();
3
30
  addListener(event, listener) {
@@ -1 +1 @@
1
- {"version":3,"file":"event-hub.js","sourceRoot":"","sources":["../src/event-hub.ts"],"names":[],"mappings":"AAIA,MAAM,OAAO,QAAQ;IACX,SAAS,GAAG,IAAI,GAAG,EAA+E,CAAA;IAEnG,WAAW,CAChB,KAAa,EACb,QAAgD;QAEhD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,EAAE,CAAC,CAAA;QACtC,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC,GAAG,CAAC,QAA8D,CAAC,CAAA;IAChG,CAAC;IAEM,cAAc,CACnB,KAAa,EACb,QAAgD;QAEhD,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC,MAAM,CAAC,QAA8D,CAAC,CAAA;QACnG,CAAC;IACH,CAAC;IAEM,SAAS,CACd,KAAa,EACb,QAAgD;QAEhD,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;QACjC,OAAO,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAA;IACzE,CAAC;IAEM,IAAI,CAAoC,KAAa,EAAE,GAAyB;QACrF,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,KAAK,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAA;QACtE,CAAC;IACH,CAAC;IAEM,CAAC,MAAM,CAAC,OAAO,CAAC;QACrB,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAA;IACxB,CAAC;CACF"}
1
+ {"version":3,"file":"event-hub.js","sourceRoot":"","sources":["../src/event-hub.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,OAAO,QAAQ;IACX,SAAS,GAAG,IAAI,GAAG,EAA+E,CAAA;IAEnG,WAAW,CAChB,KAAa,EACb,QAAgD;QAEhD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,EAAE,CAAC,CAAA;QACtC,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC,GAAG,CAAC,QAA8D,CAAC,CAAA;IAChG,CAAC;IAEM,cAAc,CACnB,KAAa,EACb,QAAgD;QAEhD,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC,MAAM,CAAC,QAA8D,CAAC,CAAA;QACnG,CAAC;IACH,CAAC;IAEM,SAAS,CACd,KAAa,EACb,QAAgD;QAEhD,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;QACjC,OAAO,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAA;IACzE,CAAC;IAEM,IAAI,CAAoC,KAAa,EAAE,GAAyB;QACrF,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,KAAK,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAA;QACtE,CAAC;IACH,CAAC;IAEM,CAAC,MAAM,CAAC,OAAO,CAAC;QACrB,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAA;IACxB,CAAC;CACF"}
package/esm/sort-by.js CHANGED
@@ -10,7 +10,6 @@ export const compareBy = (entity1, entity2, field, direction) => {
10
10
  return 0;
11
11
  };
12
12
  Array.prototype.sortBy = function (key, direction = 'asc') {
13
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
14
13
  return this.sort((a, b) => compareBy(a, b, key, direction));
15
14
  };
16
15
  //# sourceMappingURL=sort-by.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sort-by.js","sourceRoot":"","sources":["../src/sort-by.ts"],"names":[],"mappings":"AAeA,MAAM,CAAC,MAAM,SAAS,GAAG,CAAuB,OAAU,EAAE,OAAU,EAAE,KAAQ,EAAE,SAAyB,EAAE,EAAE;IAC7G,MAAM,CAAC,GAAG,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAA;IACjD,MAAM,CAAC,GAAG,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAA;IACjD,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,CAAC,CAAA;IACX,CAAC;IACD,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,CAAA;IACV,CAAC;IACD,OAAO,CAAC,CAAA;AACV,CAAC,CAAA;AAED,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,UAAU,GAAG,EAAE,SAAS,GAAG,KAAK;IACvD,+DAA+D;IAC/D,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC,CAAA;AAC7D,CAAC,CAAA"}
1
+ {"version":3,"file":"sort-by.js","sourceRoot":"","sources":["../src/sort-by.ts"],"names":[],"mappings":"AAeA,MAAM,CAAC,MAAM,SAAS,GAAG,CAAuB,OAAU,EAAE,OAAU,EAAE,KAAQ,EAAE,SAAyB,EAAE,EAAE;IAC7G,MAAM,CAAC,GAAG,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAA;IACjD,MAAM,CAAC,GAAG,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAA;IACjD,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,CAAC,CAAA;IACX,CAAC;IACD,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,CAAA;IACV,CAAC;IACD,OAAO,CAAC,CAAA;AACV,CAAC,CAAA;AAED,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,UAAgC,GAAM,EAAE,YAA4B,KAAK;IAChG,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC,CAAA;AAC7D,CAAC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@furystack/utils",
3
- "version": "8.1.6",
3
+ "version": "8.1.8",
4
4
  "description": "General utilities",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -35,6 +35,6 @@
35
35
  "homepage": "https://github.com/furystack/furystack",
36
36
  "devDependencies": {
37
37
  "typescript": "^5.9.3",
38
- "vitest": "^4.0.15"
38
+ "vitest": "^4.0.17"
39
39
  }
40
40
  }
package/src/event-hub.ts CHANGED
@@ -2,6 +2,33 @@ type ListenerFunction<EventTypeMap extends object, T extends keyof EventTypeMap>
2
2
  arg: EventTypeMap[T],
3
3
  ) => void | PromiseLike<void>
4
4
 
5
+ /**
6
+ * A typed event emitter that provides type-safe event subscription and emission.
7
+ * Use this to create strongly-typed pub/sub patterns in your application.
8
+ *
9
+ * @typeParam EventTypeMap - An object type where keys are event names and values are event payload types
10
+ * @example
11
+ * ```ts
12
+ * type MyEvents = {
13
+ * userLoggedIn: { userId: string }
14
+ * userLoggedOut: { userId: string }
15
+ * dataUpdated: { items: string[] }
16
+ * }
17
+ *
18
+ * const hub = new EventHub<MyEvents>()
19
+ *
20
+ * // Subscribe to events
21
+ * hub.subscribe('userLoggedIn', (event) => {
22
+ * console.log('User logged in:', event.userId)
23
+ * })
24
+ *
25
+ * // Emit events
26
+ * hub.emit('userLoggedIn', { userId: '123' })
27
+ *
28
+ * // Clean up when done
29
+ * hub[Symbol.dispose]()
30
+ * ```
31
+ */
5
32
  export class EventHub<EventTypeMap extends object> implements Disposable {
6
33
  private listeners = new Map<keyof EventTypeMap, Set<ListenerFunction<EventTypeMap, keyof EventTypeMap>>>()
7
34
 
package/src/sort-by.ts CHANGED
@@ -25,7 +25,6 @@ export const compareBy = <T, K extends keyof T>(entity1: T, entity2: T, field: K
25
25
  return 0
26
26
  }
27
27
 
28
- Array.prototype.sortBy = function (key, direction = 'asc') {
29
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
28
+ Array.prototype.sortBy = function <T, K extends keyof T>(key: K, direction: 'asc' | 'desc' = 'asc'): T[] {
30
29
  return this.sort((a, b) => compareBy(a, b, key, direction))
31
30
  }