Repository: strapi/strapi
File: packages/core/content-type-builder/admin/src/components/AIChat/hooks/useAIFetch.ts (TypeScript)

Code before:
/**
 * In charge of fetching data from Strapi AI endpoints
 */

import { useState } from 'react';

import { Message, useChat } from '@ai-sdk/react';
import { useAppInfo, getFetchClient } from '@strapi/admin/strapi-admin';

import { STRAPI_AI_CHAT_URL, STRAPI_AI_URL } from '../lib/constants';
import { Attachment } from '../lib/types/attachments';
import { Schema } from '../lib/types/schema';

/* -------------------------------------------------------------------------------------------------
 * AI Token Management
 * -----------------------------------------------------------------------------------------------*/
interface AITokenData {
  token: string;
  expiresAt: string;
}

let aiTokenCache: AITokenData | null = null;

const isTokenExpired = (expiresAt: string): boolean => {
  // Add 1 minute buffer before expiration
  const bufferMs = 60 * 1000;
  return new Date(expiresAt).getTime() - bufferMs <= Date.now();
};

const storeAIToken = (tokenData: AITokenData) => {
  aiTokenCache = tokenData;
  // Optional: also store in sessionStorage for persistence across components
  sessionStorage.setItem('strapi-ai-token', JSON.stringify(tokenData));
};

const getStoredAIToken = (): AITokenData | null => {
  // First check memory cache
  if (aiTokenCache && !isTokenExpired(aiTokenCache.expiresAt)) {
    return aiTokenCache;
  }

  // Then check sessionStorage
  const stored = sessionStorage.getItem('strapi-ai-token');
  if (stored) {
    try {
      const tokenData = JSON.parse(stored) as AITokenData;
      if (!isTokenExpired(tokenData.expiresAt)) {
        aiTokenCache = tokenData; // Update memory cache
        return tokenData;
      }
    } catch {}

Review comment (suggestion):
```suggestion
  // Add 1 minute buffer before expiration
  const bufferMs = 60 * 1000;
```
I would let this method be accurate, and move the safety padding to the place where it's actually used

Code after:
/**
 * In charge of fetching data from Strapi AI endpoints
 */

import { useState } from 'react';

import { Message, useChat } from '@ai-sdk/react';
import { useAppInfo } from '@strapi/admin/strapi-admin';

import { fetchAI, makeChatFetch, safeParseJson } from '../lib/aiClient';
import { STRAPI_AI_CHAT_URL, STRAPI_AI_URL } from '../lib/constants';
import { Attachment } from '../lib/types/attachments';
import { Schema } from '../lib/types/schema';

/* -------------------------------------------------------------------------------------------------
 * Types
 * -----------------------------------------------------------------------------------------------*/
/**
 * Chat title
 */
export namespace GenerateTitle {
  export interface Request {
    body: {
      chatId: string;
      message: string;
    };
  }
  export interface Response {
    data: {
      title: string;
    };
    error?: string;
  }
}

/**
 * Upload a project to the chat
 */
export namespace UploadProject {
  export interface Request {
    body: {
      chatId: string;
      name: string;
      type: 'code';
      files: {
        path: string;
        content: string;
      }[];
    };
  }
  export interface Response {