diff --git a/apps/origin-pooling/App.tsx b/apps/origin-pooling/App.tsx new file mode 100644 index 0000000..dfcf3a5 --- /dev/null +++ b/apps/origin-pooling/App.tsx @@ -0,0 +1,339 @@ +/** + * Origin Pooling Demo + * + * Dynamically add/remove sandboxes under two shared origins (alpha, beta) + * plus isolated sandboxes (each gets a unique origin and its own VM). + * Same-origin sandboxes share a ReactHost / Hermes VM; removing the last + * one triggers the idle TTL. + * + * Access control demo: alpha and beta only accept messages from "isolated-1". + * Other isolated sandboxes (isolated-2, isolated-3, …) will get + * AccessDeniedError when trying to ping alpha or beta. + * + * Messaging is handled inside the sandbox widget via globalThis.postMessage. + * The host only logs messages received via onMessage. + */ +import SandboxReactNativeView from '@callstack/react-native-sandbox' +import React, {useCallback, useRef, useState} from 'react' +import { + Button, + Platform, + SafeAreaView, + ScrollView, + StatusBar, + StyleSheet, + Text, + View, +} from 'react-native' + +type SandboxEntry = {key: string; label: string; origin: string} +type LogEntry = {source: string; text: string; ts: number} + +let nextId = 0 +let nextIsolatedId = 0 + +const ORIGIN_ALPHA = 'alpha' +const ORIGIN_BETA = 'beta' +const ISOLATED_PREFIX = 'isolated-' +const COLOR_ALPHA = '#8232ff' +const COLOR_BETA = '#e67e22' +const COLOR_ISOLATED = '#6c757d' + +/** + * Only "isolated-1" is permitted to send messages to alpha/beta. + * All other isolated origins will be denied. + */ +const PERMITTED_ISOLATED = `${ISOLATED_PREFIX}1` + +/** Alpha uses a function-based TTL (4 seconds) */ +const ALPHA_TTL = () => 4000 +/** Beta and isolated use a static TTL (2 seconds) */ +const DEFAULT_TTL = 2000 + +export default function App() { + const [sandboxes, setSandboxes] = useState([]) + const [log, setLog] = useState([]) + const logScrollRef = useRef(null) + + const addLog = useCallback((source: string, text: string) => { + setLog(prev => [...prev.slice(-49), {source, text, ts: Date.now()}]) + }, []) + + const addSandbox = useCallback((origin: string) => { + const id = String(++nextId) + const actualOrigin = + origin === ISOLATED_PREFIX + ? `${ISOLATED_PREFIX}${++nextIsolatedId}` + : origin + setSandboxes(prev => [ + ...prev, + {key: id, label: `#${id}`, origin: actualOrigin}, + ]) + }, []) + + const removeSandbox = useCallback((key: string) => { + setSandboxes(prev => prev.filter(s => s.key !== key)) + }, []) + + const clearLog = useCallback(() => setLog([]), []) + + const alphas = sandboxes.filter(s => s.origin === ORIGIN_ALPHA) + const betas = sandboxes.filter(s => s.origin === ORIGIN_BETA) + const isolated = sandboxes.filter(s => s.origin.startsWith(ISOLATED_PREFIX)) + + return ( + + Origin Pooling Demo + + Same-origin sandboxes share a VM. Isolated sandboxes each get a unique + origin (own VM). Only isolated-1 can message alpha/beta — others get + AccessDeniedError. + + + +