@furystack/utils 7.0.2 → 8.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 (103) hide show
  1. package/README.md +3 -3
  2. package/esm/debounce.d.ts.map +1 -1
  3. package/esm/deep-merge.d.ts.map +1 -1
  4. package/esm/deep-merge.spec.js +1 -1
  5. package/esm/deep-merge.spec.js.map +1 -1
  6. package/esm/event-hub.d.ts +1 -2
  7. package/esm/event-hub.d.ts.map +1 -1
  8. package/esm/event-hub.js +2 -2
  9. package/esm/event-hub.js.map +1 -1
  10. package/esm/event-hub.spec.js +4 -4
  11. package/esm/event-hub.spec.js.map +1 -1
  12. package/esm/index.d.ts +6 -4
  13. package/esm/index.d.ts.map +1 -1
  14. package/esm/index.js +6 -4
  15. package/esm/index.js.map +1 -1
  16. package/esm/is-async-disposable.d.ts +7 -0
  17. package/esm/is-async-disposable.d.ts.map +1 -0
  18. package/esm/is-async-disposable.js +9 -0
  19. package/esm/is-async-disposable.js.map +1 -0
  20. package/esm/is-async-disposable.spec.d.ts +2 -0
  21. package/esm/is-async-disposable.spec.d.ts.map +1 -0
  22. package/esm/is-async-disposable.spec.js +21 -0
  23. package/esm/is-async-disposable.spec.js.map +1 -0
  24. package/esm/is-disposable.d.ts +7 -0
  25. package/esm/is-disposable.d.ts.map +1 -0
  26. package/esm/is-disposable.js +9 -0
  27. package/esm/is-disposable.js.map +1 -0
  28. package/esm/is-disposable.spec.d.ts +2 -0
  29. package/esm/is-disposable.spec.d.ts.map +1 -0
  30. package/esm/is-disposable.spec.js +21 -0
  31. package/esm/is-disposable.spec.js.map +1 -0
  32. package/esm/observable-value.d.ts +4 -5
  33. package/esm/observable-value.d.ts.map +1 -1
  34. package/esm/observable-value.js +3 -3
  35. package/esm/observable-value.js.map +1 -1
  36. package/esm/observable-value.spec.d.ts.map +1 -1
  37. package/esm/observable-value.spec.js +10 -9
  38. package/esm/observable-value.spec.js.map +1 -1
  39. package/esm/path-helper.js +1 -1
  40. package/esm/path-helper.js.map +1 -1
  41. package/esm/path-helper.spec.d.ts.map +1 -1
  42. package/esm/path-helper.spec.js +9 -1
  43. package/esm/path-helper.spec.js.map +1 -1
  44. package/esm/sort-by.d.ts +1 -1
  45. package/esm/sort-by.d.ts.map +1 -1
  46. package/esm/tuple.d.ts.map +1 -1
  47. package/esm/using-async.d.ts +8 -0
  48. package/esm/using-async.d.ts.map +1 -0
  49. package/esm/using-async.js +22 -0
  50. package/esm/using-async.js.map +1 -0
  51. package/esm/using-async.spec.d.ts +17 -0
  52. package/esm/using-async.spec.d.ts.map +1 -0
  53. package/esm/using-async.spec.js +84 -0
  54. package/esm/using-async.spec.js.map +1 -0
  55. package/esm/using.d.ts +8 -0
  56. package/esm/using.d.ts.map +1 -0
  57. package/esm/using.js +15 -0
  58. package/esm/using.js.map +1 -0
  59. package/esm/{disposable.spec.d.ts → using.spec.d.ts} +2 -7
  60. package/esm/using.spec.d.ts.map +1 -0
  61. package/esm/using.spec.js +62 -0
  62. package/esm/using.spec.js.map +1 -0
  63. package/esm/value-observer.d.ts +3 -4
  64. package/esm/value-observer.d.ts.map +1 -1
  65. package/esm/value-observer.js +3 -3
  66. package/esm/value-observer.js.map +1 -1
  67. package/package.json +3 -3
  68. package/src/deep-merge.spec.ts +2 -2
  69. package/src/event-hub.spec.ts +4 -4
  70. package/src/event-hub.ts +2 -4
  71. package/src/index.ts +6 -4
  72. package/src/is-async-disposable.spec.ts +23 -0
  73. package/src/is-async-disposable.ts +8 -0
  74. package/src/is-disposable.spec.ts +23 -0
  75. package/src/is-disposable.ts +8 -0
  76. package/src/observable-value.spec.ts +11 -9
  77. package/src/observable-value.ts +3 -4
  78. package/src/path-helper.spec.ts +10 -1
  79. package/src/path-helper.ts +1 -1
  80. package/src/using-async.spec.ts +93 -0
  81. package/src/using-async.ts +24 -0
  82. package/src/using.spec.ts +67 -0
  83. package/src/using.ts +13 -0
  84. package/src/value-observer.ts +3 -4
  85. package/esm/disposable.d.ts +0 -49
  86. package/esm/disposable.d.ts.map +0 -1
  87. package/esm/disposable.js +0 -56
  88. package/esm/disposable.js.map +0 -1
  89. package/esm/disposable.spec.d.ts.map +0 -1
  90. package/esm/disposable.spec.js +0 -118
  91. package/esm/disposable.spec.js.map +0 -1
  92. package/esm/trace.d.ts +0 -119
  93. package/esm/trace.d.ts.map +0 -1
  94. package/esm/trace.js +0 -139
  95. package/esm/trace.js.map +0 -1
  96. package/esm/trace.spec.d.ts +0 -2
  97. package/esm/trace.spec.d.ts.map +0 -1
  98. package/esm/trace.spec.js +0 -184
  99. package/esm/trace.spec.js.map +0 -1
  100. package/src/disposable.spec.ts +0 -130
  101. package/src/disposable.ts +0 -69
  102. package/src/trace.spec.ts +0 -200
  103. package/src/trace.ts +0 -265
