Skip to content

Commit 5cf6b2d

Browse files
Introduce Promise Helpers (#2102)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 2afb07a commit 5cf6b2d

25 files changed

+420
-347
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@whatwg-node/cookie-store": patch
3+
---
4+
dependencies updates:
5+
- Added dependency [`@whatwg-node/promise-helpers@^0.0.0` ↗︎](https://www.npmjs.com/package/@whatwg-node/promise-helpers/v/0.0.0) (to `dependencies`)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@whatwg-node/disposablestack": patch
3+
---
4+
dependencies updates:
5+
- Added dependency [`@whatwg-node/promise-helpers@^0.0.0` ↗︎](https://www.npmjs.com/package/@whatwg-node/promise-helpers/v/0.0.0) (to `dependencies`)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@whatwg-node/node-fetch": patch
3+
---
4+
dependencies updates:
5+
- Added dependency [`@whatwg-node/promise-helpers@^0.0.0` ↗︎](https://www.npmjs.com/package/@whatwg-node/promise-helpers/v/0.0.0) (to `dependencies`)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@whatwg-node/server": patch
3+
---
4+
dependencies updates:
5+
- Added dependency [`@whatwg-node/promise-helpers@^0.0.0` ↗︎](https://www.npmjs.com/package/@whatwg-node/promise-helpers/v/0.0.0) (to `dependencies`)

.changeset/green-rocks-bow.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@whatwg-node/promise-helpers': patch
3+
---
4+
5+
New promise helpers

deno.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"fetchache": "./packages/fetchache/src/index.ts",
99
"@whatwg-node/node-fetch": "./packages/node-fetch/src/index.ts",
1010
"@whatwg-node/server": "./packages/server/src/index.ts",
11-
"@whatwg-node/server-plugin-cookies": "./packages/server-plugin-cookies/src/index.ts"
11+
"@whatwg-node/server-plugin-cookies": "./packages/server-plugin-cookies/src/index.ts",
12+
"@whatwg-node/promise-helpers": "./packages/promise-helpers/src/index.ts"
1213
}
1314
}

packages/cookie-store/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
},
3535
"typings": "dist/typings/index.d.ts",
3636
"dependencies": {
37+
"@whatwg-node/promise-helpers": "^0.0.0",
3738
"tslib": "^2.6.3"
3839
},
3940
"publishConfig": {

packages/cookie-store/src/CookieStore.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { fakePromise } from '@whatwg-node/promise-helpers';
12
import { CookieChangeEvent } from './CookieChangeEvent.js';
23
import { parse } from './parse.js';
34
import {
@@ -21,15 +22,15 @@ export class CookieStore extends EventTarget {
2122
this.cookieMap = parse(cookieString);
2223
}
2324

24-
async get(
25+
get(
2526
init?: CookieStoreGetOptions['name'] | CookieStoreGetOptions | undefined,
2627
): Promise<Cookie | undefined> {
2728
if (init == null) {
2829
throw new TypeError('CookieStoreGetOptions must not be empty');
2930
} else if (init instanceof Object && !Object.keys(init).length) {
3031
throw new TypeError('CookieStoreGetOptions must not be empty');
3132
}
32-
return (await this.getAll(init))[0];
33+
return this.getAll(init).then(cookies => cookies[0]);
3334
}
3435

3536
async set(init: CookieListItem | string, possibleValue?: string): Promise<void> {
@@ -95,21 +96,21 @@ export class CookieStore extends EventTarget {
9596
}
9697
}
9798

98-
async getAll(init?: CookieStoreGetOptions['name'] | CookieStoreGetOptions): Promise<Cookie[]> {
99+
getAll(init?: CookieStoreGetOptions['name'] | CookieStoreGetOptions): Promise<Cookie[]> {
99100
const cookies = Array.from(this.cookieMap.values());
100101
if (init == null || Object.keys(init).length === 0) {
101-
return cookies;
102+
return fakePromise(cookies);
102103
}
103104
let name: string | undefined;
104105
if (typeof init === 'string') {
105106
name = init as string;
106107
} else {
107108
name = init.name;
108109
}
109-
return cookies.filter(cookie => cookie.name === name);
110+
return fakePromise(cookies.filter(cookie => cookie.name === name));
110111
}
111112

112-
async delete(init: CookieStoreDeleteOptions['name'] | CookieStoreDeleteOptions): Promise<void> {
113+
delete(init: CookieStoreDeleteOptions['name'] | CookieStoreDeleteOptions): Promise<void> {
113114
const item: CookieListItem = {
114115
name: '',
115116
value: '',
@@ -128,6 +129,6 @@ export class CookieStore extends EventTarget {
128129

129130
item.expires = 0;
130131

131-
await this.set(item);
132+
return this.set(item);
132133
}
133134
}

packages/disposablestack/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
},
3535
"typings": "dist/typings/index.d.ts",
3636
"dependencies": {
37+
"@whatwg-node/promise-helpers": "^0.0.0",
3738
"tslib": "^2.6.3"
3839
},
3940
"publishConfig": {

packages/disposablestack/src/AsyncDisposableStack.ts

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
import { handleMaybePromiseLike, MaybePromiseLike } from '@whatwg-node/promise-helpers';
12
import { PonyfillSuppressedError } from './SupressedError.js';
23
import { DisposableSymbols } from './symbols.js';
3-
import { isAsyncDisposable, isSyncDisposable, MaybePromise } from './utils.js';
4+
import { isAsyncDisposable, isSyncDisposable } from './utils.js';
45

56
const SuppressedError = globalThis.SuppressedError || PonyfillSuppressedError;
67

78
export class PonyfillAsyncDisposableStack implements AsyncDisposableStack {
8-
private callbacks: (() => MaybePromise<void>)[] = [];
9+
private callbacks: (() => MaybePromiseLike<void>)[] = [];
910
get disposed(): boolean {
1011
return this.callbacks.length === 0;
1112
}
@@ -19,14 +20,14 @@ export class PonyfillAsyncDisposableStack implements AsyncDisposableStack {
1920
return value;
2021
}
2122

22-
adopt<T>(value: T, onDisposeAsync: (value: T) => MaybePromise<void>): T {
23+
adopt<T>(value: T, onDisposeAsync: (value: T) => MaybePromiseLike<void>): T {
2324
if (onDisposeAsync) {
2425
this.callbacks.push(() => onDisposeAsync(value));
2526
}
2627
return value;
2728
}
2829

29-
defer(onDisposeAsync: () => MaybePromise<void>): void {
30+
defer(onDisposeAsync: () => MaybePromiseLike<void>): void {
3031
if (onDisposeAsync) {
3132
this.callbacks.push(onDisposeAsync);
3233
}
@@ -45,24 +46,17 @@ export class PonyfillAsyncDisposableStack implements AsyncDisposableStack {
4546

4647
private _error?: Error | undefined;
4748

48-
private _iterateCallbacks(): MaybePromise<void> {
49+
private _iterateCallbacks(): MaybePromiseLike<void> {
4950
const cb = this.callbacks.pop();
5051
if (cb) {
51-
try {
52-
const res$ = cb();
53-
if (res$?.then) {
54-
return res$.then(
55-
() => this._iterateCallbacks(),
56-
error => {
57-
this._error = this._error ? new SuppressedError(error, this._error) : error;
58-
return this._iterateCallbacks();
59-
},
60-
);
61-
}
62-
} catch (error: any) {
63-
this._error = this._error ? new SuppressedError(error, this._error) : error;
64-
}
65-
return this._iterateCallbacks();
52+
return handleMaybePromiseLike(
53+
cb,
54+
() => this._iterateCallbacks(),
55+
error => {
56+
this._error = this._error ? new SuppressedError(error, this._error) : error;
57+
return this._iterateCallbacks();
58+
},
59+
);
6660
}
6761
}
6862

packages/disposablestack/src/utils.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,3 @@ export function isSyncDisposable(obj: any): obj is Disposable {
77
export function isAsyncDisposable(obj: any): obj is AsyncDisposable {
88
return obj?.[DisposableSymbols.asyncDispose] != null;
99
}
10-
11-
export type MaybePromise<T> = T | PromiseLike<T>;

packages/disposablestack/tests/disposablestack.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { describe, expect, it, jest } from '@jest/globals';
2+
import { MaybePromise } from '@whatwg-node/promise-helpers';
23
import { AsyncDisposableStack, DisposableStack, DisposableSymbols, patchSymbols } from '../src';
34

45
function createTestCases<
@@ -11,7 +12,7 @@ function createTestCases<
1112
): Record<
1213
string,
1314
{
14-
run(stack: TStack, disposeFn: jest.Mock): void | Promise<void>;
15+
run(stack: TStack, disposeFn: jest.Mock): MaybePromise<void>;
1516
check(disposeFn: jest.Mock): void;
1617
}
1718
> {

packages/node-fetch/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"typings": "dist/typings/index.d.ts",
3636
"dependencies": {
3737
"@whatwg-node/disposablestack": "^0.0.5",
38+
"@whatwg-node/promise-helpers": "^0.0.0",
3839
"busboy": "^1.6.0",
3940
"tslib": "^2.6.3"
4041
},

packages/node-fetch/src/fetchCurl.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,10 @@ import { Buffer } from 'node:buffer';
22
import { PassThrough, Readable } from 'node:stream';
33
import { pipeline } from 'node:stream/promises';
44
import { rootCertificates } from 'node:tls';
5+
import { createDeferredPromise } from '@whatwg-node/promise-helpers';
56
import { PonyfillRequest } from './Request.js';
67
import { PonyfillResponse } from './Response.js';
7-
import {
8-
createDeferredPromise,
9-
defaultHeadersSerializer,
10-
isNodeReadable,
11-
shouldRedirect,
12-
} from './utils.js';
8+
import { defaultHeadersSerializer, isNodeReadable, shouldRedirect } from './utils.js';
139

1410
export function fetchCurl<TResponseJSON = any, TRequestJSON = any>(
1511
fetchRequest: PonyfillRequest<TRequestJSON>,

packages/node-fetch/src/utils.ts

Lines changed: 1 addition & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -25,43 +25,7 @@ export function defaultHeadersSerializer(
2525
return headerArray;
2626
}
2727

28-
function isPromise<T>(val: T | Promise<T>): val is Promise<T> {
29-
return (val as any)?.then != null;
30-
}
31-
32-
export function fakePromise<T>(value: T): Promise<T> {
33-
if (isPromise(value)) {
34-
return value;
35-
}
36-
// Write a fake promise to avoid the promise constructor
37-
// being called with `new Promise` in the browser.
38-
return {
39-
then(resolve: (value: T) => any) {
40-
if (resolve) {
41-
const callbackResult = resolve(value);
42-
if (isPromise(callbackResult)) {
43-
return callbackResult;
44-
}
45-
return fakePromise(callbackResult);
46-
}
47-
return this;
48-
},
49-
catch() {
50-
return this;
51-
},
52-
finally(cb) {
53-
if (cb) {
54-
const callbackResult = cb();
55-
if (isPromise(callbackResult)) {
56-
return callbackResult.then(() => value);
57-
}
58-
return fakePromise(value);
59-
}
60-
return this;
61-
},
62-
[Symbol.toStringTag]: 'Promise',
63-
};
64-
}
28+
export { fakePromise } from '@whatwg-node/promise-helpers';
6529

6630
export function isArrayBufferView(obj: any): obj is ArrayBufferView {
6731
return obj != null && obj.buffer != null && obj.byteLength != null && obj.byteOffset != null;
@@ -71,30 +35,6 @@ export function isNodeReadable(obj: any): obj is Readable {
7135
return obj != null && obj.pipe != null;
7236
}
7337

74-
export interface DeferredPromise<T = void> {
75-
promise: Promise<T>;
76-
resolve: (value: T) => void;
77-
reject: (reason: any) => void;
78-
}
79-
80-
export function createDeferredPromise<T = void>(): DeferredPromise<T> {
81-
let resolveFn: (value: T) => void;
82-
let rejectFn: (reason: any) => void;
83-
const promise = new Promise<T>(function deferredPromiseExecutor(resolve, reject) {
84-
resolveFn = resolve;
85-
rejectFn = reject;
86-
});
87-
return {
88-
promise,
89-
get resolve() {
90-
return resolveFn;
91-
},
92-
get reject() {
93-
return rejectFn;
94-
},
95-
};
96-
}
97-
9838
export function isIterable(value: any): value is Iterable<unknown> {
9939
return value?.[Symbol.iterator] != null;
10040
}

packages/promise-helpers/package.json

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"name": "@whatwg-node/promise-helpers",
3+
"version": "0.0.0",
4+
"type": "module",
5+
"description": "Promise helpers",
6+
"repository": {
7+
"type": "git",
8+
"url": "ardatan/whatwg-node",
9+
"directory": "packages/promise-helpers"
10+
},
11+
"author": "Arda TANRIKULU <[email protected]>",
12+
"license": "MIT",
13+
"engines": {
14+
"node": ">=18.0.0"
15+
},
16+
"main": "dist/cjs/index.js",
17+
"module": "dist/esm/index.js",
18+
"exports": {
19+
".": {
20+
"require": {
21+
"types": "./dist/typings/index.d.cts",
22+
"default": "./dist/cjs/index.js"
23+
},
24+
"import": {
25+
"types": "./dist/typings/index.d.ts",
26+
"default": "./dist/esm/index.js"
27+
},
28+
"default": {
29+
"types": "./dist/typings/index.d.ts",
30+
"default": "./dist/esm/index.js"
31+
}
32+
},
33+
"./package.json": "./package.json"
34+
},
35+
"typings": "dist/typings/index.d.ts",
36+
"dependencies": {
37+
"tslib": "^2.6.3"
38+
},
39+
"publishConfig": {
40+
"directory": "dist",
41+
"access": "public"
42+
},
43+
"sideEffects": false,
44+
"buildOptions": {
45+
"input": "./src/index.ts"
46+
},
47+
"typescript": {
48+
"definition": "dist/typings/index.d.ts"
49+
}
50+
}

0 commit comments

Comments
 (0)