@pistonite/pure 0.0.19 → 0.21.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pistonite/pure",
3
- "version": "0.0.19",
3
+ "version": "0.21.0",
4
4
  "type": "module",
5
5
  "description": "Pure TypeScript libraries for my projects",
6
6
  "homepage": "https://github.com/Pistonite/pure",
@@ -33,7 +33,7 @@
33
33
  "@types/file-saver": "^2.0.7",
34
34
  "eslint": "^9.19.0",
35
35
  "typescript": "^5.7.2",
36
- "vitest": "^2.1.8",
36
+ "vitest": "^3.0.5",
37
37
  "mono-dev": "0.0.0"
38
38
  }
39
39
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * # pure/memory
3
+ * JS memory utilities
4
+ *
5
+ * @module
6
+ */
7
+ export { cell, type CellConstructor, type Cell } from "./cell.ts";
8
+ export { persist, type PersistConstructor, type Persist } from "./persist.ts";
@@ -0,0 +1,139 @@
1
+ export type ExternalWeakRef<TUnderlying, TType> = {
2
+ /**
3
+ * A marker value for the underlying object type.
4
+ *
5
+ * This is commonly a string literal or a symbol.
6
+ */
7
+ type: TType;
8
+
9
+ /**
10
+ * The underlying object reference.
11
+ */
12
+ ref: TUnderlying | undefined;
13
+
14
+ /**
15
+ * Free the underlying object.
16
+ */
17
+ free: () => void;
18
+
19
+ /**
20
+ * Update the underlying object reference.
21
+ *
22
+ * If the new reference is the same as the old one, nothing will happen.
23
+ * If the old reference is not undefined, it will be freed.
24
+ */
25
+ set: (value: TUnderlying | undefined) => void;
26
+ };
27
+
28
+ export type ExternalWeakRefConstructor<TUnderlying, TType> = {
29
+ /**
30
+ * A marker value for the underlying object type.
31
+ *
32
+ * This is commonly a string literal or a symbol.
33
+ */
34
+ marker: TType;
35
+
36
+ /**
37
+ * The function to free the underlying object.
38
+ */
39
+ free: (obj: TUnderlying) => void;
40
+ };
41
+
42
+ /**
43
+ * Create a weak reference type for managing externally memory-managed object. This means
44
+ * the objects needs to be freed manually by the external code.
45
+ *
46
+ * The `marker` option is used to distinguish between different types of weak references
47
+ * with the same underlying representation for the reference.
48
+ *
49
+ * Note that the underlying representation should not be undefined-able!
50
+ *
51
+ * ## Example
52
+ * ```typescript
53
+ * import { makeExternalWeakRefType } from "@pistonite/pure/memory";
54
+ *
55
+ * // assume `number` is the JS type used to represent the external object
56
+ * // for example, this can be a pointer to a C++ object
57
+ * declare function freeFoo(obj: number) => void;
58
+ *
59
+ * // some function that allocates a foo object externally and returns
60
+ * // a reference
61
+ * declare function getFoo(): number;
62
+ *
63
+ * const makeFooRef = makeExternalWeakRefType({
64
+ * marker: "foo",
65
+ * free: (obj) => {
66
+ * freeFoo(obj);
67
+ * }
68
+ * });
69
+ * type FooRef = ReturnType<typeof makeFooRef>;
70
+ *
71
+ * // create a reference to a foo object
72
+ * // now this reference can be passed around in JS,
73
+ * // as long as the ownership model is clear and the owner
74
+ * // remembers to free it
75
+ * const fooRef = makeFooRef(getFoo());
76
+ *
77
+ * // free the foo object when it is no longer needed
78
+ * fooRef.free();
79
+ *
80
+ * ## Updating the reference
81
+ * The `set` method will update the reference and free the old one if exists
82
+ * ```
83
+ * const fooRef = makeFooRef(getFoo());
84
+ * fooRef.set(getFoo()); // the old one will be freed, unless it is the same as the new one
85
+ * ```
86
+ *
87
+ * This has a major pitfall: If the ExternalWeakRef is shared, the new object will be accessible
88
+ * by code that has the old reference. In other words, when the reference is updated, code that
89
+ * already has the old reference will not able to know that it has changed.
90
+ *
91
+ * If this is a problem, you should use this pattern instead:
92
+ * ```typescript
93
+ * // track the "current" valid reference
94
+ * let currentRef = makeFooRef(undefined);
95
+ *
96
+ * export const getFooRef = (): FooRef => {
97
+ * // because of this function, many other places can hold
98
+ * // a valid reference to foo
99
+ * return currentRef;
100
+ * }
101
+ *
102
+ * export const updateFooRef = (newFoo: number): void => {
103
+ * // when updating the reference, we create a new weak ref and free the old one
104
+ * if (currentRef.ref === newFoo) {
105
+ * return; // always need to check if old and new are the same, otherwise we will be freeing the new one
106
+ * }
107
+ * const newRef = makeFooRef(newFoo);
108
+ * currentRef.free();
109
+ * currentRef = newRef;
110
+ *
111
+ * // now other places that hold the old reference will see it's freed
112
+ * }
113
+ * ```
114
+ */
115
+ export const makeExternalWeakRefType = <TUnderlying, TType>({
116
+ marker,
117
+ free,
118
+ }: ExternalWeakRefConstructor<TUnderlying, TType>) => {
119
+ return (
120
+ obj: TUnderlying | undefined,
121
+ ): ExternalWeakRef<TUnderlying, TType> => {
122
+ const weakRefObj = {
123
+ type: marker,
124
+ ref: obj,
125
+ free: () => {
126
+ if (weakRefObj.ref !== undefined) {
127
+ free(weakRefObj.ref);
128
+ }
129
+ },
130
+ set: (value: TUnderlying | undefined) => {
131
+ if (weakRefObj.ref !== undefined && weakRefObj.ref !== value) {
132
+ free(weakRefObj.ref);
133
+ }
134
+ weakRefObj.ref = value;
135
+ },
136
+ };
137
+ return weakRefObj;
138
+ };
139
+ };
package/src/pref/dark.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { persist } from "../sync/persist.ts";
1
+ import { persist } from "../memory/persist.ts";
2
2
  import { injectStyle } from "./injectStyle.ts";
