Skip to content

Instantly share code, notes, and snippets.

@jay-babu
Created December 11, 2025 06:23
Show Gist options
  • Select an option

  • Save jay-babu/855bcdbbd0e2e0256ab9aa2d3c9ee478 to your computer and use it in GitHub Desktop.

Select an option

Save jay-babu/855bcdbbd0e2e0256ab9aa2d3c9ee478 to your computer and use it in GitHub Desktop.
diff --git a/src/config/client.ts b/src/config/client.ts
index 88e53b893..acb5e36fa 100644
--- a/src/config/client.ts
+++ b/src/config/client.ts
@@ -69,18 +69,64 @@ export const client = axios.create({
paramsSerializer: (params) => qs.stringify(params, { indices: false }),
});
-client.interceptors.request.use(async (config) => {
+// Cache Firebase token to avoid ~50ms getIdToken() call on every request
+let cachedToken: string | null = null;
+let cachedTokenUserId: string | undefined = undefined;
+
+export const getCachedToken = (): string | null | Promise<string | null> => {
+ const currentUser = firebaseAuth.currentUser;
+
+ // Return synchronously if cached and same user
+ if (cachedToken && cachedTokenUserId === currentUser?.uid) {
+ return cachedToken;
+ }
+
+ // Clear cache if user changed
+ if (!currentUser) {
+ cachedToken = null;
+ cachedTokenUserId = undefined;
+ return null;
+ }
+
+ // Only go async when we need to fetch
+ return (async () => {
+ cachedToken = await currentUser.getIdToken();
+ cachedTokenUserId = currentUser.uid;
+ return cachedToken;
+ })();
+};
+
+// Export function to clear token cache (call on logout)
+export const clearTokenCache = () => {
+ cachedToken = null;
+ cachedTokenUserId = undefined;
+};
+
+client.interceptors.request.use((config) => {
config.headers["X-AVAILABILITY"] = REDIRECT_TO_CORE_SALE_API();
- // Get token using the centralized function that prefers Firebase when available
- const token = await firebaseAuth.currentUser?.getIdToken();
- if (token) {
- config.headers.Authorization = `Bearer ${token}`;
- const currentUser = firebaseAuth.currentUser;
- config.headers["X-User-ID"] = currentUser?.uid;
+ const tokenOrPromise = getCachedToken();
+
+ // If token is cached (synchronous), set headers and return config directly
+ if (typeof tokenOrPromise === "string") {
+ config.headers.Authorization = `Bearer ${tokenOrPromise}`;
+ config.headers["X-User-ID"] = firebaseAuth.currentUser?.uid;
+ return config;
}
- return config;
+ // If no user, return config directly
+ if (tokenOrPromise === null) {
+ return config;
+ }
+
+ // Only go async when we need to fetch a new token
+ return tokenOrPromise.then((token) => {
+ if (token) {
+ config.headers.Authorization = `Bearer ${token}`;
+ config.headers["X-User-ID"] = firebaseAuth.currentUser?.uid;
+ }
+ return config;
+ });
});
// https://github.com/kubb-labs/kubb/commit/87370f5b09df4429e4ee8e3d40494e66f36c9484
diff --git a/src/context/Sales/factory/PreProcessWASMInputFactory.tsx b/src/context/Sales/factory/PreProcessWASMInputFactory.tsx
index 2efdfd900..81b63b1ff 100644
--- a/src/context/Sales/factory/PreProcessWASMInputFactory.tsx
+++ b/src/context/Sales/factory/PreProcessWASMInputFactory.tsx
@@ -39,10 +39,9 @@ import type {
WasmPricingCartInput,
} from "src/lib/hades_price";
import type { Department as SpringDepartment } from "src/spring-generated";
-import {
- EntitySalesChannelPaymentMethod,
- usePreprocessPricingData,
-} from "src/spring-generated";
+import { EntitySalesChannelPaymentMethod } from "src/spring-generated";
+import type { PreprocessPricingDataMutationResponse } from "src/spring-generated";
+import { getCachedToken } from "src/config/client";
import { useServiceState } from "src/store/serviceStatus/serviceStatusSlice";
import {
convertDiscount,
@@ -593,11 +592,7 @@ const createLocalPreProcessWASMInput =
};
const createServerPreProcessWASMInput =
- (
- preprocessPricingDataMutateAsync: ReturnType<
- typeof usePreprocessPricingData
- >["mutateAsync"],
- ): UseMutateAsyncFunction<
+ (): UseMutateAsyncFunction<
PreProcessWASMInputResult,
Error,
PreProcessWASMInputParams,
@@ -632,24 +627,55 @@ const createServerPreProcessWASMInput =
}
}
- const result = await timed("preprocessPricingData-server", async () =>
- preprocessPricingDataMutateAsync({
- data: {
- dayOfWeek: params.dayOfWeek,
- localDate: params.localDate,
- localTime: params.localTime,
- bulkItemPricesInput,
- cohortId: params.entity.cohortId,
- cohortItemIds,
- cohortPriceLevelId: params.actualCohortPriceLevel,
- departmentIds,
- entityId: params.entity.id,
- entityItemIds,
- loyaltyCustomerId: params.requestedCustomer?.loyaltyCustomerId,
- selectedSalesChannelId: params.selectedSalesChannel.id,
+ // Bypass axios/useMutation to avoid ~46ms overhead - use fetch directly
+ const requestData = {
+ dayOfWeek: params.dayOfWeek,
+ localDate: params.localDate,
+ localTime: params.localTime,
+ bulkItemPricesInput,
+ cohortId: params.entity.cohortId,
+ cohortItemIds,
+ cohortPriceLevelId: params.actualCohortPriceLevel,
+ departmentIds,
+ entityId: params.entity.id,
+ entityItemIds,
+ loyaltyCustomerId: params.requestedCustomer?.loyaltyCustomerId,
+ selectedSalesChannelId: params.selectedSalesChannel.id,
+ };
+
+ // Prepare all sync work before the timed block
+ const tokenOrPromise = timed("getCachedToken", () => getCachedToken());
+ // Avoid async/await overhead when token is already cached
+ const resolvedToken =
+ typeof tokenOrPromise === "string" || tokenOrPromise === null
+ ? timed("tokenSync", () => tokenOrPromise)
+ : await timed("tokenAsync", () => tokenOrPromise);
+ const baseUrl = import.meta.env.VITE_APP_API_URL;
+ const body = JSON.stringify(requestData);
+
+ const result = await timed("preprocessPricingData-server", async () => {
+ const response = await fetch(`${baseUrl}/api/v1/pricing_preprocess`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ ...(resolvedToken
+ ? {
+ Authorization: `Bearer ${resolvedToken}`,
+ }
+ : {}),
+ "X-Entity-ID": String(params.entity.id),
+ "X-Timezone": Intl.DateTimeFormat().resolvedOptions().timeZone,
+ "X-AVAILABILITY": "CRITICAL",
},
- }),
- );
+ body,
+ });
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ return response.json() as Promise<PreprocessPricingDataMutationResponse>;
+ });
// Direct mapping from server response to WASM input using shared utilities
const entityItemPriceMap = buildEntityItemPriceMap(
@@ -753,9 +779,6 @@ const createServerPreProcessWASMInput =
};
export const usePreProcessWASMInputFactory = () => {
- const { mutateAsync: preprocessPricingDataMutateAsync } =
- usePreprocessPricingData();
-
const isOffline = useServiceState("pricingContext").status === "offline";
return useMemo(() => {
@@ -766,11 +789,9 @@ export const usePreProcessWASMInputFactory = () => {
};
} else {
return {
- mutateAsync: createServerPreProcessWASMInput(
- preprocessPricingDataMutateAsync,
- ),
+ mutateAsync: createServerPreProcessWASMInput(),
isPending: false,
};
}
- }, [preprocessPricingDataMutateAsync, isOffline]);
+ }, [isOffline]);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment