Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 6 additions & 23 deletions src/adapters/node.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import BrowserAdapter from './browser.js';
import { requestWithFetch } from './fetch.js';
import type { AdapterConfig, RequestOptions } from '../types.js';
import type { AdapterConfig } from '../types.js';

/**
* The Fetch transport for Node.js. It is the browser transport plus a User-Agent, so
* every verb, the current host and namespace, and headers set later with `setHeaders`
* work the same way in both.
*/
export default class NodeAdapter extends BrowserAdapter {
constructor(config: AdapterConfig = {}) {
const headers = new Headers(config.headers);
Expand All @@ -10,25 +14,4 @@ export default class NodeAdapter extends BrowserAdapter {
}
super({ ...config, headers });
}

override request(method: string, url: string, options: RequestOptions & { data?: unknown } = {}): Promise<unknown> {
const { data, ...requestOptions } = options;
return requestWithFetch(this.fetchConfig, { path: url, method, data, options: requestOptions });
}

override post(path: string, data: unknown = {}, options: RequestOptions = {}): Promise<unknown> {
return this.request('POST', path, { ...options, data });
}

override put(path: string, data: unknown = {}, options: RequestOptions = {}): Promise<unknown> {
return this.request('PUT', path, { ...options, data });
}

override patch(path: string, data: unknown = {}, options: RequestOptions = {}): Promise<unknown> {
return this.request('PATCH', path, { ...options, data });
}

override delete(path: string, options: RequestOptions = {}, legacyOptions?: RequestOptions): Promise<unknown> {
return this.request('DELETE', path, legacyOptions ?? options);
}
}
27 changes: 27 additions & 0 deletions tests/core.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,33 @@ describe('edge and failure contracts', () => {
expect(new Headers(presetNode.headers).get('User-Agent')).toBe('custom');
});

it('sends Node requests to the right URL with the method, body and current headers', async () => {
const fetchMock = vi.fn(() => Promise.resolve(new Response('{"id":"order_1"}', { status: 200 })));
const node = new NodeAdapter({ host: 'https://api.test', namespace: 'v1', publicKey: 'pk', fetch: fetchMock });
node.setHeaders({ 'Customer-Token': 'token_1' });

await expect(node.get('orders', { status: 'active' })).resolves.toEqual({ id: 'order_1' });
await node.post('orders', { id: 1 });
await node.delete('orders/1', {}, { headers: { 'X-Request': 'yes' } });

const [getUrl, getInit] = fetchMock.mock.calls[0] as unknown as [string, RequestInit];
expect(getUrl).toBe('https://api.test/v1/orders?status=active');
expect(getInit.method).toBe('GET');
const getHeaders = new Headers(getInit.headers);
expect(getHeaders.get('Authorization')).toBe('Bearer pk');
expect(getHeaders.get('Customer-Token')).toBe('token_1');
expect(getHeaders.get('User-Agent')).toBe('@fleetbase/sdk;node');

const [postUrl, postInit] = fetchMock.mock.calls[1] as unknown as [string, RequestInit];
expect(postUrl).toBe('https://api.test/v1/orders');
expect(postInit).toMatchObject({ method: 'POST', body: '{"id":1}' });

const [deleteUrl, deleteInit] = fetchMock.mock.calls[2] as unknown as [string, RequestInit];
expect(deleteUrl).toBe('https://api.test/v1/orders/1');
expect(deleteInit.method).toBe('DELETE');
expect(new Headers(deleteInit.headers).get('X-Request')).toBe('yes');
});

it('covers UUID fallback and sparse address data', () => {
const originalCrypto = globalThis.crypto;
vi.stubGlobal('crypto', {});
Expand Down
Loading