3
3
 
4
4
  const dark = persist({
@@ -1,4 +1,4 @@
1
- import { persist } from "../sync/persist.ts";
1
+ import { persist } from "../memory/persist.ts";
2
2
 
3
3
  let supportedLocales: readonly string[] = [];
4
4
  let defaultLocale: string = "";
package/src/sync/batch.ts CHANGED
@@ -1,4 +1,9 @@
1
- import { makePromise, type AwaitRet } from "./util.ts";
1
+ import {
2
+ type AnyFn,
3
+ makePromise,
4
+ type PromiseHandle,
5
+ type AwaitRet,
6
+ } from "./util.ts";
2
7
 
3
8
  /**
4
9
  * An async event wrapper that allows multiple calls in an interval
@@ -87,8 +92,7 @@ import { makePromise, type AwaitRet } from "./util.ts";
87
92
  * ```
88
93
  *
89
94
  */
90
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
91
- export function batch<TFn extends (...args: any[]) => any>({
95
+ export function batch<TFn extends AnyFn>({
92
96
  fn,
93
97
  batch,
94
98
  unbatch,
@@ -108,8 +112,7 @@ export function batch<TFn extends (...args: any[]) => any>({
108
112
  /**
109
113
  * Options to construct a `batch` function
110
114
  */
111
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
112
- export type BatchConstructor<TFn extends (...args: any[]) => any> = {
115
+ export type BatchConstructor<TFn extends AnyFn> = {
113
116
  /** Function to be wrapped */
114
117
  fn: TFn;
115
118
  /** Function to batch the inputs across multiple calls */
@@ -135,15 +138,11 @@ export type BatchConstructor<TFn extends (...args: any[]) => any> = {
135
138
  disregardExecutionTime?: boolean;
136
139
  };
137
140
 
138
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
139
- class BatchImpl<TFn extends (...args: any[]) => any> {
141
+ class BatchImpl<TFn extends AnyFn> {
140
142
  private idle: boolean;
141
- private scheduled: {
143
+ private scheduled: (PromiseHandle<AwaitRet<TFn>> & {
142
144
  input: Parameters<TFn>;
143
- promise: Promise<AwaitRet<TFn>>;
144
- resolve: (value: AwaitRet<TFn>) => void;
145
- reject: (error: unknown) => void;
146
- }[];
145
+ })[];
147
146
 
148
147
  constructor(
149
148
  private fn: TFn,
@@ -166,7 +165,7 @@ class BatchImpl<TFn extends (...args: any[]) => any> {
166
165
  this.idle = false;
167
166
  return this.execute(...args);
168
167
  }
169
- const { promise, reject, resolve } = makePromise<AwaitRet<TFn>>();
168
+ const { promise, resolve, reject } = makePromise<AwaitRet<TFn>>();
170
169
  this.scheduled.push({
171
170
  input: args,
172
171
  promise,
@@ -1,4 +1,9 @@
1
- import { type AwaitRet, makePromise } from "./util.ts";
1
+ import {
2
+ type AnyFn,
3
+ type AwaitRet,
4
+ makePromise,
5
+ type PromiseHandle,
6
+ } from "./util.ts";
2
7
 
3
8
  /**
4
9
  * An async event wrapper that is guaranteed to:
@@ -78,8 +83,7 @@ import { type AwaitRet, makePromise } from "./util.ts";
78
83
  * });
79
84
  * ```
80
85
  */
81
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
82
- export function debounce<TFn extends (...args: any[]) => any>({
86
+ export function debounce<TFn extends AnyFn>({
83
87
  fn,
84
88
  interval,
85
89
  disregardExecutionTime,
@@ -114,14 +118,10 @@ export type DebounceConstructor<TFn> = {
114
118
  disregardExecutionTime?: boolean;
115
119
  };
116
120
 
117
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
118
- class DebounceImpl<TFn extends (...args: any[]) => any> {
121
+ class DebounceImpl<TFn extends AnyFn> {
119
122
  private idle: boolean;
120
- private next?: {
123
+ private next?: PromiseHandle<AwaitRet<TFn>> & {
121
124
  args: Parameters<TFn>;
122
- promise: Promise<AwaitRet<TFn>>;
123
- resolve: (result: AwaitRet<TFn>) => void;
124
- reject: (error: unknown) => void;
125
125
  };
126
126
  constructor(
127
127
  private fn: TFn,
package/src/sync/index.ts CHANGED
@@ -4,13 +4,19 @@
4
4
  *
5
5
  * @module
6
6
  */
7
- export { serial, type SerialConstructor } from "./serial.ts";
8
- export { latest, type LatestConstructor } from "./latest.ts";
7
+ export {
8
+ serial,
9
+ type SerialConstructor,
10
+ type SerialEventCancelCallback,
11
+ type SerialCancelToken,
12
+ } from "./serial.ts";
13
+ export { latest, type LatestConstructor, type UpdateArgsFn } from "./latest.ts";
9
14
  export { debounce, type DebounceConstructor } from "./debounce.ts";
10
15
  export { batch, type BatchConstructor } from "./batch.ts";
11
- export { cell, type CellConstructor, type Cell } from "./cell.ts";
12
- export { persist, type PersistConstructor, type Persist } from "./persist.ts";
13
16
  export { once, type OnceConstructor } from "./once.ts";
14
17
 
18
+ // types
19
+ export type { AnyFn, AwaitRet } from "./util.ts";
20
+
15
21
  // unstable
16
22
  export { RwLock } from "./RwLock.ts";
@@ -1,18 +1,64 @@
1
- import { expect, test } from "vitest";
1
+ import { expect, test, describe } from "vitest";
2
2
 
3
3
  import { latest } from "./latest.ts";
4
4
 
5
- test("latest", async () => {
6
- let counter = 0;
5
+ describe("latest", () => {
6
+ test("returns latest result", async () => {
7
+ let counter = 0;
7
8
 
8
- const execute = latest({
9
- fn: () => {
10
- return ++counter;
11
- },
9
+ const execute = latest({
10
+ fn: () => {
11
+ return ++counter;
12
+ },
13
+ });
14
+
15
+ const result1 = execute();
16
+ const result2 = execute();
17
+ expect(await result1).toStrictEqual(2);
18
+ expect(await result2).toStrictEqual(2);
19
+ });
20
+
21
+ test("uses are args equal, args are equal", async () => {
22
+ let counter = 0;
23
+
24
+ const execute = latest({
25
+ fn: async (name: string) => {
26
+ ++counter;
27
+ await new Promise((resolve) => setTimeout(resolve, 50));
28
+ return "hello " + name;
29
+ },
30
+
31
+ areArgsEqual: ([nameA], [nameB]) => nameA === nameB,
32
+ });
33
+
34
+ const result1 = execute("foo");
35
+ const result2 = execute("foo");
36
+ // vi.advanceTimersByTime(50);
37
+ expect(await result1).toStrictEqual("hello foo");
38
+ expect(await result2).toStrictEqual("hello foo");
39
+ // should only be called once
40
+ expect(counter).toEqual(1);
12
41
  });
13
42
 
14
- const result1 = execute();
15
- const result2 = execute();
16
- expect(await result1).toStrictEqual(2);
17
- expect(await result2).toStrictEqual(2);
43
+ // this test doesn't pass with fake timers for some reason
44
+ test("uses are args equal, args are not equal", async () => {
45
+ let counter = 0;
46
+
47
+ const execute = latest({
48
+ fn: async (name: string) => {
49
+ ++counter;
50
+ await new Promise((resolve) => setTimeout(resolve, 50));
51
+ return "hello " + name;
52
+ },
53
+
54
+ areArgsEqual: ([nameA], [nameB]) => nameA === nameB,
55
+ });
56
+
57
+ const result1 = execute("foo");
58
+ const result2 = execute("bar");
59
+ // returns latest - both resolved at same time
60
+ expect(await result1).toStrictEqual("hello bar");
61
+ expect(await result2).toStrictEqual("hello bar");
62
+ expect(counter).toEqual(2);
63
+ });
18
64
  });
@@ -1,4 +1,9 @@
1
- import { makePromise } from "./util.ts";
1
+ import {
2
+ type AnyFn,
3
+ type AwaitRet,
4
+ makePromise,
5
+ type PromiseHandle,
6
+ } from "./util.ts";
2
7
 
3
8
  /**
4
9
  * An async event wrapper that always resolve to the result of the latest
@@ -26,54 +31,119 @@ import { makePromise } from "./util.ts";
26
31
  * console.log(await result1); // 2
27
32
  * console.log(await result2); // 2
28
33
  * ```
34
+ *
35
+ * ## Advanced Usage
36
+ * See the constructor options for more advanced usage, for example,
37
+ * control how arguments are updated when new calls are made.
29
38
  */
30
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
- export function latest<TFn extends (...args: any[]) => any>({
39
+ export function latest<TFn extends AnyFn>({
32
40
  fn,
41
+ areArgsEqual,
42
+ updateArgs,
33
43
  }: LatestConstructor<TFn>) {
34
- const impl = new LatestImpl(fn);
44
+ const impl = new LatestImpl(fn, areArgsEqual, updateArgs);
35
45
  return (...args: Parameters<TFn>) => impl.invoke(...args);
36
46
  }
37
47
 
38
- export type LatestConstructor<TFn> = {
48
+ export type LatestConstructor<TFn extends AnyFn> = {
39
49
  /** Function to be wrapped */
40
50
  fn: TFn;
51
+
52
+ /**
53
+ * Optional function to compare if arguments of 2 calls are equal.
54
+ *
55
+ * By default, separate calls are considered different, and the result
56
+ * of the latest call will be returned. However, if the function is pure,
57
+ * and the argument of a new call is the same as the call being executed,
58
+ * then the result of the call being executed will be returned. In other words,
59
+ * the new call will not result in another execution of the function.
60
+ */
61
+ areArgsEqual?: (a: Parameters<TFn>, b: Parameters<TFn>) => boolean;
62
+
63
+ /**
64
+ * Optional function to update the arguments.
65
+ *
66
+ * By default, when new calls are made while the previous call is being executed,
67
+ * The function will be executed again with the latest arguments. This function
68
+ * is used to change this behavior and is called when new calls are made. In other words,
69
+ * the default value for this function is `(_current, _middle, latest) => latest`.
70
+ *
71
+ * The arguments are:
72
+ * - `current`: The arguments of the call currently being executed
73
+ * - `latest`: The argument of this new call
74
+ * - `middle`: If more than one call is made while the previous call is being executed,
75
+ * this array contains arguments of the calls between `current` and `latest`
76
+ * - `next`: This is the returned value of the previous call to updateArgs, i.e. the args
77
+ * to be executed next.
78
+ *
79
+ */
80
+ updateArgs?: UpdateArgsFn<TFn>;
41
81
  };
42
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
- export class LatestImpl<TFn extends (...args: any[]) => any> {
44
- private hasNewer: boolean;
45
- private pending?: {
46
- promise: Promise<Awaited<ReturnType<TFn>>>;
47
- resolve: (result: Awaited<ReturnType<TFn>>) => void;
48
- reject: (error: unknown) => void;
49
- };
82
+ export type UpdateArgsFn<TFn extends AnyFn> = (
83
+ current: Parameters<TFn>,
84
+ middle: Parameters<TFn>[],
85
+ latest: Parameters<TFn>,
86
+ next: Parameters<TFn> | undefined,
87
+ ) => Parameters<TFn>;
88
+ export class LatestImpl<TFn extends AnyFn> {
89
+ private pending?: PromiseHandle<AwaitRet<TFn>>;
90
+
91
+ /** current arguments. undefined means no current call */
92
+ private currentArgs?: Parameters<TFn>;
93
+ /** next arguments. undefined means no newer call */
94
+ private nextArgs?: Parameters<TFn>;
95
+
96
+ private middleArgs: Parameters<TFn>[];
50
97
 
51
- constructor(private fn: TFn) {
52
- this.hasNewer = false;
98
+ private areArgsEqual: (a: Parameters<TFn>, b: Parameters<TFn>) => boolean;
99
+ private updateArgs: UpdateArgsFn<TFn>;
100
+
101
+ constructor(
102
+ private fn: TFn,
103
+ areArgsEqual?: (a: Parameters<TFn>, b: Parameters<TFn>) => boolean,
104
+ updateArgs?: UpdateArgsFn<TFn>,
105
+ ) {
106
+ this.middleArgs = [];
107
+ this.areArgsEqual = areArgsEqual || (() => false);
108
+ this.updateArgs = updateArgs || ((_current, _middle, latest) => latest);
53
109
  }
54
110
 
55
- public async invoke(
56
- ...args: Parameters<TFn>
57
- ): Promise<Awaited<ReturnType<TFn>>> {
111
+ public async invoke(...args: Parameters<TFn>): Promise<AwaitRet<TFn>> {
58
112
  if (this.pending) {
59
- this.hasNewer = true;
113
+ // pending means currentArgs is not undefined
114
+ const currentArgs = this.currentArgs as Parameters<TFn>;
115
+ const nextArgs = this.updateArgs(
116
+ currentArgs,
117
+ this.middleArgs,
118
+ args,
119
+ this.nextArgs,
120
+ );
121
+ if (this.areArgsEqual(nextArgs, currentArgs)) {
122
+ // do not schedule new call
123
+ this.nextArgs = undefined;
124
+ } else {
125
+ this.nextArgs = nextArgs;
126
+ }
60
127
  return this.pending.promise;
61
128
  }
62
- this.pending = makePromise<Awaited<ReturnType<TFn>>>();
129
+
130
+ // assign to next args to make the loop cleaner
131
+ this.nextArgs = args;
132
+
133
+ this.pending = makePromise();
63
134
  let error = undefined;
64
135
  let result;
65
- while (true) {
66
- this.hasNewer = false;
136
+ while (this.nextArgs) {
137
+ this.currentArgs = this.nextArgs;
138
+ this.nextArgs = undefined;
67
139
  try {
68
140
  const fn = this.fn;
69
- result = await fn(...args);
141
+ result = await fn(...this.currentArgs);
70
142
  } catch (e) {
71
143
  error = e;
72
144
  }
73
- if (!this.hasNewer) {
74
- break;
75
- }
76
145
  }
146
+ this.currentArgs = undefined;
77
147
  const pending = this.pending;
78
148
  this.pending = undefined;
79
149
  if (error) {
package/src/sync/once.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { makePromise } from "./util";
1
+ import { type AnyFn, type AwaitRet, makePromise } from "./util.ts";
2
2
 
3
3
  /**
4
4
  * An async event wrapper that ensures an async initialization is only ran once.
@@ -66,13 +66,9 @@ import { makePromise } from "./util";
66
66
  * This is not an issue if the resource doesn't leak other resources,
67
67
  * since it will eventually be GC'd.
68
68
  */
69
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
- export function once<TFn extends (...args: any[]) => any>({
71
- fn,
72
- }: OnceConstructor<TFn>) {
69
+ export function once<TFn extends AnyFn>({ fn }: OnceConstructor<TFn>) {
73
70
  const impl = new OnceImpl(fn);
74
- return (...args: Parameters<TFn>): Promise<Awaited<ReturnType<TFn>>> =>
75
- impl.invoke(...args);
71
+ return (...args: Parameters<TFn>) => impl.invoke(...args);
76
72
  }
77
73
 
78
74
  export type OnceConstructor<TFn> = {
@@ -80,22 +76,18 @@ export type OnceConstructor<TFn> = {
80
76
  fn: TFn;
81
77
  };
82
78
 
83
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
- export class OnceImpl<TFn extends (...args: any[]) => any> {
85
- private promise: Promise<Awaited<ReturnType<TFn>>> | undefined;
79
+ export class OnceImpl<TFn extends AnyFn> {
80
+ private promise: Promise<AwaitRet<TFn>> | undefined;
86
81
 
87
82
  constructor(private fn: TFn) {
88
83
  this.fn = fn;
89
84
  }
90
85
 
91
- public async invoke(
92
- ...args: Parameters<TFn>
93
- ): Promise<Awaited<ReturnType<TFn>>> {
86
+ public async invoke(...args: Parameters<TFn>): Promise<AwaitRet<TFn>> {
94
87
  if (this.promise) {
95
88
  return this.promise;
96
89
  }
97
- const { promise, resolve, reject } =
98
- makePromise<Awaited<ReturnType<TFn>>>();
90
+ const { promise, resolve, reject } = makePromise<AwaitRet<TFn>>();
99
91
  this.promise = promise;
100
92
  try {
101
93
  const result = await this.fn(...args);
@@ -1,4 +1,5 @@
1
1
  import type { Result } from "../result/index.ts";
2
+ import type { AnyFn } from "./util.ts";
2
3
 
3
4
  /**
4
5
  * An async event wrapper that is cancelled when a new one starts.
@@ -132,8 +133,7 @@ import type { Result } from "../result/index.ts";
132
133
  * If the underlying function throws, the exception will be re-thrown to the caller.
133
134
  */
134
135
 
135
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
136
- export function serial<TFn extends (...args: any[]) => any>({
136
+ export function serial<TFn extends AnyFn>({
137
137
  fn,
138
138
  onCancel,
139
139
  }: SerialConstructor<TFn>) {
@@ -157,8 +157,7 @@ export type SerialConstructor<TFn> = {
157
157
  onCancel?: SerialEventCancelCallback;
158
158
  };
159
159
 
160
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
161
- class SerialImpl<TFn extends (...args: any[]) => any> {
160
+ class SerialImpl<TFn extends AnyFn> {
162
161
  private serial: SerialId;
163
162
  private fn: SerialFnCreator<TFn>;
164
163
  private onCancel: SerialEventCancelCallback;
package/src/sync/util.ts CHANGED
@@ -21,8 +21,14 @@ export const makePromise = <T>(): {
21
21
  };
22
22
  };
23
23
 
24
+ export type PromiseHandle<T> = ReturnType<typeof makePromise<T>>;
25
+
24
26
  /** Shorthand for Awaited<ReturnType<T>> */
25
27
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
28
  export type AwaitRet<T> = T extends (...args: any[]) => infer R
27
29
  ? Awaited<R>
28
30
  : never;
31
+
32
+ /** Type for any function */
33
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
+ export type AnyFn = (...args: any[]) => any;
File without changes
File without changes