package/src/trace.spec.ts DELETED
@@ -1,200 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest'
2
- import { Trace } from './trace.js'
3
-
4
- class MockClass {
5
- constructor(private testValue?: string) {}
6
-
7
- public testError(msg: string) {
8
- throw Error(msg)
9
- }
10
-
11
- public testScope() {
12
- return this.testValue
13
- }
14
-
15
- public static addStatic(this: void, ...args: number[]) {
16
- return args.reduce((a, b) => a + b, 0)
17
- }
18
-
19
- public addInstance(this: void, ...args: number[]) {
20
- return args.reduce((a, b) => a + b, 0)
21
- }
22
-
23
- public async addInstanceAsync(this: void, ...args: number[]): Promise<number> {
24
- return args.reduce((a, b) => a + b, 0)
25
- }
26
-
27
- public async testErrorAsync(msg: string): Promise<number> {
28
- throw Error(msg)
29
- }
30
- }
31
-
32
- export const traceTests = describe('Trace tests', () => {
33
- describe('Static method traces', () => {
34
- it('Static Methods call should be traced with args', () => {
35
- const args = [1, 2, 3]
36
- const doneCallback = vi.fn()
37
- const observer = Trace.method({
38
- object: MockClass,
39
- method: MockClass.addStatic,
40
- onCalled: (traceData) => {
41
- expect(args).toEqual(traceData.methodArguments)
42
- observer.dispose()
43
- doneCallback()
44
- },
45
- })
46
- MockClass.addStatic(...args)
47
- expect(doneCallback).toBeCalled()
48
- })
49
-
50
- it('Static Methods call should be traced with args and return value', () => {
51
- const args = [1, 2, 3]
52
- const doneCallback = vi.fn()
53
- const observer = Trace.method({
54
- object: MockClass,
55
- method: MockClass.addStatic,
56
- onFinished: (traceData) => {
57
- expect(args).toEqual(traceData.methodArguments)
58
- expect(traceData.returned).toBe(1 + 2 + 3)
59
- observer.dispose()
60
- doneCallback()
61
- },
62
- })
63
- MockClass.addStatic(...args)
64
- expect(doneCallback).toBeCalled()
65
- })
66
-
67
- it("shouldn't be triggered after observer is disposed", () => {
68
- const args = [1, 2, 3]
69
- const shouldNotCall = vi.fn()
70
- const doneCallback = vi.fn()
71
-
72
- const observer = Trace.method({
73
- object: MockClass,
74
- method: MockClass.addStatic,
75
- onCalled: () => {
76
- shouldNotCall("Shouldn't be triggered here")
77
- },
78
- })
79
- const observer2 = Trace.method({
80
- object: MockClass,
81
- method: MockClass.addStatic,
82
- onCalled: () => {
83
- observer2.dispose()
84
- doneCallback()
85
- },
86
- })
87
- observer.dispose()
88
- const returned = MockClass.addStatic(...args)
89
- expect(returned).toEqual(1 + 2 + 3)
90
- expect(doneCallback).toBeCalled()
91
- expect(shouldNotCall).not.toBeCalled()
92
- })
93
- })
94
-
95
- describe('Instance method traces', () => {
96
- it('should be traced with arguments', () => {
97
- const instance = new MockClass()
98
- const args = [1, 2, 3]
99
- const doneCallback = vi.fn()
100
-
101
- const observer = Trace.method({
102
- object: instance,
103
- method: instance.addInstance,
104
- onFinished: (traceData) => {
105
- expect(args).toEqual(traceData.methodArguments)
106
- expect(traceData.returned).toBe(1 + 2 + 3)
107
- observer.dispose()
108
- doneCallback()
109
- },
110
- })
111
- instance.addInstance(...args)
112
- expect(doneCallback).toBeCalled()
113
- })
114
-
115
- it('should be traced asynchronously', async () => {
116
- const instance = new MockClass()
117
- const args = [1, 2, 3]
118
- const doneCallback = vi.fn()
119
-
120
- const observer = Trace.method({
121
- object: instance,
122
- method: instance.addInstanceAsync,
123
- isAsync: true,
124
- onFinished: (traceData) => {
125
- expect(args).toEqual(traceData.methodArguments)
126
- const { returned } = traceData
127
- expect(returned).toBe(1 + 2 + 3)
128
- observer.dispose()
129
- doneCallback()
130
- },
131
- })
132
- await instance.addInstanceAsync(...args)
133
- expect(doneCallback).toBeCalled()
134
- })
135
-
136
- it("should have a valid 'this' scope", () => {
137
- const instance = new MockClass('testValue')
138
- const doneCallback = vi.fn()
139
-
140
- const observer = Trace.method({
141
- object: instance,
142
- method: instance.testScope,
143
- onFinished: (traceData) => {
144
- if (traceData.returned) {
145
- expect(traceData.returned).toBe('testValue')
146
- observer.dispose()
147
- doneCallback()
148
- }
149
- },
150
- })
151
- expect(instance.testScope()).toBe('testValue')
152
- expect(doneCallback).toBeCalled()
153
- })
154
-
155
- it('should handle throwing errors', () => {
156
- const instance = new MockClass('testValue')
157
- const doneCallback = vi.fn()
158
-
159
- const observer = Trace.method({
160
- object: instance,
161
- method: instance.testError,
162
- onError: (traceData) => {
163
- if (traceData.error) {
164
- expect(traceData.error.message).toBe('message')
165
- observer.dispose()
166
- doneCallback()
167
- }
168
- },
169
- })
170
- expect(() => {
171
- instance.testError('message')
172
- }).toThrow()
173
- expect(doneCallback).toBeCalled()
174
- })
175
-
176
- it('should handle throwing errors with asyncs', async () => {
177
- const instance = new MockClass('testValue')
178
- const doneCallback = vi.fn()
179
-
180
- const observer = Trace.method({
181
- object: instance,
182
- method: instance.testErrorAsync,
183
- isAsync: true,
184
- onError: (traceData) => {
185
- if (traceData.error) {
186
- expect(traceData.error.message).toBe('message')
187
- observer.dispose()
188
- doneCallback()
189
- }
190
- },
191
- })
192
- try {
193
- await instance.testErrorAsync('message')
194
- } catch (error) {
195
- // ignore
196
- }
197
- expect(doneCallback).toBeCalled()
198
- })
199
- })
200
- })
package/src/trace.ts DELETED
@@ -1,265 +0,0 @@
1
- import type { Disposable } from './disposable.js'
2
- import { ObservableValue } from './observable-value.js'
3
-
4
- /**
5
- * Options object for tracing method calls
6
- */
7
- export interface TraceMethodOptions<T, TMethod extends (...args: TArgs) => unknown, TArgs extends unknown[]> {
8
- /**
9
- * The context object. Can be an instance or a constructor for static methods
10
- */
11
- object: T
12
- /**
13
- * The method reference that needs to be traced
14
- */
15
- method: TMethod
16
- /**
17
- * Callback that will be called right before executing the method
18
- */
19
- onCalled?: (newValue: TraceMethodCall<TArgs>) => void
20
- /**
21
- * Callback that will be called right after the method returns
22
- */
23
- onFinished?: (newValue: TraceMethodFinished<ReturnType<TMethod>, TArgs>) => void
24
- /**
25
- * Callback that will be called when a method throws an error
26
- */
27
- onError?: (newValue: TraceMethodError<TArgs>) => void
28
-
29
- /**
30
- * The method execution will be awaited if set
31
- */
32
- isAsync?: boolean
33
- }
34
-
35
- /**
36
- * Defines a trace method call object
37
- */
38
- export interface TraceMethodCall<TArgs extends any[]> {
39
- /**
40
- * The timestamp when the event occured
41
- */
42
- startDateTime: Date
43
-
44
- /**
45
- * The provided arguments for the call
46
- */
47
- methodArguments: TArgs
48
- }
49
-
50
- /**
51
- * Defines a trace event when a method call has been finished
52
- */
53
- export interface TraceMethodFinished<TReturns, TArgs extends any[]> extends TraceMethodCall<TArgs> {
54
- returned: TReturns
55
- finishedDateTime: Date
56
- }
57
-
58
- /**
59
- * Defines a trace event when an error was thrown during a method call
60
- */
61
- export interface TraceMethodError<TArgs extends any[]> extends TraceMethodCall<TArgs> {
62
- error: any
63
- errorDateTime: Date
64
- }
65
-
66
- /**
67
- * Defines a method mapping object
68
- */
69
- export interface MethodMapping<TReturns, TArgs extends any[]> {
70
- /**
71
- * The original method instance
72
- */
73
- originalMethod: (...args: TArgs) => TReturns
74
- /**
75
- * An observable for distributing the events
76
- */
77
- callObservable: ObservableValue<TraceMethodCall<TArgs>>
78
-
79
- finishedObservable: ObservableValue<TraceMethodFinished<any, TArgs>>
80
- errorObservable: ObservableValue<TraceMethodError<TArgs>>
81
- }
82
-
83
- /**
84
- * Defines an Object Trace mapping
85
- */
86
- export interface ObjectTrace {
87
- /**
88
- * Map about the already wrapped methods
89
- */
90
- methodMappings: Map<string, MethodMapping<any, any[]>>
91
- }
92
-
93
- /**
94
- * Helper class that can be used to trace method calls programmatically
95
- *
96
- * Usage example:
97
- * ```ts
98
- * const methodTracer: IDisposable = Trace.method({
99
- * object: myObjectInstance, // You can define an object constructor for static methods as well
100
- * method: myObjectInstance.method, // The method to be tracked
101
- * isAsync: true, // if you set to async, method finished will be *await*-ed
102
- * onCalled: (traceData) => {
103
- * console.log("Method called:", traceData)
104
- * },
105
- * onFinished: (traceData) => {
106
- * console.log("Method call finished:", traceData)
107
- * },
108
- * onError: (traceData) => {
109
- * console.log("Method throwed an error:", traceData)
110
- * }
111
- * });
112
- * ```
113
- */
114
- export class Trace {
115
- private static objectTraces: Map<object, ObjectTrace> = new Map()
116
-
117
- private static getMethodTrace<TArgs extends any[], TReturns>(
118
- object: object,
119
- method: (...args: TArgs) => TReturns,
120
- ): MethodMapping<TReturns, TArgs> {
121
- const objectTrace = this.objectTraces.get(object) as any as ObjectTrace
122
- return objectTrace.methodMappings.get(method.name) as any as MethodMapping<TReturns, TArgs>
123
- }
124
-
125
- private static traceStart<TReturns, TArgs extends any[]>(
126
- methodTrace: MethodMapping<TReturns, TArgs[]>,
127
- args: TArgs[],
128
- ) {
129
- const startDateTime = new Date()
130
- const traceValue = {
131
- methodArguments: args,
132
- startDateTime,
133
- }
134
- methodTrace.callObservable.setValue(traceValue)
135
- return traceValue
136
- }
137
-
138
- private static traceFinished<TReturns, TArgs extends any[]>(
139
- methodTrace: MethodMapping<TReturns, TArgs>,
140
- args: TArgs,
141
- callTrace: TraceMethodCall<TArgs>,
142
- returned: any,
143
- ) {
144
- const finishedTrace: TraceMethodFinished<TReturns, TArgs> = {
145
- methodArguments: args,
146
- startDateTime: callTrace.startDateTime,
147
- finishedDateTime: new Date(),
148
- returned,
149
- }
150
- methodTrace.finishedObservable.setValue(finishedTrace)
151
- }
152
-
153
- private static traceError<TReturns, TArgs extends any[]>(
154
- methodTrace: MethodMapping<TReturns, TArgs>,
155
- args: TArgs,
156
- callTrace: TraceMethodCall<TArgs>,
157
- error: any,
158
- ) {
159
- const errorTrace: TraceMethodError<TArgs> = {
160
- methodArguments: args,
161
- startDateTime: callTrace.startDateTime,
162
- errorDateTime: new Date(),
163
- error,
164
- }
165
- methodTrace.errorObservable.setValue(errorTrace)
166
- return errorTrace
167
- }
168
-
169
- private static callMethod<TReturns, TArgs extends any[]>(
170
- object: object,
171
- method: (...args: TArgs) => TReturns,
172
- args: TArgs,
173
- ) {
174
- const methodTrace: any = this.getMethodTrace(object, method)
175
- const start = this.traceStart(methodTrace, args)
176
- try {
177
- const returned = methodTrace.originalMethod.call(object, ...args)
178
- this.traceFinished(methodTrace, args, start, returned)
179
- return returned
180
- } catch (error) {
181
- this.traceError(methodTrace, args, start, error)
182
- throw error
183
- }
184
- }
185
-
186
- private static async callMethodAsync<TReturns, TArgs extends any[]>(
187
- object: object,
188
- method: (...args: TArgs) => TReturns,
189
- args: TArgs,
190
- ) {
191
- const methodTrace: any = this.getMethodTrace(object, method)
192
- const start = this.traceStart(methodTrace, args)
193
- try {
194
- const returned = await methodTrace.originalMethod.call(object, ...args)
195
- this.traceFinished(methodTrace, args, start, returned)
196
- return returned
197
- } catch (error) {
198
- this.traceError(methodTrace, args, start, error)
199
- throw error
200
- }
201
- }
202
-
203
- /**
204
- * Creates an observer that will be observe method calls, finishes and errors
205
- * @param options The options object for the trace
206
- * @returns the trace object that can be disposed after usage
207
- */
208
- public static method<T extends object, TMethod extends (...args: TArgs) => any, TArgs extends any[]>(
209
- options: TraceMethodOptions<T, TMethod, TArgs>,
210
- ): Disposable {
211
- // add object mapping
212
- if (!this.objectTraces.has(options.object)) {
213
- this.objectTraces.set(options.object, {
214
- methodMappings: new Map(),
215
- })
216
- }
217
- // setup override if needed
218
- if (!(options.method as any).isTraced) {
219
- const overriddenMethod = options.isAsync
220
- ? (...args: TArgs) => this.callMethodAsync(options.object, options.method, args)
221
- : (...args: TArgs) => this.callMethod(options.object, options.method, args)
222
- Object.defineProperty(overriddenMethod, 'name', { value: options.method.name })
223
- Object.defineProperty(overriddenMethod, 'isTraced', { value: options.method.name })
224
- ;(options.object as any)[options.method.name] = overriddenMethod
225
- }
226
- const objectTrace = this.objectTraces.get(options.object) as any as ObjectTrace
227
-
228
- // add method mapping if needed
229
- if (!objectTrace.methodMappings.has(options.method.name)) {
230
- objectTrace.methodMappings.set(options.method.name, {
231
- originalMethod: options.method,
232
- callObservable: new ObservableValue<TraceMethodCall<any>>({
233
- methodArguments: [],
234
- startDateTime: new Date(''),
235
- }),
236
- finishedObservable: new ObservableValue<TraceMethodFinished<ReturnType<TMethod>, any>>({
237
- methodArguments: [],
238
- startDateTime: new Date(''),
239
- finishedDateTime: new Date(''),
240
- returned: undefined as ReturnType<TMethod>,
241
- }),
242
- errorObservable: new ObservableValue<TraceMethodError<any>>({
243
- methodArguments: [],
244
- startDateTime: new Date(''),
245
- errorDateTime: new Date(''),
246
- error: undefined,
247
- }),
248
- })
249
- }
250
- const methodTrace = objectTrace.methodMappings.get(options.method.name) as any as MethodMapping<
251
- ReturnType<TMethod>,
252
- TArgs
253
- >
254
- const callbacks = [
255
- options.onCalled && methodTrace.callObservable.subscribe(options.onCalled),
256
- options.onFinished && methodTrace.finishedObservable.subscribe(options.onFinished),
257
- options.onError && methodTrace.errorObservable.subscribe(options.onError),
258
- ]
259
-
260
- // Subscribe and return the observer
261
- return {
262
- dispose: () => callbacks.forEach((c) => c && c.dispose()),
263
- }
264
- }
265
- }