rezilient.js
Version:
Rezilient.js - Revolutionary offline-first framework with AI-awareness, principle-driven development, carbon-conscious computing, and self-healing capabilities
393 lines (334 loc) • 10.3 kB
JavaScript
// src/hooks/useAetherStore.js
/**
* React-style hooks for Aether.js stores and sync engine
* Provides a modern, declarative API for state management and synchronization
*/
/**
* Hook for subscribing to AetherStore or PersistentStore changes
* @param {AetherStore|PersistentStore} store - The store to subscribe to
* @param {function} [selector] - Optional selector function to extract specific data
* @returns {any} Current store value or selected value
*/
export function useAetherStore(store, selector = null) {
// For vanilla JS environments without React
if (typeof React === 'undefined') {
return useAetherStoreVanilla(store, selector);
}
const [state, setState] = React.useState(() => {
const currentValue = store.get();
return selector ? selector(currentValue) : currentValue;
});
React.useEffect(() => {
const unsubscribe = store.subscribe(newValue => {
const selectedValue = selector ? selector(newValue) : newValue;
setState(selectedValue);
});
return unsubscribe;
}, [store, selector]);
return state;
}
/**
* Vanilla JavaScript version of useAetherStore
* Returns an object with current value and update methods
*/
function useAetherStoreVanilla(store, selector = null) {
let currentValue = store.get();
let listeners = [];
const getValue = () => {
const value = store.get();
return selector ? selector(value) : value;
};
const subscribe = (callback) => {
listeners.push(callback);
// Subscribe to store changes
const unsubscribe = store.subscribe(newValue => {
const selectedValue = selector ? selector(newValue) : newValue;
currentValue = selectedValue;
// Notify all listeners
listeners.forEach(listener => {
try {
listener(selectedValue);
} catch (error) {
console.error('Error in useAetherStore listener:', error);
}
});
});
return () => {
// Remove listener
const index = listeners.indexOf(callback);
if (index > -1) {
listeners.splice(index, 1);
}
// If no more listeners, unsubscribe from store
if (listeners.length === 0) {
unsubscribe();
}
};
};
return {
value: getValue(),
subscribe,
get: getValue
};
}
/**
* Hook for managing sync state and operations
* @param {SyncEngine} syncEngine - The sync engine instance
* @returns {object} Sync state and control methods
*/
export function useSyncEngine(syncEngine) {
if (typeof React === 'undefined') {
return useSyncEngineVanilla(syncEngine);
}
const [syncState, setSyncState] = React.useState(syncEngine.getSyncState());
const [queueStats, setQueueStats] = React.useState(null);
React.useEffect(() => {
// Subscribe to sync state changes
const unsubscribe = syncEngine.subscribeSyncState(setSyncState);
// Load initial queue stats
syncEngine.getQueueStats().then(setQueueStats);
// Subscribe to queue updates
const unsubscribeQueue = syncEngine.addEventListener('queue-updated', () => {
syncEngine.getQueueStats().then(setQueueStats);
});
return () => {
unsubscribe();
unsubscribeQueue();
};
}, [syncEngine]);
const syncActions = React.useMemo(() => ({
sync: () => syncEngine.processQueue(),
clearQueue: () => syncEngine.clearQueue(),
removeMutation: (id) => syncEngine.removeMutation(id),
forceSyncMutation: (id) => syncEngine.forceSyncMutation(id),
getHealthStatus: () => syncEngine.getHealthStatus()
}), [syncEngine]);
return {
syncState,
queueStats,
actions: syncActions,
isOnline: syncState.status !== 'offline',
isSyncing: syncState.status === 'syncing',
hasErrors: syncState.status === 'error',
pendingCount: syncState.pending
};
}
/**
* Vanilla JavaScript version of useSyncEngine
*/
function useSyncEngineVanilla(syncEngine) {
let syncState = syncEngine.getSyncState();
let queueStats = null;
let listeners = [];
// Load initial queue stats
syncEngine.getQueueStats().then(stats => {
queueStats = stats;
notifyListeners();
});
// Subscribe to sync state changes
const unsubscribeSyncState = syncEngine.subscribeSyncState(newState => {
syncState = newState;
notifyListeners();
});
// Subscribe to queue updates
const unsubscribeQueue = syncEngine.addEventListener('queue-updated', () => {
syncEngine.getQueueStats().then(stats => {
queueStats = stats;
notifyListeners();
});
});
function notifyListeners() {
listeners.forEach(listener => {
try {
listener({
syncState,
queueStats,
isOnline: syncState.status !== 'offline',
isSyncing: syncState.status === 'syncing',
hasErrors: syncState.status === 'error',
pendingCount: syncState.pending
});
} catch (error) {
console.error('Error in useSyncEngine listener:', error);
}
});
}
const subscribe = (callback) => {
listeners.push(callback);
// Immediately call with current state
callback({
syncState,
queueStats,
isOnline: syncState.status !== 'offline',
isSyncing: syncState.status === 'syncing',
hasErrors: syncState.status === 'error',
pendingCount: syncState.pending
});
return () => {
const index = listeners.indexOf(callback);
if (index > -1) {
listeners.splice(index, 1);
}
if (listeners.length === 0) {
unsubscribeSyncState();
unsubscribeQueue();
}
};
};
const actions = {
sync: () => syncEngine.processQueue(),
clearQueue: () => syncEngine.clearQueue(),
removeMutation: (id) => syncEngine.removeMutation(id),
forceSyncMutation: (id) => syncEngine.forceSyncMutation(id),
getHealthStatus: () => syncEngine.getHealthStatus()
};
return {
subscribe,
actions,
getCurrentState: () => ({
syncState,
queueStats,
isOnline: syncState.status !== 'offline',
isSyncing: syncState.status === 'syncing',
hasErrors: syncState.status === 'error',
pendingCount: syncState.pending
})
};
}
/**
* Hook for creating and managing a persistent store
* @param {string} key - Storage key
* @param {any} initialValue - Initial value
* @param {object} options - Store options
* @returns {array} [value, setValue, store] tuple
*/
export function usePersistentStore(key, initialValue, options = {}) {
if (typeof React === 'undefined') {
return usePersistentStoreVanilla(key, initialValue, options);
}
const [store] = React.useState(() => {
const { PersistentStore } = require('../data/PersistentStore.js');
return new PersistentStore(key, initialValue);
});
const value = useAetherStore(store);
const setValue = React.useCallback((newValue) => {
if (typeof newValue === 'function') {
store.update(newValue);
} else {
store.set(newValue);
}
}, [store]);
return [value, setValue, store];
}
/**
* Vanilla JavaScript version of usePersistentStore
*/
function usePersistentStoreVanilla(key, initialValue, options = {}) {
const { PersistentStore } = require('../data/PersistentStore.js');
const store = new PersistentStore(key, initialValue);
const setValue = (newValue) => {
if (typeof newValue === 'function') {
store.update(newValue);
} else {
store.set(newValue);
}
};
return {
store,
setValue,
getValue: () => store.get(),
subscribe: (callback) => store.subscribe(callback)
};
}
/**
* Hook for offline-aware components
* @returns {object} Network state and utilities
*/
export function useNetworkState() {
if (typeof React === 'undefined') {
return useNetworkStateVanilla();
}
const [isOnline, setIsOnline] = React.useState(
typeof navigator !== 'undefined' ? navigator.onLine : true
);
React.useEffect(() => {
if (typeof window === 'undefined') return;
const handleOnline = () => setIsOnline(true);
const handleOffline = () => setIsOnline(false);
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return {
isOnline,
isOffline: !isOnline,
connectionType: typeof navigator !== 'undefined' && navigator.connection
? navigator.connection.effectiveType
: 'unknown'
};
}
/**
* Vanilla JavaScript version of useNetworkState
*/
function useNetworkStateVanilla() {
let isOnline = typeof navigator !== 'undefined' ? navigator.onLine : true;
let listeners = [];
if (typeof window !== 'undefined') {
const handleOnline = () => {
isOnline = true;
notifyListeners();
};
const handleOffline = () => {
isOnline = false;
notifyListeners();
};
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
}
function notifyListeners() {
const state = {
isOnline,
isOffline: !isOnline,
connectionType: typeof navigator !== 'undefined' && navigator.connection
? navigator.connection.effectiveType
: 'unknown'
};
listeners.forEach(listener => {
try {
listener(state);
} catch (error) {
console.error('Error in useNetworkState listener:', error);
}
});
}
const subscribe = (callback) => {
listeners.push(callback);
// Immediately call with current state
callback({
isOnline,
isOffline: !isOnline,
connectionType: typeof navigator !== 'undefined' && navigator.connection
? navigator.connection.effectiveType
: 'unknown'
});
return () => {
const index = listeners.indexOf(callback);
if (index > -1) {
listeners.splice(index, 1);
}
};
};
return {
subscribe,
getCurrentState: () => ({
isOnline,
isOffline: !isOnline,
connectionType: typeof navigator !== 'undefined' && navigator.connection
? navigator.connection.effectiveType
: 'unknown'
})
};
}