Skip to content

Commit

Permalink
Wait until (#116)
Browse files Browse the repository at this point in the history
* feat(server): waitUntil

* Better tests

* Remove get-port

* Node 14
  • Loading branch information
ardatan committed Sep 12, 2022
1 parent b41ca0a commit 722ffda
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/twenty-papayas-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@whatwg-node/server': minor
---

Implement `waitUntil`
7 changes: 7 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const CI = !!process.env.CI;
const ROOT_DIR = __dirname;
const TSCONFIG = resolve(ROOT_DIR, 'tsconfig.json');
const tsconfig = require(TSCONFIG);
const ESM_PACKAGES = [];

module.exports = {
testEnvironment: 'node',
Expand All @@ -15,6 +16,12 @@ module.exports = {
moduleNameMapper: pathsToModuleNameMapper(tsconfig.compilerOptions.paths, {
prefix: `${ROOT_DIR}/`,
}),
transformIgnorePatterns: [`node_modules/(?!(${ESM_PACKAGES.join('|')})/)`],
transform: {
'^.+\\.mjs?$': 'babel-jest',
'^.+\\.ts?$': 'babel-jest',
'^.+\\.js$': 'babel-jest',
},
collectCoverage: false,
cacheDirectory: resolve(ROOT_DIR, `${CI ? '' : 'node_modules/'}.cache/jest`),
};
44 changes: 39 additions & 5 deletions packages/server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,21 @@ export type ServerAdapter<TServerContext, TBaseObject> = TBaseObject &
ServerAdapterObject<TServerContext>['fetch'] &
ServerAdapterObject<TServerContext>;

function handleWaitUntils(waitUntilPromises: Promise<unknown>[]) {
return Promise.allSettled(waitUntilPromises).then(waitUntils =>
waitUntils.forEach(waitUntil => {
if (waitUntil.status === 'rejected') {
console.error(waitUntil.reason);
}
})
);
}

export function createServerAdapter<
TServerContext = {
req: NodeRequest;
res: ServerResponse;
waitUntil(promise: Promise<unknown>): void;
},
TBaseObject = unknown
>({
Expand All @@ -77,18 +88,23 @@ export function createServerAdapter<
}

async function requestListener(nodeRequest: NodeRequest, serverResponse: ServerResponse) {
const waitUntilPromises: Promise<unknown>[] = [];
const response = await handleNodeRequest(nodeRequest, {
req: nodeRequest,
res: serverResponse,
waitUntil(p: Promise<any>) {
p.catch(err => console.error(err));
waitUntil(p: Promise<unknown>) {
waitUntilPromises.push(p);
},
} as any);
if (response) {
return sendNodeResponse(response, serverResponse);
await sendNodeResponse(response, serverResponse);
} else {
return new Promise(resolve => serverResponse.end(resolve));
await new Promise(resolve => {
serverResponse.statusCode = 404;
serverResponse.end(resolve);
});
}
await handleWaitUntils(waitUntilPromises);
}

function handleEvent(event: FetchEvent) {
Expand All @@ -108,7 +124,7 @@ export function createServerAdapter<
handle: requestListener,
};

function genericRequestHandler(input: any, ctx: any) {
function genericRequestHandler(input: any, ctx: any, ...rest: any[]) {
if ('process' in globalThis && process.versions?.['bun'] != null) {
// This is required for bun
input.text();
Expand All @@ -128,6 +144,24 @@ export function createServerAdapter<
}
// Or is it Request itself?
// Then ctx is present and it is the context
if (rest?.length > 0) {
ctx = Object.assign({}, ctx, ...rest);
}
if (!ctx.waitUntil) {
const waitUntilPromises: Promise<unknown>[] = [];
ctx.waitUntil = (p: Promise<unknown>) => {
waitUntilPromises.push(p);
};
const response$ = handleRequest(input, {
...ctx,
waitUntil(p: Promise<unknown>) {
waitUntilPromises.push(p);
},
});
if (waitUntilPromises.length > 0) {
return handleWaitUntils(waitUntilPromises).then(() => response$);
}
}
return handleRequest(input, ctx);
}

Expand Down
59 changes: 59 additions & 0 deletions packages/server/test/node.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { createServerAdapter } from '@whatwg-node/server';
import { createServer, Server } from 'http';
import { fetch } from '@whatwg-node/fetch';

describe('Node Specific Cases', () => {
let port = 9876;
let server: Server | undefined;
afterEach(done => {
if (server) {
server.close(err => {
if (err) {
throw err;
}
server = undefined;
done();
});
} else {
done();
}
port = Math.floor(Math.random() * 1000) + 9800;
});
it('should handle empty responses', async () => {
const serverAdapter = createServerAdapter({
async handleRequest() {
return undefined as any;
},
});
server = createServer(serverAdapter);
await new Promise<void>(resolve => server!.listen(port, resolve));
const response = await fetch(`http://localhost:${port}`);
await response.text();
expect(response.status).toBe(404);
});
it('should handle waitUntil properly', async () => {
let flag = false;
const serverAdapter = createServerAdapter({
handleRequest(_request, { waitUntil }) {
waitUntil(
Promise.resolve().then(() => {
flag = true;
})
);
return Promise.resolve(
new Response(null, {
status: 204,
})
);
},
});
server = createServer(serverAdapter);
await new Promise<void>(resolve => server!.listen(port, resolve));
const response$ = fetch(`http://localhost:${port}`);
expect(flag).toBe(false);
const response = await response$;
await response.text();
expect(flag).toBe(true);
expect.assertions(2);
});
});

0 comments on commit 722ffda

Please sign in to comment.