@softsky/utils 2.8.1 → 2.8.2

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 CHANGED
@@ -440,7 +440,7 @@ Effects are simplest way to react to signal changes.
440
440
  Returned data from handler function will be passed to it on next signal change.
441
441
  Returns a function that will clear the effect.
442
442
 
443
-
443
+ ```ts
444
444
  // Will print signal on change
445
445
  effect(()=>{
446
446
  console.log($mySignal())
@@ -451,6 +451,7 @@ const mySignal = $mySignal()
451
451
  if(last>mySignal) console.log('Increment!')
452
452
  return mySignal;
453
453
  })
454
+ ```
454
455
 
455
456
  ---
456
457
  __function__ `untrack` - __SIGNALS SYSTEM__
@@ -466,7 +467,7 @@ console.log(untrack($a)+$b())
466
467
  ```
467
468
 
468
469
  ---
469
- __function__ `derived` - __SIGNALS SYSTEM__
470
+ __function__ `computed` - __SIGNALS SYSTEM__
470
471
 
471
472
  Creates a derived reactive memoized signal.
472
473
 
@@ -509,6 +510,52 @@ const timeout = setTimeout(() => promise.reject('Timeout')}, 5000)
509
510
  primise.then(() => clearTimeout(timeout))
510
511
  ```
511
512
 
513
+ ---
514
+ __function__ `resource` - __SIGNALS SYSTEM__
515
+
516
+ Creates a async resource with outputs:
517
+ 1. value$ - return value of function
518
+ 2. isLoading$ - whether promise is still running
519
+ 3. error$ - error that happened during. Set on error, cleared on refresh
520
+ 4. refresh - force rerun handler
521
+
522
+ MAKE SURE THAT ALL SIGNAL'S VALUES ARE GRABBED BEFORE NEXT TICK!!!
523
+
524
+ ```ts
525
+ const userId$ = signal(1);
526
+
527
+ // WRONG
528
+ const userResource = resource(async () => {
529
+ const res = await fetch(`/api/user`)
530
+ const userId = userId$(); // ERROR: Will not subscribe because after Promise i.e. next tick
531
+ return res.json().find(x=>x.id===userId);
532
+ })
533
+
534
+ // Correct
535
+ const userResource = resource(async () => {
536
+ const userId = userId$(); // Correctly subscribed because ran on the same tick
537
+ const res = await fetch(`/api/user`)
538
+ return res.json().find(x=>x.id===userId);
539
+ })
540
+
541
+ // React to changes
542
+ effect(() => {
543
+ if (userResource.isLoading$()) {
544
+ console.log('loading user...')
545
+ } else if (userResource.error$()) {
546
+ console.error('failed to load user', userResource.error$())
547
+ } else {
548
+ console.log('user loaded', userResource.value$())
549
+ }
550
+ })
551
+
552
+ // Force a refresh
553
+ userResource.refresh()
554
+
555
+ // Stop automatic updates and cleanup
556
+ userResource.clear()
557
+ ```
558
+
512
559
  ---
513
560
 
514
561
 
package/dist/arrays.d.ts CHANGED
@@ -29,9 +29,8 @@ export declare function permutations<T>(array: T[]): T[][];
29
29
  * If compare returns > 0 it means target is smaller.
30
30
  * If compare returns < 0 it means target is bigger.
31
31
  *
32
- * ```ts
32
+ * @example
33
33
  * pushToSorted(numArray, 10, x => x - 10);
34
- * ```
35
34
  */
36
35
  export declare function pushToSorted<T>(array: T[], element: T, compare: (x: T) => number): void;
37
36
  /**
package/dist/arrays.js CHANGED
@@ -101,9 +101,8 @@ export function permutations(array) {
101
101
  * If compare returns > 0 it means target is smaller.
102
102
  * If compare returns < 0 it means target is bigger.
103
103
  *
104
- * ```ts
104
+ * @example
105
105
  * pushToSorted(numArray, 10, x => x - 10);
106
- * ```
107
106
  */
