Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decrease bundle size + update readme #54

Merged
merged 14 commits into from
Sep 18, 2024
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
162 changes: 99 additions & 63 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"build:browser": "tsup --format esm,iife --out-dir dist/browser --env.NODE_ENV production",
"build:node": "tsup --format cjs --out-dir dist/node --env.NODE_ENV production",
"build:cleanup": "rm -f dist/browser/index.d.mts dist/node/index.d.ts && mv dist/browser/index.d.ts dist/index.d.ts",
"types-check": "tsc --noEmit",
"type-check": "tsc --noEmit",
"test": "jest --forceExit --coverage --detectOpenHandles",
"lint": "eslint ./src/**/*.ts ./test/**/*.spec.ts",
"release": "npm version patch && git push --tags",
Expand Down
12 changes: 7 additions & 5 deletions src/api-handler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type {
FetcherInstance,
RequestConfig,
FetchResponse,
CreatedCustomFetcherInstance,
} from './types/request-handler';
import type {
ApiHandlerConfig,
Expand All @@ -16,6 +16,7 @@ import { createRequestHandler } from './request-handler';
/**
* Creates an instance of API Handler.
* It creates an API fetcher function using native fetch() or a custom fetcher if it is passed as "fetcher".
* @url https://github.com/MattCCC/fetchff
*
* @param {Object} config - Configuration object for the API fetcher.
* @param {string} config.apiUrl - The base URL for the API.
Expand All @@ -24,6 +25,7 @@ import { createRequestHandler } from './request-handler';
* @param {number} config.cancellable - If true, the ongoing previous requests will be automatically cancelled.
* @param {number} config.rejectCancelled - If true and request is set to cancellable, a cancelled request promise will be rejected. By default, instead of rejecting the promise, defaultResponse is returned.
* @param {number} config.timeout - Request timeout
* @param {number} config.dedupeTime - Time window, in milliseconds, during which identical requests are deduplicated (treated as single request).
* @param {string} config.strategy - Error Handling Strategy
* @param {string} config.flattenResponse - Whether to flatten response "data" object within "data" one
* @param {*} config.defaultResponse - Default response when there is no data or when endpoint fails depending on the chosen strategy. It's "null" by default
Expand Down Expand Up @@ -72,11 +74,11 @@ function createApiFetcher<
const requestHandler = createRequestHandler(config);

/**
* Get Fetcher Provider Instance
* Get Custom Fetcher Provider Instance
*
* @returns {FetcherInstance} Request Handler's Fetcher instance
* @returns {CreatedCustomFetcherInstance | null} Request Handler's Custom Fetcher Instance
*/
function getInstance(): FetcherInstance {
function getInstance(): CreatedCustomFetcherInstance | null {
return requestHandler.getInstance();
}

Expand All @@ -87,7 +89,7 @@ function createApiFetcher<
* @returns {Promise}
*/
function handleNonImplemented(endpointName: string): Promise<null> {
console.error(`${endpointName} endpoint must be added to 'endpoints'.`);
console.error(`Add ${endpointName} to 'endpoints'.`);

return Promise.resolve(null);
}
Expand Down
6 changes: 6 additions & 0 deletions src/const.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
export const APPLICATION_JSON = 'application/json';
export const CONTENT_TYPE = 'Content-Type';
export const UNDEFINED = 'undefined';
export const ABORT_ERROR = 'AbortError';
export const TIMEOUT_ERROR = 'TimeoutError';
export const CANCELLED_ERROR = 'CanceledError';
export const GET = 'GET';
export const HEAD = 'HEAD';
3 changes: 2 additions & 1 deletion src/hash.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { UNDEFINED } from './const';
import { RequestConfig } from './types';

// Garbage collected hash cache table
Expand Down Expand Up @@ -56,7 +57,7 @@ export function hash(input: string): string {
export function hashFromConfig(requestConfig: RequestConfig): string {
let key = hashCache.get(requestConfig);

if (typeof key === 'undefined') {
if (typeof key === UNDEFINED) {
const keyString = JSON.stringify(requestConfig);

key = hash(keyString);
Expand Down
36 changes: 22 additions & 14 deletions src/queue-manager.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ABORT_ERROR, TIMEOUT_ERROR } from './const';
import type { RequestConfig } from './types';
import type { QueueItem, RequestsQueue } from './types/queue-manager';

Expand Down Expand Up @@ -32,21 +33,25 @@ export async function addRequest(
const item = queue.get(config);

if (item) {
const isCancellable = item[3];
const previousController = item[0];
const timeoutId = item[1];

// If the request is already in the queue and within the dedupeTime, reuse the existing controller
if (!item.isCancellable && now - item.timestamp < dedupeTime) {
return item.controller;
if (!isCancellable && now - item[2] < dedupeTime) {
return previousController;
}

// If the request is too old, remove it and proceed to add a new one
// Abort previous request, if applicable, and continue as usual
if (item.isCancellable) {
item.controller.abort(
new DOMException('Aborted due to new request', 'AbortError'),
if (isCancellable) {
previousController.abort(
new DOMException('Aborted due to new request', ABORT_ERROR),
);
}

if (item.timeoutId !== null) {
clearTimeout(item.timeoutId);
if (timeoutId !== null) {
clearTimeout(timeoutId);
}

queue.delete(config);
Expand All @@ -58,14 +63,14 @@ export async function addRequest(
? setTimeout(() => {
const error = new DOMException(
`${config.url} aborted due to timeout`,
'TimeoutError',
TIMEOUT_ERROR,
);

removeRequest(config, error);
}, timeout)
: null;

queue.set(config, { controller, timeoutId, timestamp: now, isCancellable });
queue.set(config, [controller, timeoutId, now, isCancellable]);

return controller;
}
Expand All @@ -83,13 +88,16 @@ export async function removeRequest(
const item = queue.get(config);

if (item) {
const controller = item[0];
const timeoutId = item[1];

// If the request is not yet aborted, abort it with the provided error
if (error && !item.controller.signal.aborted) {
item.controller.abort(error);
if (error && !controller.signal.aborted) {
controller.abort(error);
}

if (item.timeoutId !== null) {
clearTimeout(item.timeoutId);
if (timeoutId !== null) {
clearTimeout(timeoutId);
}

queue.delete(config);
Expand All @@ -107,5 +115,5 @@ export async function getController(
): Promise<AbortController | undefined> {
const item = queue.get(config);

return item?.controller;
return item?.[0];
}
Loading
Loading