@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.
- package/dist/Ryunix.esm.js +441 -28
- package/dist/Ryunix.esm.js.map +1 -1
- package/dist/Ryunix.umd.js +442 -27
- package/dist/Ryunix.umd.js.map +1 -1
- package/dist/Ryunix.umd.min.js +1 -1
- package/dist/Ryunix.umd.min.js.map +1 -1
- package/package.json +1 -1
package/dist/Ryunix.esm.js
CHANGED
|
@@ -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 (
|
|
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
|
-
|
|
1291
|
+
const newRoot = {
|
|
1275
1292
|
dom: activeRoot.dom,
|
|
1276
1293
|
props: activeRoot.props,
|
|
1277
1294
|
alternate: currentState.currentRoot || null,
|
|
1278
1295
|
};
|
|
1279
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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:
|
|
2746
|
+
console.log(`[Ryunix Debug] init: hasChildNodes=${hasChildNodes}, has SSR content detected.`);
|
|
2646
2747
|
}
|
|
2647
2748
|
|
|
2648
|
-
if
|
|
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')
|
|
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
|
-
|
|
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
|