@unsetsoft/ryunixjs 1.2.5-canary.0 → 1.2.5-canary.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,6 @@
1
1
  // Improved state management - avoid global mutable object
2
2
  // Instead, create a state manager that can be instantiated per render tree
3
3
 
4
-
5
4
  const rIC =
6
5
  typeof requestIdleCallback !== 'undefined'
7
6
  ? requestIdleCallback
@@ -18,7 +17,6 @@ const createRenderState = () => ({
18
17
  effects: [],
19
18
  });
20
19
 
21
- // Singleton for backward compatibility, but allows testing with isolated instances
22
20
  let globalState = createRenderState();
23
21
 
24
22
  const getState = () => globalState;
@@ -96,7 +94,12 @@ const is = {
96
94
 
97
95
  const nextValidSibling$1 = (node) => {
98
96
  let next = node;
99
- while (next && (next.nodeType === 3 && !next.nodeValue.trim() || next.nodeType === 8)) {
97
+ while (
98
+ next &&
99
+ ((next.nodeType === 3 && !next.nodeValue.trim()) ||
100
+ next.nodeType === 8 ||
101
+ (next.nodeType === 1 && next.hasAttribute('data-ryunix-ssr')))
102
+ ) {
100
103
  next = next.nextSibling;
101
104
  }
102
105
  return next
@@ -251,6 +254,7 @@ const isNew = (prev, next) => (key) => {
251
254
  */
252
255
  const isGone = (next) => (key) => !(key in next);
253
256
 
257
+
254
258
  /**
255
259
  * Cancel effects for a single fiber
256
260
  * @param {Object} fiber - Fiber node
@@ -712,6 +716,8 @@ const updateDom = (dom, prevProps = {}, nextProps = {}) => {
712
716
  });
713
717
  };
714
718
 
719
+
720
+
715
721
  /**
716
722
  * Clear all children from a DOM element
717
723
  * @param {HTMLElement} container - DOM element to clear
@@ -1071,6 +1077,7 @@ const reconcileChildren = (wipFiber, elements) => {
1071
1077
  alternate: matchedFiber,
1072
1078
  effectTag: EFFECT_TAGS.UPDATE,
1073
1079
  hooks: matchedFiber.hooks,
1080
+ stateError: matchedFiber.stateError,
1074
1081
  key: element.key,
1075
1082
  index,
1076
1083
  };
@@ -1215,6 +1222,11 @@ const haveDepsChanged = (oldDeps, newDeps) => {
1215
1222
  };
1216
1223
 
1217
1224
  const useStore = (initialState, priority = getCurrentPriority()) => {
1225
+ // SSR safety check - more reliable than state.isServerRendering
1226
+ if (typeof window === 'undefined') {
1227
+ return [is.function(initialState) ? initialState() : initialState, () => { }]
1228
+ }
1229
+
1218
1230
  const state = getState();
1219
1231
  if (state.isServerRendering) {
1220
1232
  return [is.function(initialState) ? initialState() : initialState, () => { }]
@@ -1227,6 +1239,11 @@ const useStore = (initialState, priority = getCurrentPriority()) => {
1227
1239
 
1228
1240
 
1229
1241
  const useReducer = (reducer, initialState, init, defaultPriority = getCurrentPriority()) => {
1242
+ // SSR safety check - more reliable than state.isServerRendering
1243
+ if (typeof window === 'undefined') {
1244
+ return [init ? init(initialState) : initialState, () => { }]
1245
+ }
1246
+
1230
1247
  const state = getState();
1231
1248
  if (state.isServerRendering) {
1232
1249
  return [init ? init(initialState) : initialState, () => { }]
@@ -1271,14 +1288,12 @@ const useReducer = (reducer, initialState, init, defaultPriority = getCurrentPri
1271
1288
 
1272
1289
  if (!activeRoot) return
1273
1290
 
1274
- currentState.wipRoot = {
1291
+ const newRoot = {
1275
1292
  dom: activeRoot.dom,
1276
1293
  props: activeRoot.props,
1277
1294
  alternate: currentState.currentRoot || null,
1278
1295
  };
1279
- currentState.deletions = [];
1280
- currentState.hookIndex = 0;
1281
- queueUpdate(() => scheduleWork$1(currentState.wipRoot, priority));
1296
+ queueUpdate(() => scheduleWork$1(newRoot, priority));
1282
1297
  };
1283
1298
 
1284
1299
  wipFiber.hooks[hookIndex] = hook;
@@ -1298,6 +1313,11 @@ const useReducer = (reducer, initialState, init, defaultPriority = getCurrentPri
1298
1313
  * of the values in the `deps` array have changed since the last render. If the `deps` array
1299
1314
  */
1300
1315
  const useEffect = (callback, deps) => {
1316
+ // SSR safety check - more reliable than state.isServerRendering
1317
+ if (typeof window === 'undefined') {
1318
+ return
1319
+ }
1320
+
1301
1321
  const state = getState();
1302
1322
  if (state.isServerRendering) {
1303
1323
  return
@@ -1337,6 +1357,11 @@ const useEffect = (callback, deps) => {
1337
1357
  * contains the initial value passed to the `useRef` function.
1338
1358
  */
1339
1359
  const useRef = (initialValue) => {
1360
+ // SSR safety check - more reliable than state.isServerRendering
1361
+ if (typeof window === 'undefined') {
1362
+ return { current: initialValue }
1363
+ }
1364
+
1340
1365
  const state = getState();
1341
1366
  if (state.isServerRendering) {
1342
1367
  return { current: initialValue }
@@ -1371,6 +1396,11 @@ const useRef = (initialValue) => {
1371
1396
  * @returns The `useMemo` function is returning the `value` calculated by the `compute` function.
1372
1397
  */
1373
1398
  const useMemo = (compute, deps) => {
1399
+ // SSR safety check - more reliable than state.isServerRendering
1400
+ if (typeof window === 'undefined') {
1401
+ return compute()
1402
+ }
1403
+
1374
1404
  const state = getState();
1375
1405
  if (state.isServerRendering) {
1376
1406
  return compute()
@@ -1649,8 +1679,26 @@ const findRoute = (routes, path) => {
1649
1679
  * `value` prop set to `contextValue`, and wrapping the `children` within a `Fragment`.
1650
1680
  */
1651
1681
  const RouterProvider = ({ routes, children }) => {
1682
+ // SSR: Return server-safe version without hooks
1683
+ if (typeof window === 'undefined') {
1684
+ const location = '/';
1685
+ const currentRouteData = findRoute(routes, location) || {};
1686
+ const contextValue = {
1687
+ location,
1688
+ params: currentRouteData.params || {},
1689
+ query: {},
1690
+ navigate: () => {},
1691
+ route: currentRouteData.route,
1692
+ };
1693
+ return createElement(
1694
+ RouterContext.Provider,
1695
+ { value: contextValue },
1696
+ Fragment({ children }),
1697
+ )
1698
+ }
1699
+
1652
1700
  const [location, setLocation] = useStore(
1653
- typeof window !== 'undefined' ? window.location.pathname : '/'
1701
+ window.location.pathname
1654
1702
  );
1655
1703
 
1656
1704
  useEffect(() => {
@@ -1955,6 +2003,11 @@ const useSwitch = (initialState = false) => {
1955
2003
  * @param {Array} deps - Dependencies array
1956
2004
  */
1957
2005
  const useLayoutEffect = (callback, deps) => {
2006
+ // SSR safety check - more reliable than state.isServerRendering
2007
+ if (typeof window === 'undefined') {
2008
+ return
2009
+ }
2010
+
1958
2011
  const state = getState();
1959
2012
  if (state.isServerRendering) {
1960
2013
  return
@@ -1989,6 +2042,14 @@ const useLayoutEffect = (callback, deps) => {
1989
2042
  // Counter for deterministic ID generation
1990
2043
  let idCounter = 0;
1991
2044
 
2045
+ /**
2046
+ * Reset the idCounter for useId - call this before each SSR renderToString
2047
+ * to ensure deterministic IDs across multiple renders
2048
+ */
2049
+ const resetIdCounter = () => {
2050
+ idCounter = 0;
2051
+ };
2052
+
1992
2053
  /**
1993
2054
  * useId - Generate a deterministic, unique ID that is stable across SSR and hydration.
1994
2055
  * @returns {string} A unique ID string
@@ -2076,6 +2137,7 @@ var hooks = /*#__PURE__*/Object.freeze({
2076
2137
  NavLink: NavLink,
2077
2138
  RouterProvider: RouterProvider,
2078
2139
  createContext: createContext,
2140
+ resetIdCounter: resetIdCounter,
2079
2141
  useCallback: useCallback,
2080
2142
  useDebounce: useDebounce,
2081
2143
  useDeferredValue: useDeferredValue,
@@ -2110,6 +2172,21 @@ const updateFunctionComponent = (fiber) => {
2110
2172
  fiber.effectTag = EFFECT_TAGS.HYDRATE;
2111
2173
  }
2112
2174
 
2175
+ // Memo bailout: skip re-render if props haven't changed
2176
+ if (fiber.type._isMemo && fiber.alternate) {
2177
+ const { children: _pc, ...prevRest } = fiber.alternate.props || {};
2178
+ const { children: _nc, ...nextRest } = fiber.props || {};
2179
+ if (fiber.type._arePropsEqual(prevRest, nextRest)) {
2180
+ fiber.hooks = fiber.alternate.hooks;
2181
+ const oldChild = fiber.alternate.child;
2182
+ if (oldChild) {
2183
+ oldChild.parent = fiber;
2184
+ fiber.child = oldChild;
2185
+ }
2186
+ return
2187
+ }
2188
+ }
2189
+
2113
2190
  let children = [fiber.type(fiber.props)];
2114
2191
 
2115
2192
  if (fiber.type._contextId && fiber.props.value !== undefined) {
@@ -2429,6 +2506,22 @@ function performUnitOfWork(fiber) {
2429
2506
  } catch (error) {
2430
2507
  if (process.env.NODE_ENV !== 'production') {
2431
2508
  console.error('[Ryunix ErrorBoundary] Caught error during render:', error);
2509
+
2510
+ try {
2511
+ // Attempt to attach original JSX source map for DevOverlay lookup
2512
+ const src = fiber.props && fiber.props.__source;
2513
+ if (src && error && typeof error === 'object') {
2514
+ error.__ryunix_source = src;
2515
+ }
2516
+
2517
+ let targetFiber = fiber;
2518
+ while (!error.__ryunix_source && targetFiber) {
2519
+ if (targetFiber.props && targetFiber.props.__source) {
2520
+ error.__ryunix_source = targetFiber.props.__source;
2521
+ }
2522
+ targetFiber = targetFiber.parent;
2523
+ }
2524
+ } catch(e) {}
2432
2525
  }
2433
2526
 
2434
2527
  // Traverse upwards to find nearest ErrorBoundary
@@ -2530,7 +2623,7 @@ const workLoop = (deadline) => {
2530
2623
  }
2531
2624
  };
2532
2625
 
2533
- // ... performUnitOfWork stays same ...
2626
+
2534
2627
 
2535
2628
  const scheduleWork = (root, priority = getCurrentPriority()) => {
2536
2629
  const state = getState();
@@ -2603,7 +2696,12 @@ const render = (element, container) => {
2603
2696
 
2604
2697
  const nextValidSibling = (node) => {
2605
2698
  let next = node;
2606
- while (next && (next.nodeType === 3 && !next.nodeValue.trim() || next.nodeType === 8)) {
2699
+ while (
2700
+ next &&
2701
+ ((next.nodeType === 3 && !next.nodeValue.trim()) ||
2702
+ next.nodeType === 8 ||
2703
+ (next.nodeType === 1 && next.hasAttribute('data-ryunix-ssr')))
2704
+ ) {
2607
2705
  next = next.nextSibling;
2608
2706
  }
2609
2707
  return next
@@ -2641,11 +2739,17 @@ const init = (MainElement, root = '__ryunix', components = {}) => {
2641
2739
  state.isHydrating = false;
2642
2740
  state.hydrationFailed = false;
2643
2741
 
2742
+ // Auto-detect SSR based on child nodes - no need to manually set process.env.RYUNIX_SSR
2743
+ const hasChildNodes = state.containerRoot && state.containerRoot.hasChildNodes();
2744
+
2644
2745
  if (process.env.NODE_ENV !== 'production' && process.env.RYUNIX_DEBUG) {
2645
- console.log(`[Ryunix Debug] init: RYUNIX_SSR=${process.env.RYUNIX_SSR}, hasChildNodes=${state.containerRoot.hasChildNodes()}`);
2746
+ console.log(`[Ryunix Debug] init: hasChildNodes=${hasChildNodes}, has SSR content detected.`);
2646
2747
  }
2647
2748
 
2648
- if (process.env.RYUNIX_SSR && state.containerRoot.hasChildNodes()) {
2749
+ // Auto-detect: if there's existing content, try to hydrate (SSR)
2750
+ // If explicitly disabled via RYUNIX_SSR=false, skip hydration
2751
+ const ssrEnabled = process.env.RYUNIX_SSR !== 'false';
2752
+ if (hasChildNodes && ssrEnabled) {
2649
2753
  if (process.env.NODE_ENV !== 'production' && process.env.RYUNIX_DEBUG) {
2650
2754
  console.log(`[Ryunix Debug] init: SSR content detected. Starting hydration on #${root}`);
2651
2755
  }
@@ -2776,7 +2880,7 @@ const renderToStringImpl = (element) => {
2776
2880
  if (value) {
2777
2881
  attributes += ` class="${escapeHtml(value)}"`;
2778
2882
  }
2779
- } else if (!key.startsWith('on')) { // Ignore event listeners
2883
+ } else if (!key.startsWith('on') && key !== '__source' && key !== '__self') {
2780
2884
  if (typeof value === 'boolean') {
2781
2885
  if (value) attributes += ` ${key}=""`;
2782
2886
  } else if (value != null) {
@@ -2934,7 +3038,7 @@ const renderToStreamImpl = async (element, push, suspenseTasks = []) => {
2934
3038
  if (value) {
2935
3039
  attributes += ` class="${escapeHtml(value)}"`;
2936
3040
  }
2937
- } else if (!key.startsWith('on')) {
3041
+ } else if (!key.startsWith('on') && key !== '__source' && key !== '__self') {
2938
3042
  if (typeof value === 'boolean') {
2939
3043
  if (value) attributes += ` ${key}=""`;
2940
3044
  } else if (value != null) {
@@ -2968,6 +3072,9 @@ const renderToReadableStream = (element, options = {}) => {
2968
3072
  const state = getState();
2969
3073
  const encoder = new TextEncoder();
2970
3074
 
3075
+ // Reset idCounter for deterministic useId values
3076
+ resetIdCounter();
3077
+
2971
3078
  return new ReadableStream({
2972
3079
  async start(controller) {
2973
3080
  const wasServerRendering = state.isServerRendering;
@@ -2980,7 +3087,7 @@ const renderToReadableStream = (element, options = {}) => {
2980
3087
  try {
2981
3088
  // 0. Inject RC helper script first
2982
3089
  const nonceAttr = options.nonce ? ` nonce="${options.nonce}"` : '';
2983
- push(`<script${nonceAttr}>${RC_SCRIPT}</script>`);
3090
+ push(`<script${nonceAttr} data-ryunix-ssr>${RC_SCRIPT}</script>`);
2984
3091
 
2985
3092
  // 1. Render initial tree (with fallbacks)
2986
3093
  await renderToStreamImpl(element, push, suspenseTasks);
@@ -2992,8 +3099,8 @@ const renderToReadableStream = (element, options = {}) => {
2992
3099
  const task = suspenseTasks.shift();
2993
3100
  const res = await task;
2994
3101
  if (res.success) {
2995
- push(`<template id="P:${res.id}">${res.content}</template>`);
2996
- push(`<script${nonceAttr}>$RC("S:${res.id}", "P:${res.id}")</script>`);
3102
+ push(`<template id="P:${res.id}" data-ryunix-ssr>${res.content}</template>`);
3103
+ push(`<script${nonceAttr} data-ryunix-ssr>$RC("S:${res.id}", "P:${res.id}")</script>`);
2997
3104
  }
2998
3105
  }
2999
3106
 
@@ -3012,6 +3119,10 @@ const renderToString = (element, options = {}) => {
3012
3119
  const wasServerRendering = state.isServerRendering;
3013
3120
  state.isServerRendering = true;
3014
3121
  state.ssrMetadata = {};
3122
+
3123
+ // Reset idCounter for deterministic useId values
3124
+ resetIdCounter();
3125
+
3015
3126
  try {
3016
3127
  return renderToStringImpl(element)
3017
3128
  } finally {
@@ -3042,18 +3153,12 @@ const renderToStringAsync = async (element, options = {}) => {
3042
3153
  * @returns {Function} Memoized component
3043
3154
  */
3044
3155
  const memo = (Component, arePropsEqual = shallowEqual) => {
3045
- let prevProps = null;
3046
- let prevResult = null;
3047
-
3048
3156
  const MemoizedComponent = (props) => {
3049
- if (prevProps && arePropsEqual(prevProps, props)) {
3050
- return prevResult
3051
- }
3052
- prevProps = props;
3053
- prevResult = Component(props);
3054
- return prevResult
3157
+ return Component(props)
3055
3158
  };
3056
-
3159
+ MemoizedComponent._isMemo = true;
3160
+ MemoizedComponent._wrappedComponent = Component;
3161
+ MemoizedComponent._arePropsEqual = arePropsEqual;
3057
3162
  MemoizedComponent.displayName = `Memo(${Component.displayName || Component.name || 'Component'})`;
3058
3163
  return MemoizedComponent
3059
3164
  };
@@ -3294,6 +3399,312 @@ function createActionProxy(actionId) {
3294
3399
  };
3295
3400
  }
3296
3401
 
3402
+ function RyunixDevOverlay(propsOrError) {
3403
+ // If propsOrError is an event or wrapped object, try to extract error
3404
+ const rawError = propsOrError && propsOrError.nativeEvent
3405
+ ? propsOrError.error
3406
+ : propsOrError;
3407
+
3408
+ let error = rawError instanceof Error || (rawError && rawError.message)
3409
+ ? rawError
3410
+ : (rawError?.error || rawError);
3411
+
3412
+ // Debug string if error is broken
3413
+ const debugObjectStr = JSON.stringify(propsOrError, Object.getOwnPropertyNames(propsOrError || {}));
3414
+
3415
+ const [snippet, setSnippet] = useStore(null);
3416
+ const [startLine, setStartLine] = useStore(1);
3417
+ const [errorFile, setErrorFile] = useStore('');
3418
+ const [errorLine, setErrorLine] = useStore(0);
3419
+
3420
+ // Normalize stack ensuring we have lines
3421
+ let stackLines = [];
3422
+ if (error && error.stack) {
3423
+ stackLines = typeof error.stack === 'string' ? error.stack.split('\n').filter(line => {
3424
+ const trimmed = line.trim();
3425
+ if (!trimmed) return false;
3426
+ if (trimmed.includes('node_modules')) return false;
3427
+ // Filter out internal Ryunix core framework files to isolate user code
3428
+ const isInternal = [
3429
+ 'components.js', 'workers.js', 'reconciler.js', 'commits.js',
3430
+ 'hooks.js', 'errorBoundary.js', 'serverBoundary.js', 'app-router.js',
3431
+ 'app-router-server.js', 'render.js', 'createElement.js', 'index.js'
3432
+ ].some(file => trimmed.includes(file));
3433
+ return !isInternal;
3434
+ }) : error.stack;
3435
+ }
3436
+
3437
+ const errorName = error && error.name ? error.name : 'Unknown Error Type';
3438
+ const errorMessage = error && error.message ? error.message : `Raw unhandled error. Debug: ${debugObjectStr}`;
3439
+
3440
+ useEffect(() => {
3441
+ let targetPath = null;
3442
+ let targetLine = null;
3443
+
3444
+ // 1. Direct JSX __source mapping (injected by Webpack/SWC)
3445
+ if (error && error.__ryunix_source && error.__ryunix_source.fileName) {
3446
+ targetPath = error.__ryunix_source.fileName;
3447
+ targetLine = error.__ryunix_source.lineNumber;
3448
+ }
3449
+
3450
+ // 2. Fallback to Regex Stack Parsing
3451
+ if (!targetPath || !targetLine) {
3452
+ for (let i = 0; i < stackLines.length; i++) {
3453
+ const line = stackLines[i];
3454
+ if (!line.includes(':')) continue;
3455
+
3456
+ // Deterministic string-based parsing (no regex on uncontrolled data)
3457
+ let matchedPath = null;
3458
+ let matchedLine = null;
3459
+
3460
+ // V8 format: "at fn (file:line:col)" — extract content between parens
3461
+ const parenOpen = line.indexOf('(');
3462
+ const parenClose = line.lastIndexOf(')');
3463
+ if (parenOpen !== -1 && parenClose > parenOpen) {
3464
+ const inner = line.slice(parenOpen + 1, parenClose);
3465
+ const c2 = inner.lastIndexOf(':');
3466
+ const c1 = c2 > 0 ? inner.lastIndexOf(':', c2 - 1) : -1;
3467
+ if (c1 > 0) {
3468
+ const col = inner.slice(c2 + 1);
3469
+ const ln = inner.slice(c1 + 1, c2);
3470
+ if (/^\d+$/.test(ln) && /^\d+$/.test(col)) {
3471
+ matchedPath = inner.slice(0, c1);
3472
+ matchedLine = parseInt(ln, 10);
3473
+ }
3474
+ }
3475
+ }
3476
+
3477
+ // V8 format without parens: "at file:line:col"
3478
+ if (!matchedPath) {
3479
+ const trimmed = line.trim();
3480
+ if (trimmed.startsWith('at ')) {
3481
+ const rest = trimmed.slice(3).trim();
3482
+ const c2 = rest.lastIndexOf(':');
3483
+ const c1 = c2 > 0 ? rest.lastIndexOf(':', c2 - 1) : -1;
3484
+ if (c1 > 0) {
3485
+ const col = rest.slice(c2 + 1);
3486
+ const ln = rest.slice(c1 + 1, c2);
3487
+ if (/^\d+$/.test(ln) && /^\d+$/.test(col)) {
3488
+ matchedPath = rest.slice(0, c1);
3489
+ matchedLine = parseInt(ln, 10);
3490
+ }
3491
+ }
3492
+ }
3493
+ }
3494
+
3495
+ // Ryunix format: "file.ryx:line" or "file.jsx:line"
3496
+ if (!matchedPath) {
3497
+ const exts = ['.ryx', '.jsx', '.js', '.ts', '.tsx'];
3498
+ const c1 = line.lastIndexOf(':');
3499
+ if (c1 > 0) {
3500
+ const ln = line.slice(c1 + 1).trim();
3501
+ const filePart = line.slice(0, c1).trim();
3502
+ if (/^\d+$/.test(ln) && exts.some(ext => filePart.endsWith(ext))) {
3503
+ matchedPath = filePart;
3504
+ matchedLine = parseInt(ln, 10);
3505
+ }
3506
+ }
3507
+ }
3508
+
3509
+ if (matchedPath && matchedLine) {
3510
+ targetPath = matchedPath;
3511
+ targetLine = matchedLine;
3512
+ break;
3513
+ }
3514
+ }
3515
+ }
3516
+
3517
+ if (targetPath && targetLine) {
3518
+ setErrorFile(targetPath);
3519
+ setErrorLine(targetLine);
3520
+ fetch(`/_ryunix/source?file=${encodeURIComponent(targetPath)}&line=${targetLine}`)
3521
+ .then(res => res.json())
3522
+ .then(data => {
3523
+ if (data.snippet) {
3524
+ setSnippet(data.snippet);
3525
+ setStartLine(data.startLine);
3526
+ }
3527
+ })
3528
+ .catch(err => console.error('Failed to fetch source snippet', err));
3529
+ }
3530
+ }, [error]);
3531
+
3532
+ const overlayStyle = {
3533
+ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, zIndex: 2147483647,
3534
+ backgroundColor: 'rgba(0, 0, 0, 0.85)', backdropFilter: 'blur(8px)',
3535
+ display: 'flex', alignItems: 'center', justifyContent: 'center',
3536
+ padding: '20px', fontFamily: 'system-ui, -apple-system, sans-serif'
3537
+ };
3538
+
3539
+ const modalStyle = {
3540
+ backgroundColor: '#0c0c0c', width: '100%', maxWidth: '1000px', maxHeight: '90vh',
3541
+ borderRadius: '12px', boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.8)',
3542
+ display: 'flex', flexDirection: 'column', overflow: 'hidden',
3543
+ border: '1px solid #333'
3544
+ };
3545
+
3546
+ const headerStyle = {
3547
+ backgroundColor: '#161616', padding: '16px 24px', borderBottom: '1px solid #333',
3548
+ display: 'flex', justifyContent: 'space-between', alignItems: 'center'
3549
+ };
3550
+
3551
+ const badgeStyle = {
3552
+ backgroundColor: 'rgba(239, 68, 68, 0.2)', color: '#ef4444', padding: '4px 8px',
3553
+ borderRadius: '4px', fontSize: '13px', fontWeight: 'bold', textTransform: 'uppercase',
3554
+ letterSpacing: '0.05em'
3555
+ };
3556
+
3557
+ const contentStyle = {
3558
+ padding: '32px', overflowY: 'auto', flex: 1, color: '#fff'
3559
+ };
3560
+
3561
+ const titleStyle = {
3562
+ fontSize: '24px', fontWeight: 'bold', marginBottom: '24px',
3563
+ fontFamily: 'ui-monospace, monospace', wordBreak: 'break-word', lineHeight: 1.4
3564
+ };
3565
+
3566
+ const snippetContainerStyle = {
3567
+ backgroundColor: '#000', borderRadius: '8px', border: '1px solid #333',
3568
+ padding: '16px', fontFamily: 'ui-monospace, monospace', fontSize: '14px',
3569
+ overflowX: 'auto', color: '#d1d5db', marginBottom: '32px',
3570
+ whiteSpace: 'pre-wrap',
3571
+ maxHeight: '150px',
3572
+ height: 'auto'
3573
+ };
3574
+
3575
+ const lineStyle = (isErrorLine) => ({
3576
+ display: 'flex',
3577
+ backgroundColor: isErrorLine ? 'rgba(239, 68, 68, 0.15)' : 'transparent',
3578
+ padding: '2px 8px',
3579
+ borderRadius: '4px',
3580
+ borderLeft: isErrorLine ? '3px solid #ef4444' : '3px solid transparent'
3581
+ });
3582
+
3583
+ const snippetLines = snippet ? snippet.split('\n') : [];
3584
+
3585
+ let badgeText = 'UNHANDLED RUNTIME ERROR';
3586
+ if (errorName && errorName !== 'Error' && errorName !== 'Unknown Error Type') {
3587
+ badgeText = errorName.replace(/([a-z])([A-Z])/g, '$1 $2').toUpperCase();
3588
+ }
3589
+
3590
+ return createElement('div', { style: overlayStyle },
3591
+ createElement('div', { style: modalStyle },
3592
+ createElement('div', { style: headerStyle },
3593
+ createElement('div', { style: { display: 'flex', alignItems: 'center', gap: '12px' } },
3594
+ createElement('span', { style: badgeStyle }, badgeText),
3595
+ createElement('span', { style: { color: '#9ca3af', fontSize: '14px' } }, 'Ryunix Development')
3596
+ ),
3597
+ createElement('button', {
3598
+ onClick: () => window.location.reload(),
3599
+ style: { background: 'none', border: 'none', color: '#9ca3af', cursor: 'pointer', outline: 'none' },
3600
+ title: 'Reload page'
3601
+ },
3602
+ createElement('svg', { width: '20', height: '20', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round' },
3603
+ createElement('path', { d: 'M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8' }),
3604
+ createElement('path', { d: 'M3 3v5h5' })
3605
+ )
3606
+ )
3607
+ ),
3608
+ createElement('div', { style: contentStyle },
3609
+ createElement('h1', { style: titleStyle },
3610
+ createElement('span', { style: { color: '#f87171' } }, errorName),
3611
+ ': ', errorMessage
3612
+ ),
3613
+
3614
+ errorFile && createElement('div', { style: { marginBottom: '16px', color: '#9ca3af', fontSize: '14px', display: 'flex', alignItems: 'center', gap: '8px' } },
3615
+ createElement('svg', { width: '16', height: '16', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round' },
3616
+ createElement('path', { d: 'M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z' }),
3617
+ createElement('polyline', { points: '13 2 13 9 20 9' })
3618
+ ),
3619
+ errorFile, ':', errorLine
3620
+ ),
3621
+
3622
+ snippet && createElement('div', { style: snippetContainerStyle },
3623
+ createElement('div', { style: { display: 'flex', flexDirection: 'column' } },
3624
+ ...snippetLines.map((lineText, index) => {
3625
+ const currentLineNumber = startLine + index;
3626
+ const isErrorLine = currentLineNumber === errorLine;
3627
+ return createElement('div', { key: index, style: lineStyle(isErrorLine) },
3628
+ createElement('span', { style: { color: '#6b7280', width: '40px', userSelect: 'none', textAlign: 'right', marginRight: '16px', display: 'inline-block' } }, currentLineNumber),
3629
+ createElement('span', { style: { color: isErrorLine ? '#f87171' : '#e5e7eb', whiteSpace: 'pre' } }, lineText || ' ')
3630
+ );
3631
+ })
3632
+ )
3633
+ ),
3634
+
3635
+ createElement('div', { style: { marginBottom: '16px' } },
3636
+ createElement('p', { style: { color: '#9ca3af', fontSize: '14px', marginBottom: '8px', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.05em' } }, 'Call Stack'),
3637
+ createElement('div', { style: snippetContainerStyle },
3638
+ stackLines.length > 0 ? createElement('ul', { style: { listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexDirection: 'column', gap: '12px' } },
3639
+ ...stackLines.map((line, i) => {
3640
+ if (i === 0 && (line.startsWith('Error:') || line.startsWith('TypeError:'))) return null;
3641
+
3642
+ // Deterministic string-based stack frame parsing (no polynomial regex)
3643
+ const trimmed = line.trim();
3644
+ let fnName = '<anonymous>';
3645
+ let filePath = line;
3646
+
3647
+ // V8 format: "at fnName (file:line:col)" or "at file:line:col"
3648
+ if (trimmed.startsWith('at ')) {
3649
+ const rest = trimmed.slice(3);
3650
+ const parenOpen = rest.indexOf('(');
3651
+ const parenClose = rest.lastIndexOf(')');
3652
+ if (parenOpen !== -1 && parenClose > parenOpen) {
3653
+ fnName = rest.slice(0, parenOpen).trim() || '<anonymous>';
3654
+ filePath = rest.slice(parenOpen + 1, parenClose);
3655
+ } else {
3656
+ // "at file:line:col" — no function name
3657
+ if (rest.includes(':')) {
3658
+ fnName = '<anonymous>';
3659
+ } else {
3660
+ fnName = rest;
3661
+ }
3662
+ filePath = rest;
3663
+ }
3664
+ }
3665
+ // Firefox format: "fnName@file:line:col"
3666
+ else if (trimmed.includes('@')) {
3667
+ const atIdx = trimmed.indexOf('@');
3668
+ fnName = trimmed.slice(0, atIdx) || '<anonymous>';
3669
+ filePath = trimmed.slice(atIdx + 1);
3670
+ }
3671
+ // Ryunix format: "fnName file.ext:line"
3672
+ else {
3673
+ const exts = ['.ryx', '.jsx', '.js', '.ts', '.tsx'];
3674
+ const parts = trimmed.split(/\s+/);
3675
+ if (parts.length >= 2) {
3676
+ const lastPart = parts[parts.length - 1];
3677
+ const colonIdx = lastPart.indexOf(':');
3678
+ const fileCandidate = colonIdx > 0 ? lastPart.slice(0, colonIdx) : lastPart;
3679
+ if (exts.some(ext => fileCandidate.endsWith(ext))) {
3680
+ fnName = parts.slice(0, -1).join(' ');
3681
+ filePath = lastPart;
3682
+ } else {
3683
+ fnName = parts[0];
3684
+ filePath = parts.slice(1).join(' ');
3685
+ }
3686
+ }
3687
+ }
3688
+
3689
+ return createElement('li', { key: i },
3690
+ createElement('span', { style: { color: '#60a5fa', fontWeight: 600 } }, fnName),
3691
+ createElement('div', { style: { color: '#6b7280', marginTop: '4px', paddingLeft: '16px', display: 'flex', alignItems: 'center', gap: '8px' } },
3692
+ createElement('svg', { width: '12', height: '12', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round' },
3693
+ createElement('path', { d: 'M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z' }),
3694
+ createElement('polyline', { points: '13 2 13 9 20 9' })
3695
+ ),
3696
+ filePath
3697
+ )
3698
+ );
3699
+ })
3700
+ ) : createElement('div', { style: { color: '#6b7280', fontStyle: 'italic' } }, 'No stack trace available.')
3701
+ )
3702
+ )
3703
+ )
3704
+ )
3705
+ );
3706
+ }
3707
+
3297
3708
  var Ryunix = /*#__PURE__*/Object.freeze({
3298
3709
  __proto__: null,
3299
3710
  Children: Children,
@@ -3304,6 +3715,7 @@ var Ryunix = /*#__PURE__*/Object.freeze({
3304
3715
  NavLink: NavLink,
3305
3716
  Priority: Priority,
3306
3717
  RouterProvider: RouterProvider,
3718
+ RyunixDevOverlay: RyunixDevOverlay,
3307
3719
  ServerBoundary: ServerBoundary,
3308
3720
  Suspense: Suspense,
3309
3721
  batchUpdates: batchUpdates,
@@ -3327,6 +3739,7 @@ var Ryunix = /*#__PURE__*/Object.freeze({
3327
3739
  renderToReadableStream: renderToReadableStream,
3328
3740
  renderToString: renderToString,
3329
3741
  renderToStringAsync: renderToStringAsync,
3742
+ resetIdCounter: resetIdCounter,
3330
3743
  safeRender: safeRender,
3331
3744
  shallowEqual: shallowEqual,
3332
3745
  useCallback: useCallback,
@@ -3357,5 +3770,5 @@ var Ryunix = /*#__PURE__*/Object.freeze({
3357
3770
 
3358
3771
  if (typeof window !== 'undefined') window.Ryunix = Ryunix;
3359
3772
 
3360
- export { Children, ErrorBoundary, Fragment, hooks as Hooks, Image, Link, MDXContent, MDXProvider, NavLink, Priority, RouterProvider, ServerBoundary, Suspense, batchUpdates, cloneElement, createActionProxy, createContext, createElement, createPortal, deepEqual, Ryunix as default, defaultComponents, escapeHtml, forwardRef, getMDXComponents, getState, hydrate, init, isValidElement, lazy, memo, preload, profiler, render, renderToReadableStream, renderToString, renderToStringAsync, safeRender, shallowEqual, useCallback, useDebounce, useDeferredValue, useEffect, useHash, useId, useLayoutEffect, useMDXComponents, useMemo, useMetadata, usePathname, usePersistentStore, usePersistentStore as usePersitentStore, useProfiler, useQuery, useReducer, useRef, useRouter, useSearchParams, useStore, useStorePriority, useSwitch, useThrottle, useTransition, withProfiler };
3773
+ export { Children, ErrorBoundary, Fragment, hooks as Hooks, Image, Link, MDXContent, MDXProvider, NavLink, Priority, RouterProvider, RyunixDevOverlay, ServerBoundary, Suspense, batchUpdates, cloneElement, createActionProxy, createContext, createElement, createPortal, deepEqual, Ryunix as default, defaultComponents, escapeHtml, forwardRef, getMDXComponents, getState, hydrate, init, isValidElement, lazy, memo, preload, profiler, render, renderToReadableStream, renderToString, renderToStringAsync, resetIdCounter, safeRender, shallowEqual, useCallback, useDebounce, useDeferredValue, useEffect, useHash, useId, useLayoutEffect, useMDXComponents, useMemo, useMetadata, usePathname, usePersistentStore, usePersistentStore as usePersitentStore, useProfiler, useQuery, useReducer, useRef, useRouter, useSearchParams, useStore, useStorePriority, useSwitch, useThrottle, useTransition, withProfiler };
3361
3774
  //# sourceMappingURL=Ryunix.esm.js.map