108
107
  export function pushToSorted(array, element, compare) {
109
108
  const index = binarySearch(array.length, (x) => compare(array[x]), true);
package/dist/control.d.ts CHANGED
@@ -80,7 +80,7 @@ export declare class SimpleEventSource<EVENTS extends Record<string, unknown>> {
80
80
  /**
81
81
  * Semaphore is used to limit concurrent tasks by delaying promise.
82
82
  *
83
- * ```ts
83
+ * @example
84
84
  * const semaphore = new Semaphore(2);
85
85
  *
86
86
  * async function task() {
@@ -103,7 +103,6 @@ export declare class SimpleEventSource<EVENTS extends Record<string, unknown>> {
103
103
  * // Your code
104
104
  * })
105
105
  * // ...
106
- * ```
107
106
  */
108
107
  export declare class Semaphore {
109
108
  /** The maximum number of concurrent operations allowed.*/
package/dist/control.js CHANGED
@@ -225,7 +225,7 @@ export class SimpleEventSource {
225
225
  /**
226
226
  * Semaphore is used to limit concurrent tasks by delaying promise.
227
227
  *
228
- * ```ts
228
+ * @example
229
229
  * const semaphore = new Semaphore(2);
230
230
  *
231
231
  * async function task() {
@@ -248,7 +248,6 @@ export class SimpleEventSource {
248
248
  * // Your code
249
249
  * })
250
250
  * // ...
251
- * ```
252
251
  */
253
252
  export class Semaphore {
254
253
  capacity;
@@ -43,14 +43,13 @@ export declare function capitalizeFirstLetter(value: string): string;
43
43
  /**
44
44
  * pipe() can be called on one or more functions, each of which can take the return of previous value.
45
45
  *
46
- * ```ts
46
+ * @example
47
47
  * // Takes string, converts to int, calc sqrt, convert and return date
48
48
  * pipe(
49
49
  * (x: string) => Number.parseInt(x),
50
50
  * (x) => Math.sqrt(x),
51
51
  * (x) => new Date(x)
52
52
  * )('69')
53
- * ```
54
53
  */
55
54
  export declare function pipe(): <T>(x: T) => T;
56
55
  export declare function pipe<T, A>(function1: (x: T) => A): (x: T) => A;
package/dist/numbers.d.ts CHANGED
@@ -17,11 +17,10 @@ export declare function mean(array: number[]): number;
17
17
  /**
18
18
  * Round with precision.
19
19
  *
20
- * ```ts
20
+ * @example
21
21
  * round(1.2345); // 1
22
22
  * round(1.2345, 2); // 1.23
23
23
  * round(1.2345, 10); // 1.2345
24
- * ```
25
24
  */
26
25
  export declare function round(value: number, precision?: number): number;
27
26
  /** Sum of array of numbers */
package/dist/numbers.js CHANGED
@@ -66,11 +66,10 @@ export function mean(array) {
66
66
  /**
67
67
  * Round with precision.
68
68
  *
69
- * ```ts
69
+ * @example
70
70
  * round(1.2345); // 1
71
71
  * round(1.2345, 2); // 1.23
72
72
  * round(1.2345, 10); // 1.2345
73
- * ```
74
73
  */
75
74
  export function round(value, precision = 0) {
76
75
  const mult = 10 ** precision;
package/dist/signals.d.ts CHANGED
@@ -11,7 +11,7 @@ export type Signal<T> = (setTo?: T | ((previous: T) => T)) => T;
11
11
  * when this data has changed any effects containing
12
12
  * this signal will be rerun.
13
13
  *
14
- * ```ts
14
+ * @example
15
15
  * const $mySignal = signal<number|undefined>(1) // Create signal with initial value 1
16
16
  * $mySignal(5) // Set to 5
17
17
  * $mySignal(undefined) // Set to undefined
@@ -20,7 +20,6 @@ export type Signal<T> = (setTo?: T | ((previous: T) => T)) => T;
20
20
  * effect(()=>{
21
21
  * console.log($mySignal())
22
22
  * })
23
- * ```
24
23
  */
25
24
  export declare function signal<T>(): Signal<T | undefined>;
26
25
  export declare function signal<T>(value: T): Signal<T>;
@@ -31,7 +30,7 @@ export declare function signal<T>(value: T): Signal<T>;
31
30
  * Returned data from handler function will be passed to it on next signal change.
32
31
  * Returns a function that will clear the effect.
33
32
  *
34
- *
33
+ * @example
35
34
  * // Will print signal on change
36
35
  * effect(()=>{
37
36
  * console.log($mySignal())
@@ -48,14 +47,13 @@ export declare function effect<T>(handler: (argument: T | undefined) => T, initi
48
47
  * __SIGNALS SYSTEM__
49
48
  *
50
49
  * Untrack helps to not react to changes in effects.
51
- * ```ts
50
+ * @example
52
51
  * const $a = signal(1)
53
52
  * const $b = signal(2)
54
53
  * // Will only run on changes to $b
55
54
  * effect(()=>{
56
55
  * console.log(untrack($a)+$b())
57
56
  * })
58
- * ```
59
57
  */
60
58
  export declare function untrack<T>(handler: () => T): T;
61
59
  /**
@@ -63,16 +61,15 @@ export declare function untrack<T>(handler: () => T): T;
63
61
  *
64
62
  * Creates a derived reactive memoized signal.
65
63
  *
66
- * ```ts
64
+ * @example
67
65
  * // Sum of all changes of $a()
68
66
  * const { signal: $sumOfTwo, clear: clearSum } = derived((value) => value + $a(), 0)
69
- * ```
70
67
  */
71
- export declare function derived<T>(handler: (argument: T | undefined) => T): {
68
+ export declare function computed<T>(handler: (argument: T | undefined) => T): {
72
69
  signal: Signal<T>;
73
70
  clear: () => void;
74
71
  };
75
- export declare function derived<T>(handler: (argument: T) => T, initialValue: T): {
72
+ export declare function computed<T>(handler: (argument: T) => T, initialValue: T): {
76
73
  signal: Signal<T>;
77
74
  clear: () => void;
78
75
  };
@@ -81,7 +78,7 @@ export declare function derived<T>(handler: (argument: T) => T, initialValue: T)
81
78
  *
82
79
  * Batches multiple edits, so they don't call same effects multiple times
83
80
  *
84
- * ```ts
81
+ * @example
85
82
  * const $a = signal(1)
86
83
  * const $b = signal(2)
87
84
  * effect(()=>{
@@ -94,7 +91,6 @@ export declare function derived<T>(handler: (argument: T) => T, initialValue: T)
94
91
  * $a(5);
95
92
  * $b(5);
96
93
  * })
97
- * ```
98
94
  */
99
95
  export declare function batch(handler: AnyFunction): void;
100
96
  /**
@@ -103,12 +99,65 @@ export declare function batch(handler: AnyFunction): void;
103
99
  * Returns ImmediatePromise that is resolved when check function returns truthy value.
104
100
  * If you want to, you can resolve or reject promise beforehand.
105
101
  *
106
- * ```ts
102
+ * @example
107
103
  * await when(() => $a()>5)
108
104
  * // With timeout
109
105
  * const promise = when(() => $a() > 5)
110
106
  * const timeout = setTimeout(() => promise.reject('Timeout')}, 5000)
111
107
  * primise.then(() => clearTimeout(timeout))
112
- * ```
113
108
  */
114
109
  export declare function when(check: () => unknown): ImmediatePromise<undefined>;
110
+ export type ResourceReturnType<T> = {
111
+ value$: Signal<T>;
112
+ isLoading$: Signal<boolean>;
113
+ error$: Signal<unknown>;
114
+ refresh: () => Promise<T | undefined>;
115
+ clear: () => void;
116
+ };
117
+ /**
118
+ * __SIGNALS SYSTEM__
119
+ *
120
+ * Creates a async resource with outputs:
121
+ * 1. value$ - return value of function
122
+ * 2. isLoading$ - whether promise is still running
123
+ * 3. error$ - error that happened during. Set on error, cleared on refresh
124
+ * 4. refresh - force rerun handler
125
+ *
126
+ * MAKE SURE THAT ALL SIGNAL'S VALUES ARE GRABBED BEFORE NEXT TICK!!!
127
+ *
128
+ * @example
129
+ * const userId$ = signal(1);
130
+ *
131
+ * // WRONG
132
+ * const userResource = resource(async () => {
133
+ * const res = await fetch(`/api/user`)
134
+ * const userId = userId$(); // ERROR: Will not subscribe because after Promise i.e. next tick
135
+ * return res.json().find(x=>x.id===userId);
136
+ * })
137
+ *
138
+ * // Correct
139
+ * const userResource = resource(async () => {
140
+ * const userId = userId$(); // Correctly subscribed because ran on the same tick
141
+ * const res = await fetch(`/api/user`)
142
+ * return res.json().find(x=>x.id===userId);
143
+ * })
144
+ *
145
+ * // React to changes
146
+ * effect(() => {
147
+ * if (userResource.isLoading$()) {
148
+ * console.log('loading user...')
149
+ * } else if (userResource.error$()) {
150
+ * console.error('failed to load user', userResource.error$())
151
+ * } else {
152
+ * console.log('user loaded', userResource.value$())
153
+ * }
154
+ * })
155
+ *
156
+ * // Force a refresh
157
+ * userResource.refresh()
158
+ *
159
+ * // Stop automatic updates and cleanup
160
+ * userResource.clear()
161
+ */
162
+ export declare function resource<T>(handler: (argument: T | undefined) => Promise<T>): ResourceReturnType<T | undefined>;
163
+ export declare function resource<T>(handler: (argument: T) => Promise<T>, initialValue: T): ResourceReturnType<T>;
package/dist/signals.js CHANGED
@@ -47,7 +47,7 @@ function clearEffect(handler) {
47
47
  * Returned data from handler function will be passed to it on next signal change.
48
48
  * Returns a function that will clear the effect.
49
49
  *
50
- *
50
+ * @example
51
51
  * // Will print signal on change
52
52
  * effect(()=>{
53
53
  * console.log($mySignal())
@@ -75,14 +75,13 @@ export function effect(handler, initialValue) {
75
75
  * __SIGNALS SYSTEM__
76
76
  *
77
77
  * Untrack helps to not react to changes in effects.
78
- * ```ts
78
+ * @example
79
79
  * const $a = signal(1)
80
80
  * const $b = signal(2)
81
81
  * // Will only run on changes to $b
82
82
  * effect(()=>{
83
83
  * console.log(untrack($a)+$b())
84
84
  * })
85
- * ```
86
85
  */
87
86
  export function untrack(handler) {
88
87
  const lastEffect = currentEffect;
@@ -91,7 +90,7 @@ export function untrack(handler) {
91
90
  currentEffect = lastEffect;
92
91
  return data;
93
92
  }
94
- export function derived(handler, initialValue) {
93
+ export function computed(handler, initialValue) {
95
94
  const signal$ = signal(initialValue);
96
95
  const wrappedHandler = () => signal$((value) => handler(value));
97
96
  currentEffect = wrappedHandler;
@@ -109,7 +108,7 @@ export function derived(handler, initialValue) {
109
108
  *
110
109
  * Batches multiple edits, so they don't call same effects multiple times
111
110
  *
112
- * ```ts
111
+ * @example
113
112
  * const $a = signal(1)
114
113
  * const $b = signal(2)
115
114
  * effect(()=>{
@@ -122,7 +121,6 @@ export function derived(handler, initialValue) {
122
121
  * $a(5);
123
122
  * $b(5);
124
123
  * })
125
- * ```
126
124
  */
127
125
  export function batch(handler) {
128
126
  batchedEffects = new Set();
@@ -137,13 +135,12 @@ export function batch(handler) {
137
135
  * Returns ImmediatePromise that is resolved when check function returns truthy value.
138
136
  * If you want to, you can resolve or reject promise beforehand.
139
137
  *
140
- * ```ts
138
+ * @example
141
139
  * await when(() => $a()>5)
142
140
  * // With timeout
143
141
  * const promise = when(() => $a() > 5)
144
142
  * const timeout = setTimeout(() => promise.reject('Timeout')}, 5000)
145
143
  * primise.then(() => clearTimeout(timeout))
146
- * ```
147
144
  */
148
145
  export function when(check) {
149
146
  const promise = new ImmediatePromise();
@@ -156,3 +153,42 @@ export function when(check) {
156
153
  });
157
154
  return promise;
158
155
  }
156
+ export function resource(handler, initialValue) {
157
+ const value$ = signal(initialValue);
158
+ const isLoading$ = signal(false);
159
+ const error$ = signal();
160
+ const wrappedHandler = async () => {
161
+ batch(() => {
162
+ isLoading$(true);
163
+ error$(undefined);
164
+ });
165
+ let value;
166
+ let error;
167
+ try {
168
+ value = await handler(untrack(value$));
169
+ }
170
+ catch (newError) {
171
+ error = newError;
172
+ }
173
+ batch(() => {
174
+ if (error)
175
+ error$(error);
176
+ else
177
+ value$(value);
178
+ isLoading$(false);
179
+ });
180
+ return value;
181
+ };
182
+ currentEffect = wrappedHandler;
183
+ void wrappedHandler();
184
+ currentEffect = undefined;
185
+ return {
186
+ value$,
187
+ isLoading$,
188
+ error$,
189
+ refresh: wrappedHandler,
190
+ clear: () => {
191
+ clearEffect(wrappedHandler);
192
+ },
193
+ };
194
+ }
package/dist/types.d.ts CHANGED
@@ -42,7 +42,7 @@ export type Concat<T, U> = T extends any[] ? U extends any[] ? [...T, ...U] : ne
42
42
  /**
43
43
  * Visual only overhaul. Shows final type result on hover.
44
44
  *
45
- * ```ts
45
+ * @example
46
46
  * type a = {a: '1'}
47
47
  * type b = Prettify<a & { b: 'b' }>
48
48
  * // On hovering b it will show
@@ -54,7 +54,6 @@ export type Concat<T, U> = T extends any[] ? U extends any[] ? [...T, ...U] : ne
54
54
  * type b = a & {
55
55
  * b: "b";
56
56
  * }
57
- * ```
58
57
  * */
59
58
  export type Prettify<T extends object> = {
60
59
  [k in keyof T]: T[k];
@@ -62,9 +61,8 @@ export type Prettify<T extends object> = {
62
61
  /**
63
62
  * Create a tuple of size.
64
63
  *
65
- * ```ts
64
+ * @example
66
65
  * type a = Tuple<number, 3>
67
66
  * type b = [number, number, number]
68
- * ```
69
67
  */
70
68
  export type Tuple<T, N extends number, R extends readonly T[] = []> = R['length'] extends N ? R : Tuple<T, N, [...R, T]>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softsky/utils",
3
- "version": "2.8.1",
3
+ "version": "2.8.2",
4
4
  "description": "JavaScript/TypeScript utilities",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {