diff options
| author | Arpit Chakladar <arpitchakladar+git@gmail.com> | 2026-07-13 23:52:20 +0530 |
|---|---|---|
| committer | Arpit Chakladar <arpitchakladar+git@gmail.com> | 2026-07-13 23:52:20 +0530 |
| commit | eb04552d222762de2153cb37c38dbc71275809f3 (patch) | |
| tree | 7949b103dd86f11f177cb2bf607e80a65e26f059 | |
| parent | ce401d03e75bcbfd1cdfa963954e0807219fb80d (diff) | |
| download | banglar-bhumi-utils-eb04552d222762de2153cb37c38dbc71275809f3.tar.gz banglar-bhumi-utils-eb04552d222762de2153cb37c38dbc71275809f3.zip | |
feat: added responders to JQuery ajax post and get requests
for some request we may want to prevent the request entirely and instead
have the extension give a custom response (for caching for example).
| -rw-r--r-- | src/shared/intercept-jquery-ajax.ts | 134 |
1 files changed, 118 insertions, 16 deletions
diff --git a/src/shared/intercept-jquery-ajax.ts b/src/shared/intercept-jquery-ajax.ts index b627d0e..03ea5e0 100644 --- a/src/shared/intercept-jquery-ajax.ts +++ b/src/shared/intercept-jquery-ajax.ts @@ -4,61 +4,131 @@ export type JQueryAjaxArgs = [ callback: (data: unknown, textStatus: string, jqXHR: JQuery.jqXHR) => void, dataType?: string ]; - type JQueryAjaxInterceptor = (args: JQueryAjaxArgs) => void; - type InterceptorRegistration = { urlSuffix: string; interceptor: JQueryAjaxInterceptor }; - const postInterceptors: InterceptorRegistration[] = []; const getInterceptors: InterceptorRegistration[] = []; +/** Data a responder wants to resolve the "request" with, instead of hitting the network. */ +type JQueryAjaxResponse = { + data: unknown; + textStatus?: string +}; + +/** + * A responder inspects the outgoing args and optionally returns a response. + * Returning `undefined` means "not handled, let the real request (or the next + * responder) proceed." + */ +type JQueryAjaxResponder = (args: JQueryAjaxArgs) => JQueryAjaxResponse | undefined; + +type ResponderRegistration = { + urlSuffix: string; + responder: JQueryAjaxResponder +}; +const postResponders: ResponderRegistration[] = []; +const getResponders: ResponderRegistration[] = []; + // Define a strict signature to replace the unsafe `Function` type type OriginalJQueryAjaxMethod = (...args: unknown[]) => JQuery.jqXHR; /** + * Extracts the request URL from `$.post`/`$.get` args, whether called with a + * plain string URL or a settings object. + */ +function extractUrl(args: unknown[]): string | null { + const firstArg = args[0]; + if (typeof firstArg === "string") return firstArg; + if (firstArg && typeof firstArg === "object" && "url" in firstArg && typeof firstArg.url === "string") { + return firstArg.url; + } + return null; +} + +/** + * Checks registered responders for a match. If one matches and returns a + * response, synthesizes a resolved jqXHR-like object, invokes the caller's + * own success callback (3rd positional arg) to preserve `$.post`/`$.get` + * shorthand semantics, and returns it so the real ajax call can be skipped. + * Returns `null` if no responder handled the request. + */ +function tryRespond(responders: ResponderRegistration[], args: unknown[]): JQuery.jqXHR | null { + const requestUrl = extractUrl(args); + if (typeof requestUrl !== "string") return null; + + for (const { urlSuffix, responder } of responders) { + if (!requestUrl.endsWith(urlSuffix)) continue; + + const response = responder(args as unknown as JQueryAjaxArgs); + if (response === undefined) continue; + + const { data, textStatus = "success" } = response; + const deferred = $.Deferred(); + const jqXHR = deferred.promise() as unknown as JQuery.jqXHR; + + const callback = args[2]; + if (typeof callback === "function") { + (callback as JQueryAjaxArgs[2])(data, textStatus, jqXHR); + } + deferred.resolve(data, textStatus, jqXHR); + + return jqXHR; + } + return null; +} + +/** * On DOMContentLoaded, proxies jQuery's `$.post` and `$.get` so that - * registered interceptors are called before the real request. + * registered interceptors are called before the real request, and + * registered responders get a chance to short-circuit the request entirely + * by resolving it with synthetic data. */ document.addEventListener("DOMContentLoaded", function() { // 1. Keep the original methods bound to the jQuery ($) object const originalPost = $.post.bind($); const originalGet = $.get.bind($); - /** Proxied `$.post` that invokes registered interceptors before the real call. */ + /** + * Proxied `$.post` that invokes registered interceptors before the real + * call, then checks registered responders for a short-circuit before + * falling back to the real `$.post`. + */ $.post = function(this: typeof $, ...args: unknown[]): JQuery.jqXHR { for (const { urlSuffix, interceptor } of postInterceptors) { // Safely inspect jQuery settings objects without using "any" - const firstArg = args[0]; - const requestUrl = typeof firstArg === "string" - ? firstArg - : (firstArg && typeof firstArg === "object" && "url" in firstArg && typeof firstArg.url === "string" ? firstArg.url : null); - + const requestUrl = extractUrl(args); if (typeof requestUrl === "string" && requestUrl.endsWith(urlSuffix)) { // Cast the unknown array to the expected tuple type interceptor(args as unknown as JQueryAjaxArgs); } } + const shortCircuited = tryRespond(postResponders, args); + if (shortCircuited) return shortCircuited; + // Use our strict signature instead of `Function` to satisfy ESLint return (originalPost as unknown as OriginalJQueryAjaxMethod)(...args); } as typeof $.post; // Reassert the original type so external consumers see the normal signature - /** Proxied `$.get` that invokes registered interceptors before the real call. */ + /** + * Proxied `$.get` that invokes registered interceptors before the real + * call, then checks registered responders for a short-circuit before + * falling back to the real `$.get`. + */ $.get = function(this: typeof $, ...args: unknown[]): JQuery.jqXHR { for (const { urlSuffix, interceptor } of getInterceptors) { - const firstArg = args[0]; - const requestUrl = typeof firstArg === "string" - ? firstArg - : (firstArg && typeof firstArg === "object" && "url" in firstArg && typeof firstArg.url === "string" ? firstArg.url : null); - + const requestUrl = extractUrl(args); if (typeof requestUrl === "string" && requestUrl.endsWith(urlSuffix)) { interceptor(args as unknown as JQueryAjaxArgs); } } + const shortCircuited = tryRespond(getResponders, args); + if (shortCircuited) return shortCircuited; + return (originalGet as unknown as OriginalJQueryAjaxMethod)(...args); } as typeof $.get; }); @@ -84,3 +154,35 @@ export function addPostInterceptor(urlSuffix: string, interceptor: JQueryAjaxInt export function addGetInterceptor(urlSuffix: string, interceptor: JQueryAjaxInterceptor): void { getInterceptors.push({ urlSuffix, interceptor }); } + +/** + * Registers a responder that can conditionally short-circuit a matching + * `$.post` call, resolving it with synthetic data instead of making the + * real network request. + * + * @param urlSuffix - The URL suffix to match (checked via `endsWith`). + * @param responder - Inspects the original `arguments` from `$.post` and + * either returns a `{ data, textStatus? }` response to + * short-circuit the request, or `undefined` to let the + * request proceed (checking the next responder, or + * falling through to the real `$.post`). + */ +export function addPostResponder(urlSuffix: string, responder: JQueryAjaxResponder): void { + postResponders.push({ urlSuffix, responder }); +} + +/** + * Registers a responder that can conditionally short-circuit a matching + * `$.get` call, resolving it with synthetic data instead of making the + * real network request. + * + * @param urlSuffix - The URL suffix to match (checked via `endsWith`). + * @param responder - Inspects the original `arguments` from `$.get` and + * either returns a `{ data, textStatus? }` response to + * short-circuit the request, or `undefined` to let the + * request proceed (checking the next responder, or + * falling through to the real `$.get`). + */ +export function addGetResponder(urlSuffix: string, responder: JQueryAjaxResponder): void { + getResponders.push({ urlSuffix, responder }); +} |
