Initial open-source release

This commit is contained in:
Will Chen
2025-04-11 09:37:05 -07:00
commit 43f67e0739
208 changed files with 45476 additions and 0 deletions

216
scaffold/vite.config.ts Normal file
View File

@@ -0,0 +1,216 @@
import { defineConfig, Plugin } from "vite";
import react from "@vitejs/plugin-react-swc";
import path from "path";
export function devErrorAndNavigationPlugin(): Plugin {
return {
name: "dev-error-and-navigation-handler",
apply: "serve",
transformIndexHtml(html) {
return {
html,
tags: [
{
tag: "script",
injectTo: "head",
children: `
(function() {
// Check if running inside an iframe immediately
const isInsideIframe = window.parent !== window;
if (!isInsideIframe) {
// If not in an iframe, no need for the rest of the script
// console.log('[vite-dev-navigation] Not inside an iframe. Skipping setup.');
return;
}
// Use a unique key for our timestamp to avoid conflicts (optional, but kept for state consistency)
const NAV_TIMESTAMP_KEY = '__viteDevNavTimestamp';
let previousUrl = window.location.href;
let lastNavigationTimestamp = Date.now(); // Initialize with current time
// --- Initial State Timestamp Setup (Optional but helps consistency) ---
try {
const initialState = history.state || {};
if (!initialState[NAV_TIMESTAMP_KEY]) {
initialState[NAV_TIMESTAMP_KEY] = lastNavigationTimestamp;
// Use try-catch for replaceState as well, in case state is not serializable initially
try {
history.replaceState(initialState, '', window.location.href);
// console.log('[vite-dev-navigation] Initial navigation timestamp set:', lastNavigationTimestamp);
} catch(replaceError) {
console.warn('[vite-dev-navigation] Could not set initial navigation timestamp via replaceState:', replaceError);
}
} else {
lastNavigationTimestamp = initialState[NAV_TIMESTAMP_KEY];
// console.log('[vite-dev-navigation] Using existing initial navigation timestamp:', lastNavigationTimestamp);
}
} catch (e) {
console.warn('[vite-dev-navigation] Could not access or modify initial history state:', e);
}
// --- History API Overrides ---
const originalPushState = history.pushState;
const originalReplaceState = history.replaceState;
const handleStateChangeAndNotify = (originalMethod, state, title, url) => {
const newTimestamp = Date.now();
const oldUrlForMessage = previousUrl; // Capture previous URL before potential change
// Prepare new state with timestamp
let newState = state || {};
if (typeof newState !== 'object' || newState === null) {
newState = {};
} else if (Object.isFrozen(newState)) { // Handle frozen state objects
newState = { ...newState };
}
newState[NAV_TIMESTAMP_KEY] = newTimestamp;
// Determine the intended new URL *before* calling the original method
let newUrl;
try {
// Resolve the potentially relative URL against the current location
newUrl = url ? new URL(url, window.location.href).href : window.location.href;
} catch (e) {
console.warn('[vite-dev-navigation] Error constructing URL:', url, e);
newUrl = window.location.href; // Fallback
}
// Determine the type of operation
const navigationType = (originalMethod === originalPushState ? 'pushState' : 'replaceState');
// Call the original history method
try {
originalMethod.call(history, newState, title, url);
// Update internal state *after* successful call
lastNavigationTimestamp = newTimestamp;
previousUrl = window.location.href; // Use the actual URL after the call
// Post message to parent *after* successful state change
// Use the 'newUrl' we calculated earlier as the intended target
// Use 'oldUrlForMessage' as the URL before this operation started
// console.log(\`[vite-dev-navigation] Emitting message: { type: '\${navigationType}', payload: { oldUrl: '\${oldUrlForMessage}', newUrl: '\${newUrl}' } }\`);
window.parent.postMessage({
type: navigationType, // 'pushState' or 'replaceState'
payload: {
oldUrl: oldUrlForMessage,
newUrl: newUrl // The URL passed to pushState/replaceState, resolved
}
}, '*'); // Consider a specific targetOrigin
} catch (e) {
console.error(\`[vite-dev-navigation] Error calling original \${navigationType}: \`, e);
// Optionally notify parent about the error during navigation attempt
window.parent.postMessage({
type: 'navigation-error',
payload: {
operation: navigationType,
message: e.message,
error: e.toString(),
stateAttempted: state, // Be careful sending state, might be large or sensitive
urlAttempted: url
}
}, '*');
}
};
history.pushState = function(state, title, url) {
handleStateChangeAndNotify(originalPushState, state, title, url);
};
history.replaceState = function(state, title, url) {
handleStateChangeAndNotify(originalReplaceState, state, title, url);
};
// --- Listener for Back/Forward Navigation (popstate event) ---
// We keep this listener primarily to update our internal 'previousUrl'
// and 'lastNavigationTimestamp' state so that subsequent push/replace
// messages report the correct 'oldUrl'. We no longer send messages from here.
window.addEventListener('popstate', (event) => {
const currentUrl = window.location.href;
// console.log('[vite-dev-navigation] Popstate event detected. Previous URL was:', previousUrl, 'New URL is:', currentUrl);
const newStateTimestamp = event.state?.[NAV_TIMESTAMP_KEY];
if (typeof newStateTimestamp === 'number') {
lastNavigationTimestamp = newStateTimestamp; // Update timestamp from popped state
// console.log('[vite-dev-navigation] Updated lastNavigationTimestamp from popstate:', lastNavigationTimestamp);
} else {
// console.warn('[vite-dev-navigation] Popstate event state missing navigation timestamp.');
// If timestamp is missing, we might lose track, but there's not much we can do reliably.
// Keep the last known timestamp.
}
// Update previousUrl to reflect the new reality AFTER the popstate event
previousUrl = currentUrl;
});
// --- Listener for Commands from Parent ---
window.addEventListener('message', (event) => {
// Security check: Ensure message is from parent
if (event.source !== window.parent || !event.data || typeof event.data !== 'object') {
return;
}
if (event.data.type === 'navigate') {
const direction = event.data.payload?.direction;
// console.log(\`[vite-dev-navigation] Received command: \${direction}\`);
if (direction === 'forward') {
history.forward();
} else if (direction === 'backward') {
history.back();
} else {
console.warn('[vite-dev-navigation] Received navigate command with invalid direction:', direction);
}
}
});
// --- Existing Error Handling ---
window.addEventListener('error', (event) => {
// console.log('[vite-dev-navigation] Forwarding error event to parent.');
window.parent.postMessage({
type: 'window-error',
payload: {
message: event.message,
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
error: event.error?.toString() // Include stack trace if available
}
}, '*');
});
window.addEventListener('unhandledrejection', (event) => {
// console.log('[vite-dev-navigation] Forwarding unhandledrejection event to parent.');
window.parent.postMessage({
type: 'unhandled-rejection',
payload: {
reason: event.reason instanceof Error ? event.reason.toString() : JSON.stringify(event.reason) // Attempt to serialize reason
}
}, '*');
});
// console.log('[vite-dev-navigation] Navigation/error script initialized inside iframe. Initial URL:', previousUrl, 'Initial Timestamp:', lastNavigationTimestamp);
})(); // End of IIFE
`,
},
],
};
},
};
}
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => ({
server: {
host: "::",
port: 8080,
},
plugins: [react(), devErrorAndNavigationPlugin()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
}));