@homer0/extend-promise 6.0.2 → 6.0.4
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/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +20 -28
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["type ExtractGeneric<Type> = Type extends Promise<infer X> ? X : never;\n\ntype ExtendedPromise<T extends Promise<unknown>, P, V = ExtractGeneric<T>> = Omit<\n T,\n 'then' | 'catch' | 'finally'\n> &\n P & {\n then: (...args: Parameters<T['then']>) => ExtendedPromise<Promise<V>, P>;\n catch: (...args: Parameters<T['catch']>) => ExtendedPromise<Promise<V>, P>;\n finally: (...args: Parameters<T['finally']>) => ExtendedPromise<Promise<V>, P>;\n };\ntype GenericFn = (...args: unknown[]) => unknown;\ntype ExtendedFn<F extends GenericFn, P> = F & P;\n\n/**\n * Helper class that creates a proxy for a {@link Promise} in order to add custom\n * properties.\n *\n * The only reason this class exists is so it can \"scope\" the necessary methods to extend\n * {@link Promise} and avoid workarounds in order to declare them, as they need to call\n * themselves recursively.\n */\nclass PromiseExtender<T extends Promise<unknown>, P extends Record<string, unknown>> {\n
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["type ExtractGeneric<Type> = Type extends Promise<infer X> ? X : never;\n\ntype ExtendedPromise<T extends Promise<unknown>, P, V = ExtractGeneric<T>> = Omit<\n T,\n 'then' | 'catch' | 'finally'\n> &\n P & {\n then: (...args: Parameters<T['then']>) => ExtendedPromise<Promise<V>, P>;\n catch: (...args: Parameters<T['catch']>) => ExtendedPromise<Promise<V>, P>;\n finally: (...args: Parameters<T['finally']>) => ExtendedPromise<Promise<V>, P>;\n };\ntype GenericFn = (...args: unknown[]) => unknown;\ntype ExtendedFn<F extends GenericFn, P> = F & P;\n\n/**\n * Helper class that creates a proxy for a {@link Promise} in order to add custom\n * properties.\n *\n * The only reason this class exists is so it can \"scope\" the necessary methods to extend\n * {@link Promise} and avoid workarounds in order to declare them, as they need to call\n * themselves recursively.\n */\nclass PromiseExtender<T extends Promise<unknown>, P extends Record<string, unknown>> {\n /** The proxied promise. */\n readonly promise: ExtendedPromise<T, P>;\n /**\n * @param promise The promise to extend.\n * @param properties A dictionary of custom properties to _inject_ in the promise chain.\n */\n constructor(promise: T, properties: P) {\n this.promise = this.extend(promise, properties);\n }\n /**\n * The method that actually extends a promise: it creates a proxy of the promise in\n * order to intercept the getters so it can return the custom properties if requested,\n * and return new proxies when `then`, `catch` and `finally` are called; the reason new\n * proxies are created is because those methods return new promises, and without being\n * proxied, the custom properties would be lost.\n *\n * @param promise The promise to proxy.\n * @param properties A dictionary of custom properties to _inject_ in the promise chain.\n * @throws {Error} If `promise` is not a valid instance of {@link Promise}.\n * @throws {Error} If `properties` is not an object or if it doesn't have any\n * properties.\n */\n extend(promise: T, properties: P): ExtendedPromise<T, P> {\n if (!(promise instanceof Promise)) {\n throw new Error(\"'promise' must be a valid Promise instance\");\n }\n\n if (!properties || !Object.keys(properties).length) {\n throw new Error(\"'properties' must be an object with at least one key\");\n }\n\n const extended = new Proxy(promise, {\n /**\n * This is a trap for when something is trying to read/access a property for the\n * promise. The function first validates if it's one of the functions\n * (`then`/`catch`/`finally`) in order to return a proxied function; then, if it's\n * another function (one that doesn't return another promise), it just calls the\n * original; finally, before doing a fallback to the original promise, it checks if\n * it's one of the custom properties that exented the promise.\n *\n * @param target The original promise.\n * @param name The name of the property.\n */\n get: (target, name) => {\n const targetKey = name as keyof typeof target;\n if (\n typeof name === 'string' &&\n ['then', 'catch', 'finally'].includes(name) &&\n name in target\n ) {\n const fn = target[targetKey] as unknown as GenericFn;\n return this.extendFunction(fn.bind(target), properties);\n }\n\n if (name in target) {\n const possibleFn = target[targetKey] as unknown as Record<string, unknown>;\n if (typeof possibleFn['bind'] === 'function') {\n return possibleFn['bind'](target);\n }\n }\n\n const key = name as keyof P;\n return properties[key];\n },\n });\n\n return extended as unknown as ExtendedPromise<T, P>;\n }\n /**\n * Creates a proxy for a promise function (`then`/`catch`/`finally`) so the returned\n * promise can also be extended.\n *\n * @param fn The promise function to proxy.\n * @param properties A dictionary of custom properties to _inject_ in the promise chain.\n */\n protected extendFunction<F extends GenericFn>(fn: F, properties: P): ExtendedFn<F, P> {\n return new Proxy(fn, {\n /**\n * This is a trap for when a function gets called (remember this gets used for\n * `then`/ `catch`/`finally`); it processes the result using the oringinal function\n * and since promise methods return a promise, instead of returning the original\n * result, it returns an _extended_ version of it.\n *\n * @param target The original function.\n * @param thisArg The promise the function belongs to.\n * @param args The list of arguments sent to the trap.\n */\n apply: (target, thisArg, args) => {\n const value = target.bind(thisArg)(...args);\n return this.extend(value as T, properties);\n },\n }) as ExtendedFn<F, P>;\n }\n}\n\n/**\n * Extends a {@link Promise} by injecting custom properties using a {@link Proxy}. The\n * custom properties will be available on the promise chain no matter how many `then`s,\n * `catch`s or `finally`s are added.\n *\n * @param promise The promise to extend.\n * @param properties A dictionary of custom properties to _inject_ in the promise chain.\n * @throws {Error} If `promise` is not a valid instance of {@link Promise}.\n * @throws {Error} If `properties` is not an object or if it doesn't have any\n * properties.\n */\nexport const extendPromise = <\n T extends Promise<unknown>,\n P extends Record<string, unknown>,\n>(\n promise: T,\n properties: P,\n): ExtendedPromise<T, P> => new PromiseExtender(promise, properties).promise;\n"],"mappings":"AAsBA,IAAMA,EAAN,KAAqF,CAE1E,QAKT,YAAYC,EAAYC,EAAe,CACrC,KAAK,QAAU,KAAK,OAAOD,EAASC,CAAU,CAChD,CAcA,OAAOD,EAAYC,EAAsC,CACvD,GAAI,EAAED,aAAmB,SACvB,MAAM,IAAI,MAAM,4CAA4C,EAG9D,GAAI,CAACC,GAAc,CAAC,OAAO,KAAKA,CAAU,EAAE,OAC1C,MAAM,IAAI,MAAM,sDAAsD,EAsCxE,OAnCiB,IAAI,MAAMD,EAAS,CAYlC,IAAK,CAACE,EAAQC,IAAS,CACrB,IAAMC,EAAYD,EAClB,GACE,OAAOA,GAAS,UAChB,CAAC,OAAQ,QAAS,SAAS,EAAE,SAASA,CAAI,GAC1CA,KAAQD,EACR,CACA,IAAMG,EAAKH,EAAOE,CAAS,EAC3B,OAAO,KAAK,eAAeC,EAAG,KAAKH,CAAM,EAAGD,CAAU,CACxD,CAEA,GAAIE,KAAQD,EAAQ,CAClB,IAAMI,EAAaJ,EAAOE,CAAS,EACnC,GAAI,OAAOE,EAAW,MAAY,WAChC,OAAOA,EAAW,KAAQJ,CAAM,CAEpC,CAGA,OAAOD,EADKE,CACS,CACvB,CACF,CAAC,CAGH,CAQU,eAAoCE,EAAOJ,EAAiC,CACpF,OAAO,IAAI,MAAMI,EAAI,CAWnB,MAAO,CAACH,EAAQK,EAASC,IAAS,CAChC,IAAMC,EAAQP,EAAO,KAAKK,CAAO,EAAE,GAAGC,CAAI,EAC1C,OAAO,KAAK,OAAOC,EAAYR,CAAU,CAC3C,CACF,CAAC,CACH,CACF,EAaaS,EAAgB,CAI3BV,EACAC,IAC0B,IAAIF,EAAgBC,EAASC,CAAU,EAAE","names":["PromiseExtender","promise","properties","target","name","targetKey","fn","possibleFn","thisArg","args","value","extendPromise"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@homer0/extend-promise",
|
|
3
3
|
"description": "Extends a promise with custom properties",
|
|
4
|
-
"version": "6.0.
|
|
4
|
+
"version": "6.0.4",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/homer0/packages.git",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"src/"
|
|
36
36
|
],
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@homer0/oxlint-config": "0.
|
|
39
|
-
"@homer0/tsconfig": "5.0.
|
|
38
|
+
"@homer0/oxlint-config": "1.0.2",
|
|
39
|
+
"@homer0/tsconfig": "5.0.1",
|
|
40
40
|
"@vitest/coverage-v8": "catalog:",
|
|
41
41
|
"oxlint": "catalog:",
|
|
42
42
|
"tsup": "catalog:",
|
|
@@ -56,5 +56,5 @@
|
|
|
56
56
|
"build": "tsup",
|
|
57
57
|
"prepublishOnly": "pnpm run build"
|
|
58
58
|
},
|
|
59
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "e494ba08d9dd4fe6e5d3a9ef54201f3c1f748355"
|
|
60
60
|
}
|
package/src/index.ts
CHANGED
|
@@ -21,14 +21,11 @@ type ExtendedFn<F extends GenericFn, P> = F & P;
|
|
|
21
21
|
* themselves recursively.
|
|
22
22
|
*/
|
|
23
23
|
class PromiseExtender<T extends Promise<unknown>, P extends Record<string, unknown>> {
|
|
24
|
-
/**
|
|
25
|
-
* The proxied promise.
|
|
26
|
-
*/
|
|
24
|
+
/** The proxied promise. */
|
|
27
25
|
readonly promise: ExtendedPromise<T, P>;
|
|
28
26
|
/**
|
|
29
|
-
* @param promise
|
|
30
|
-
* @param properties
|
|
31
|
-
* chain.
|
|
27
|
+
* @param promise The promise to extend.
|
|
28
|
+
* @param properties A dictionary of custom properties to _inject_ in the promise chain.
|
|
32
29
|
*/
|
|
33
30
|
constructor(promise: T, properties: P) {
|
|
34
31
|
this.promise = this.extend(promise, properties);
|
|
@@ -40,12 +37,11 @@ class PromiseExtender<T extends Promise<unknown>, P extends Record<string, unkno
|
|
|
40
37
|
* proxies are created is because those methods return new promises, and without being
|
|
41
38
|
* proxied, the custom properties would be lost.
|
|
42
39
|
*
|
|
43
|
-
* @param promise
|
|
44
|
-
* @param properties
|
|
45
|
-
* chain.
|
|
40
|
+
* @param promise The promise to proxy.
|
|
41
|
+
* @param properties A dictionary of custom properties to _inject_ in the promise chain.
|
|
46
42
|
* @throws {Error} If `promise` is not a valid instance of {@link Promise}.
|
|
47
43
|
* @throws {Error} If `properties` is not an object or if it doesn't have any
|
|
48
|
-
*
|
|
44
|
+
* properties.
|
|
49
45
|
*/
|
|
50
46
|
extend(promise: T, properties: P): ExtendedPromise<T, P> {
|
|
51
47
|
if (!(promise instanceof Promise)) {
|
|
@@ -59,15 +55,14 @@ class PromiseExtender<T extends Promise<unknown>, P extends Record<string, unkno
|
|
|
59
55
|
const extended = new Proxy(promise, {
|
|
60
56
|
/**
|
|
61
57
|
* This is a trap for when something is trying to read/access a property for the
|
|
62
|
-
* promise.
|
|
63
|
-
* The function first validates if it's one of the functions
|
|
58
|
+
* promise. The function first validates if it's one of the functions
|
|
64
59
|
* (`then`/`catch`/`finally`) in order to return a proxied function; then, if it's
|
|
65
60
|
* another function (one that doesn't return another promise), it just calls the
|
|
66
61
|
* original; finally, before doing a fallback to the original promise, it checks if
|
|
67
62
|
* it's one of the custom properties that exented the promise.
|
|
68
63
|
*
|
|
69
|
-
* @param target
|
|
70
|
-
* @param name
|
|
64
|
+
* @param target The original promise.
|
|
65
|
+
* @param name The name of the property.
|
|
71
66
|
*/
|
|
72
67
|
get: (target, name) => {
|
|
73
68
|
const targetKey = name as keyof typeof target;
|
|
@@ -98,22 +93,20 @@ class PromiseExtender<T extends Promise<unknown>, P extends Record<string, unkno
|
|
|
98
93
|
* Creates a proxy for a promise function (`then`/`catch`/`finally`) so the returned
|
|
99
94
|
* promise can also be extended.
|
|
100
95
|
*
|
|
101
|
-
* @param fn
|
|
102
|
-
* @param properties
|
|
103
|
-
* chain.
|
|
96
|
+
* @param fn The promise function to proxy.
|
|
97
|
+
* @param properties A dictionary of custom properties to _inject_ in the promise chain.
|
|
104
98
|
*/
|
|
105
99
|
protected extendFunction<F extends GenericFn>(fn: F, properties: P): ExtendedFn<F, P> {
|
|
106
100
|
return new Proxy(fn, {
|
|
107
101
|
/**
|
|
108
102
|
* This is a trap for when a function gets called (remember this gets used for
|
|
109
|
-
* `then`/
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* it returns an _extended_ version of it.
|
|
103
|
+
* `then`/ `catch`/`finally`); it processes the result using the oringinal function
|
|
104
|
+
* and since promise methods return a promise, instead of returning the original
|
|
105
|
+
* result, it returns an _extended_ version of it.
|
|
113
106
|
*
|
|
114
|
-
* @param target
|
|
115
|
-
* @param thisArg
|
|
116
|
-
* @param args
|
|
107
|
+
* @param target The original function.
|
|
108
|
+
* @param thisArg The promise the function belongs to.
|
|
109
|
+
* @param args The list of arguments sent to the trap.
|
|
117
110
|
*/
|
|
118
111
|
apply: (target, thisArg, args) => {
|
|
119
112
|
const value = target.bind(thisArg)(...args);
|
|
@@ -128,12 +121,11 @@ class PromiseExtender<T extends Promise<unknown>, P extends Record<string, unkno
|
|
|
128
121
|
* custom properties will be available on the promise chain no matter how many `then`s,
|
|
129
122
|
* `catch`s or `finally`s are added.
|
|
130
123
|
*
|
|
131
|
-
* @param promise
|
|
132
|
-
* @param properties
|
|
133
|
-
* chain.
|
|
124
|
+
* @param promise The promise to extend.
|
|
125
|
+
* @param properties A dictionary of custom properties to _inject_ in the promise chain.
|
|
134
126
|
* @throws {Error} If `promise` is not a valid instance of {@link Promise}.
|
|
135
127
|
* @throws {Error} If `properties` is not an object or if it doesn't have any
|
|
136
|
-
*
|
|
128
|
+
* properties.
|
|
137
129
|
*/
|
|
138
130
|
export const extendPromise = <
|
|
139
131
|
T extends Promise<unknown>,
|