aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArpit Chakladar <arpitchakladar+git@gmail.com>2026-07-13 23:52:20 +0530
committerArpit Chakladar <arpitchakladar+git@gmail.com>2026-07-13 23:52:20 +0530
commiteb04552d222762de2153cb37c38dbc71275809f3 (patch)
tree7949b103dd86f11f177cb2bf607e80a65e26f059
parentce401d03e75bcbfd1cdfa963954e0807219fb80d (diff)
downloadbanglar-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.ts134
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 });
+}