@jeanmemory/react
Version:
React SDK for Jean Memory - Build personalized AI chatbots in 5 lines of code
152 lines (151 loc) • 5.37 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.JeanProvider = JeanProvider;
exports.useJean = useJean;
const jsx_runtime_1 = require("react/jsx-runtime");
/**
* Jean Memory React SDK v2.0 - Provider Component
* Secure OAuth 2.1 PKCE with JWT-in-header authentication
*/
const react_1 = require("react");
const config_1 = require("./config");
const oauth_1 = require("./oauth");
const JeanContext = (0, react_1.createContext)(null);
function JeanProvider({ apiKey, children }) {
const [user, setUserState] = (0, react_1.useState)(null);
const [messages, setMessages] = (0, react_1.useState)([]);
const [isLoading, setIsLoading] = (0, react_1.useState)(false);
const [error, setError] = (0, react_1.useState)(null);
// Initialize authentication state from stored session
(0, react_1.useEffect)(() => {
const storedUser = (0, oauth_1.getUserSession)();
if (storedUser && (0, oauth_1.isAuthenticated)()) {
setUserState(storedUser);
}
}, []);
const setUser = (newUser) => {
setUserState(newUser);
setError(null);
};
const sendMessage = async (message, options) => {
if (!user) {
throw new Error('User must be authenticated to send messages');
}
setIsLoading(true);
setError(null);
try {
// SECURE: JWT token in Authorization header, API key in X-API-Key header
const response = await fetch(`${config_1.JEAN_API_BASE}/api/jean-chat`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${user.access_token}`, // User identity (JWT)
'X-API-Key': apiKey, // App authentication
'Content-Type': 'application/json'
},
body: JSON.stringify({
message,
format: options?.format || 'enhanced'
})
});
if (!response.ok) {
const errorData = await response.text();
throw new Error(`Request failed: ${response.status} - ${errorData}`);
}
const data = await response.json();
// Add messages to conversation
const userMessage = {
id: `user-${Date.now()}`,
role: 'user',
content: message,
timestamp: new Date()
};
const assistantMessage = {
id: `assistant-${Date.now()}`,
role: 'assistant',
content: data.content || data.context || 'No response received',
timestamp: new Date()
};
setMessages(prev => [...prev, userMessage, assistantMessage]);
return assistantMessage.content;
}
catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
setError(errorMessage);
throw err;
}
finally {
setIsLoading(false);
}
};
const addMemory = async (content) => {
if (!user) {
throw new Error('User must be authenticated to add memories');
}
const response = await fetch(`${config_1.JEAN_API_BASE}/mcp/tools/call`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${user.access_token}`,
'X-API-Key': apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'add_memory',
arguments: { content }
})
});
if (!response.ok) {
throw new Error(`Failed to add memory: ${response.status}`);
}
return response.json();
};
const searchMemory = async (query) => {
if (!user) {
throw new Error('User must be authenticated to search memories');
}
const response = await fetch(`${config_1.JEAN_API_BASE}/mcp/tools/call`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${user.access_token}`,
'X-API-Key': apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'search_memory',
arguments: { query }
})
});
if (!response.ok) {
throw new Error(`Failed to search memory: ${response.status}`);
}
return response.json();
};
const clearConversation = () => {
setMessages([]);
setError(null);
};
const contextValue = {
// Authentication state
isAuthenticated: !!user && (0, oauth_1.isAuthenticated)(),
isLoading,
user,
error,
// Messaging
messages,
sendMessage,
clearConversation,
// Memory management
addMemory,
searchMemory,
// Internal
setUser,
apiKey
};
return ((0, jsx_runtime_1.jsx)(JeanContext.Provider, { value: contextValue, children: children }));
}
function useJean() {
const context = (0, react_1.useContext)(JeanContext);
if (!context) {
throw new Error('useJean must be used within a JeanProvider');
}
return context;
}
;