@unsetsoft/ryunixjs 1.2.3-canary.8 → 1.2.4-canary.1

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.
@@ -56,6 +56,17 @@ const EFFECT_TAGS = Object.freeze({
56
56
  NO_EFFECT: Symbol.for('ryunix.reconciler.status.no_effect'),
57
57
  });
58
58
 
59
+ const DANGEROUSLY_SET_INNER_HTML = 'dangerousInjection';
60
+
61
+ const DANGEROUS_PROPERTIES = Object.freeze([
62
+ 'innerHTML',
63
+ 'outerHTML',
64
+ 'insertAdjacentHTML',
65
+ 'insertAdjacentText',
66
+ 'insertAdjacentElement',
67
+ 'setHTML',
68
+ ]);
69
+
59
70
  /**
60
71
  * Type checking utilities
61
72
  */
@@ -70,7 +81,12 @@ const is = {
70
81
  };
71
82
 
72
83
  /**
73
- * Create text element
84
+ * The `createTextElement` function creates a text element with the specified text content.
85
+ * @param text - The `text` parameter in the `createTextElement` function is the text content that you
86
+ * want to create a text element for. This text will be set as the `nodeValue` of the text element in
87
+ * the returned object.
88
+ * @returns A text element object is being returned with a type of RYUNIX_TYPES.TEXT_ELEMENT and props
89
+ * containing the text value provided in the function argument.
74
90
  */
75
91
  const createTextElement = (text) => {
76
92
  return {
@@ -83,7 +99,21 @@ const createTextElement = (text) => {
83
99
  };
84
100
 
85
101
  /**
86
- * Create element
102
+ * The `createElement` function creates a virtual DOM element with specified type, properties, and
103
+ * children.
104
+ * @param type - The `type` parameter in the `createElement` function represents the type of element
105
+ * you want to create, such as a HTML tag like 'div', 'span', 'p', etc.
106
+ * @param props - The `props` parameter in the `createElement` function is an object that contains the
107
+ * properties or attributes for the element being created. These properties can include things like
108
+ * class names, styles, event handlers, and any other custom attributes you want to assign to the
109
+ * element. In the code snippet you provided,
110
+ * @param children - The `children` parameter in the `createElement` function represents the child
111
+ * elements or text content that will be nested within the created element. These children can be
112
+ * passed as arguments to the `createElement` function and will be rendered as part of the element's
113
+ * content.
114
+ * @returns An object is being returned with a `type` property representing the type of element, and a
115
+ * `props` property containing the element's properties. The `props` object includes the children of
116
+ * the element, which are processed to ensure they are in the correct format.
87
117
  */
88
118
  const createElement = (type, props, ...children) => {
89
119
  const safeProps = props || {};
@@ -102,7 +132,14 @@ const createElement = (type, props, ...children) => {
102
132
  };
103
133
 
104
134
  /**
105
- * Fragment component
135
+ * The `Fragment` function in JavaScript creates a fragment element with the given children.
136
+ * @param props - The `props` parameter in the `Fragment` function is an object that contains the
137
+ * properties passed to the `Fragment` component. These properties can include `children`, which
138
+ * represents the child elements or components nested within the `Fragment`.
139
+ * @returns The `Fragment` component is returning a Ryunix fragment element created using the
140
+ * `createElement` function. The element is of type `RYUNIX_TYPES.RYUNIX_FRAGMENT` and contains the
141
+ * children passed to the `Fragment` component. If `props.children` is not an array, it is converted
142
+ * into an array before being spread into the `createElement` function.
106
143
  */
107
144
  const Fragment = (props) => {
108
145
  const children = Array.isArray(props.children)
@@ -243,6 +280,71 @@ const runEffects = (fiber) => {
243
280
  }
244
281
  };
245
282
 
283
+ /**
284
+ * Checks if a property is dangerous and should be blocked
285
+ * @param {string} propName - Property name
286
+ * @returns {boolean} True if the property is dangerous
287
+ */
288
+ const isDangerousProperty = (propName) => {
289
+ return DANGEROUS_PROPERTIES.includes(propName)
290
+ };
291
+
292
+ /**
293
+ * Validates and applies DANGEROUSLY_SET_INNER_HTML safely
294
+ * @param {HTMLElement} dom - DOM element
295
+ * @param {Object} DANGEROUSLY_SET_INNER_HTML - Object with __html property
296
+ */
297
+ const applyDangerouslySetInnerHTML = (dom, dangerousInjection) => {
298
+ if (!is.object(dangerousInjection)) {
299
+ if (process.env.NODE_ENV !== 'production') {
300
+ console.warn(
301
+ `${DANGEROUSLY_SET_INNER_HTML} must be an object with the __html property`,
302
+ );
303
+ }
304
+ return
305
+ }
306
+
307
+ if (!('__html' in dangerousInjection)) {
308
+ if (process.env.NODE_ENV !== 'production') {
309
+ console.warn(
310
+ `${DANGEROUSLY_SET_INNER_HTML} must have the __html property`,
311
+ );
312
+ }
313
+ return
314
+ }
315
+
316
+ const html = dangerousInjection.__html;
317
+
318
+ if (html == null) {
319
+ dom.innerHTML = '';
320
+ return
321
+ }
322
+
323
+ if (typeof html !== 'string') {
324
+ if (process.env.NODE_ENV !== 'production') {
325
+ console.warn(`${DANGEROUSLY_SET_INNER_HTML}.__html must be a string`);
326
+ }
327
+ return
328
+ }
329
+
330
+ // Warning in development
331
+ if (process.env.NODE_ENV !== 'production') {
332
+ console.warn(
333
+ `⚠️ WARNING: You are using ${DANGEROUSLY_SET_INNER_HTML}. ` +
334
+ 'This can expose your application to XSS attacks if the content is not sanitized. ' +
335
+ 'Make sure you trust the source of the HTML before using it.',
336
+ );
337
+ }
338
+
339
+ try {
340
+ dom.innerHTML = html;
341
+ } catch (error) {
342
+ if (process.env.NODE_ENV !== 'production') {
343
+ console.error('Error setting innerHTML:', error);
344
+ }
345
+ }
346
+ };
347
+
246
348
  /**
247
349
  * Convert camelCase to kebab-case for CSS properties
248
350
  * @param {string} camelCase - CamelCase string
@@ -357,6 +459,40 @@ const createDom = (fiber) => {
357
459
  * @param {Object} nextProps - Next props
358
460
  */
359
461
  const updateDom = (dom, prevProps = {}, nextProps = {}) => {
462
+ // Block dangerous properties
463
+ const dangerousProps = Object.keys(nextProps).filter(isDangerousProperty);
464
+ if (dangerousProps.length > 0) {
465
+ if (process.env.NODE_ENV !== 'production') {
466
+ console.error(
467
+ `⚠️ SECURITY ERROR: Attempted to use ${DANGEROUS_PROPERTIES.join(', ')}: ${dangerousProps.join(', ')}. ` +
468
+ `These properties are blocked to prevent XSS injections. ` +
469
+ `If you need to insert HTML, use ${DANGEROUSLY_SET_INNER_HTML} with the structure: ` +
470
+ `{ ${DANGEROUSLY_SET_INNER_HTML}: { __html: 'your html here' } }`,
471
+ );
472
+ }
473
+ // Remove dangerous properties from nextProps to prevent their use
474
+ dangerousProps.forEach((prop) => {
475
+ delete nextProps[prop];
476
+ });
477
+ }
478
+
479
+ // Handle dangerouslySetInnerHTML
480
+ if (DANGEROUSLY_SET_INNER_HTML in nextProps) {
481
+ // If there are children along with dangerouslySetInnerHTML, warn
482
+ if (nextProps.children && nextProps.children.length > 0) {
483
+ if (process.env.NODE_ENV !== 'production') {
484
+ console.warn(
485
+ `⚠️ WARNING: You cannot use children together with ${DANGEROUSLY_SET_INNER_HTML}. ` +
486
+ 'Children will be ignored.',
487
+ );
488
+ }
489
+ }
490
+ applyDangerouslySetInnerHTML(dom, nextProps[DANGEROUSLY_SET_INNER_HTML]);
491
+ } else if (DANGEROUSLY_SET_INNER_HTML in prevProps) {
492
+ // If dangerouslySetInnerHTML was removed, clear innerHTML
493
+ dom.innerHTML = '';
494
+ }
495
+
360
496
  // Remove old event listeners
361
497
  Object.keys(prevProps)
362
498
  .filter(isEvent)
@@ -384,11 +520,20 @@ const updateDom = (dom, prevProps = {}, nextProps = {}) => {
384
520
  OLD_STRINGS.STYLE,
385
521
  STRINGS.CLASS_NAME,
386
522
  OLD_STRINGS.CLASS_NAME,
523
+ DANGEROUSLY_SET_INNER_HTML,
387
524
  ].includes(name)
388
525
  ) {
389
526
  return
390
527
  }
391
- dom[name] = '';
528
+ // Don't try to clean dangerous properties
529
+ if (isDangerousProperty(name)) {
530
+ return
531
+ }
532
+ try {
533
+ dom[name] = '';
534
+ } catch (error) {
535
+ // Ignore errors when cleaning read-only properties
536
+ }
392
537
  });
393
538
 
394
539
  // Set new properties
@@ -397,6 +542,11 @@ const updateDom = (dom, prevProps = {}, nextProps = {}) => {
397
542
  .filter(isNew(prevProps, nextProps))
398
543
  .forEach((name) => {
399
544
  try {
545
+ // Skip dangerouslySetInnerHTML (already handled above)
546
+ if (name === DANGEROUSLY_SET_INNER_HTML) {
547
+ return
548
+ }
549
+
400
550
  // Handle style properties
401
551
  if (name === STRINGS.STYLE || name === OLD_STRINGS.STYLE) {
402
552
  const styleValue = nextProps[name];
@@ -450,6 +600,9 @@ const updateDom = (dom, prevProps = {}, nextProps = {}) => {
450
600
  });
451
601
  };
452
602
 
603
+ /**
604
+ * The `commitRoot` function commits the changes made to the virtual DOM by updating the actual DOM.
605
+ */
453
606
  const commitRoot = () => {
454
607
  const state = getState();
455
608
  state.deletions.forEach(commitWork);
@@ -587,34 +740,6 @@ const reconcileChildren = (wipFiber, elements) => {
587
740
  });
588
741
  };
589
742
 
590
- const updateFunctionComponent = (fiber) => {
591
- const state = getState();
592
- state.wipFiber = fiber;
593
- state.hookIndex = 0;
594
- state.wipFiber.hooks = [];
595
-
596
- const children = [fiber.type(fiber.props)];
597
-
598
- if (fiber.type._contextId && fiber.props.value !== undefined) {
599
- fiber._contextId = fiber.type._contextId;
600
- fiber._contextValue = fiber.props.value;
601
- }
602
-
603
- reconcileChildren(fiber, children);
604
- };
605
-
606
- const updateHostComponent = (fiber) => {
607
- if (!fiber.dom) {
608
- fiber.dom = createDom(fiber);
609
- }
610
- const children = fiber.props?.children || [];
611
- reconcileChildren(fiber, children);
612
- };
613
-
614
- const Image = ({ src, ...props }) => {
615
- return createElement('img', { ...props, src })
616
- };
617
-
618
743
  /**
619
744
  * Priority levels for updates
620
745
  */
@@ -628,738 +753,1068 @@ const Priority = {
628
753
 
629
754
  Priority.NORMAL;
630
755
 
631
- /**
632
- * Performance profiler for Ryunix
633
- */
634
- class Profiler {
635
- constructor() {
636
- this.enabled = process.env.NODE_ENV !== 'production';
637
- this.measures = new Map();
638
- this.renderTimes = [];
639
- this.maxSamples = 100;
756
+ const validateHookCall = () => {
757
+ const state = getState();
758
+ if (!state.wipFiber) {
759
+ throw new Error(
760
+ 'Hooks can only be called inside the body of a function component.',
761
+ )
640
762
  }
641
-
642
- startMeasure(name) {
643
- if (!this.enabled) return
644
- this.measures.set(name, performance.now());
763
+ if (!Array.isArray(state.wipFiber.hooks)) {
764
+ state.wipFiber.hooks = [];
645
765
  }
766
+ };
646
767
 
647
- endMeasure(name) {
648
- if (!this.enabled) return
649
- const start = this.measures.get(name);
650
- if (!start) return
768
+ const haveDepsChanged = (oldDeps, newDeps) => {
769
+ if (!oldDeps || !newDeps) return true
770
+ if (oldDeps.length !== newDeps.length) return true
771
+ return oldDeps.some((dep, i) => !Object.is(dep, newDeps[i]))
772
+ };
651
773
 
652
- const duration = performance.now() - start;
653
- this.measures.delete(name);
774
+ /**
775
+ * The `useStore` function in JavaScript is a custom hook that uses a reducer to manage state updates
776
+ * based on actions provided.
777
+ * @param initialState - The `initialState` parameter in the `useStore` function is the initial state
778
+ * of the store that will be used with the `useReducer` hook. It represents the starting state of the
779
+ * store before any actions are dispatched to update it.
780
+ * @returns The `useStore` function is returning the result of calling the `useReducer` hook with the
781
+ * `reducer` function and the `initialState` as arguments.
782
+ */
783
+ const useStore = (initialState) => {
784
+ const reducer = (state, action) =>
785
+ is.function(action) ? action(state) : action;
786
+ return useReducer(reducer, initialState)
787
+ };
654
788
 
655
- return duration
656
- }
789
+ /**
790
+ * The `useReducer` function in JavaScript is used to manage state and actions.
791
+ *
792
+ * @param reducer - The `reducer` parameter in the `useReducer` function is a function that specifies
793
+ * how the state should be updated in response to an action. It takes the current state and an action
794
+ * as arguments and returns the new state based on the action.
795
+ * @param initialState - The `initialState` parameter in the `useReducer` function represents the
796
+ * initial state of the reducer. It is the state that will be used when the reducer is first called or
797
+ * when the state needs to be reset. This initial state can be a simple value, an object, an array, or
798
+ * @param init - The `init` parameter in the `useReducer` function is an optional function that can be
799
+ * used to initialize the state. If provided, it will be called with the `initialState` as its argument
800
+ * and the return value will be used as the initial state for the reducer. If `init`
801
+ * @returns An array containing the current state and the dispatch function is being returned.
802
+ */
803
+ const useReducer = (reducer, initialState, init) => {
804
+ validateHookCall();
657
805
 
658
- recordRender(componentName, duration) {
659
- if (!this.enabled) return
806
+ const state = getState();
807
+ const { wipFiber, hookIndex } = state;
808
+ const oldHook = wipFiber.alternate?.hooks?.[hookIndex];
660
809
 
661
- this.renderTimes.push({
662
- component: componentName,
663
- duration,
664
- timestamp: Date.now(),
810
+ const hook = {
811
+ hookID: hookIndex,
812
+ type: RYUNIX_TYPES.RYUNIX_STORE,
813
+ state: oldHook ? oldHook.state : init ? init(initialState) : initialState,
814
+ queue: [],
815
+ };
816
+
817
+ if (oldHook?.queue) {
818
+ oldHook.queue.forEach((action) => {
819
+ try {
820
+ hook.state = reducer(hook.state, action);
821
+ } catch (error) {
822
+ if (process.env.NODE_ENV !== 'production') {
823
+ console.error('Error in reducer:', error);
824
+ }
825
+ }
665
826
  });
827
+ }
666
828
 
667
- if (this.renderTimes.length > this.maxSamples) {
668
- this.renderTimes.shift();
829
+ const dispatch = (action) => {
830
+ if (action === undefined) {
831
+ if (process.env.NODE_ENV !== 'production') {
832
+ console.warn('dispatch called with undefined action');
833
+ }
834
+ return
669
835
  }
670
- }
671
836
 
672
- getStats() {
673
- if (!this.enabled) return null
837
+ hook.queue.push(action);
674
838
 
675
- const total = this.renderTimes.reduce((sum, r) => sum + r.duration, 0);
676
- const avg = total / this.renderTimes.length;
677
- const max = Math.max(...this.renderTimes.map((r) => r.duration));
678
- const min = Math.min(...this.renderTimes.map((r) => r.duration));
839
+ const currentState = getState();
840
+ currentState.wipRoot = {
841
+ dom: currentState.currentRoot.dom,
842
+ props: currentState.currentRoot.props,
843
+ alternate: currentState.currentRoot,
844
+ };
845
+ currentState.deletions = [];
846
+ currentState.hookIndex = 0;
847
+ scheduleWork(currentState.wipRoot);
848
+ };
679
849
 
680
- return { total, avg, max, min, count: this.renderTimes.length }
681
- }
850
+ wipFiber.hooks[hookIndex] = hook;
851
+ state.hookIndex++;
852
+ return [hook.state, dispatch]
853
+ };
682
854
 
683
- getSlowestComponents(limit = 10) {
684
- if (!this.enabled) return []
855
+ /**
856
+ * The `useEffect` function in JavaScript is used to manage side effects in functional components by
857
+ * comparing dependencies and executing a callback function when dependencies change.
858
+ * @param callback - The `callback` parameter in the `useEffect` function is a function that will be
859
+ * executed as the effect. This function can perform side effects like data fetching, subscriptions, or
860
+ * DOM manipulations.
861
+ * @param deps - The `deps` parameter in the `useEffect` function stands for dependencies. It is an
862
+ * optional array that contains values that the effect depends on. The effect will only re-run if any
863
+ * of the values in the `deps` array have changed since the last render. If the `deps` array
864
+ */
865
+ const useEffect = (callback, deps) => {
866
+ validateHookCall();
685
867
 
686
- const byComponent = new Map();
868
+ if (!is.function(callback)) {
869
+ throw new Error('useEffect callback must be a function')
870
+ }
871
+ if (deps !== undefined && !Array.isArray(deps)) {
872
+ throw new Error('useEffect dependencies must be an array or undefined')
873
+ }
687
874
 
688
- this.renderTimes.forEach(({ component, duration }) => {
689
- if (!byComponent.has(component)) {
690
- byComponent.set(component, { total: 0, count: 0, max: 0 });
691
- }
692
- const stats = byComponent.get(component);
693
- stats.total += duration;
694
- stats.count++;
695
- stats.max = Math.max(stats.max, duration);
696
- });
875
+ const state = getState();
876
+ const { wipFiber, hookIndex } = state;
877
+ const oldHook = wipFiber.alternate?.hooks?.[hookIndex];
878
+ const hasChanged = haveDepsChanged(oldHook?.deps, deps);
697
879
 
698
- return Array.from(byComponent.entries())
699
- .map(([name, stats]) => ({
700
- name,
701
- avg: stats.total / stats.count,
702
- max: stats.max,
703
- count: stats.count,
704
- }))
705
- .sort((a, b) => b.avg - a.avg)
706
- .slice(0, limit)
707
- }
880
+ const hook = {
881
+ hookID: hookIndex,
882
+ type: RYUNIX_TYPES.RYUNIX_EFFECT,
883
+ deps,
884
+ effect: hasChanged ? callback : null,
885
+ cancel: oldHook?.cancel,
886
+ };
708
887
 
709
- logStats() {
710
- if (!this.enabled) return
888
+ wipFiber.hooks[hookIndex] = hook;
889
+ state.hookIndex++;
890
+ };
711
891
 
712
- const stats = this.getStats();
713
- if (!stats) return
892
+ /**
893
+ * The useRef function in JavaScript creates a reference object with an initial value for use in functional components.
894
+ * @param initialValue - The `initialValue` parameter in the `useRef` function represents the initial
895
+ * value that will be assigned to the `current` property of the reference object. This initial value
896
+ * will be used if there is no previous value stored in the hook.
897
+ * @returns The `useRef` function is returning the `current` property of the `hook.value` object, which
898
+ * contains the initial value passed to the `useRef` function.
899
+ */
900
+ const useRef = (initialValue) => {
901
+ validateHookCall();
714
902
 
715
- console.group('🔍 Ryunix Performance Stats');
716
- console.log(`Total renders: ${stats.count}`);
717
- console.log(`Avg render time: ${stats.avg.toFixed(2)}ms`);
718
- console.log(
719
- `Min: ${stats.min.toFixed(2)}ms | Max: ${stats.max.toFixed(2)}ms`,
720
- );
903
+ const state = getState();
904
+ const { wipFiber, hookIndex } = state;
905
+ const oldHook = wipFiber.alternate?.hooks?.[hookIndex];
721
906
 
722
- const slowest = this.getSlowestComponents(5);
723
- if (slowest.length > 0) {
724
- console.log('\n⚠️ Slowest components:');
725
- slowest.forEach((comp, i) => {
726
- console.log(
727
- `${i + 1}. ${comp.name}: ${comp.avg.toFixed(2)}ms avg (${comp.count} renders)`,
728
- );
729
- });
730
- }
731
- console.groupEnd();
732
- }
907
+ const hook = {
908
+ hookID: hookIndex,
909
+ type: RYUNIX_TYPES.RYUNIX_REF,
910
+ value: oldHook ? oldHook.value : { current: initialValue },
911
+ };
733
912
 
734
- clear() {
735
- this.renderTimes = [];
736
- this.measures.clear();
913
+ wipFiber.hooks[hookIndex] = hook;
914
+ state.hookIndex++;
915
+ return hook.value
916
+ };
917
+
918
+ /**
919
+ * The useMemo function in JavaScript is used to memoize the result of a computation based on
920
+ * dependencies.
921
+ * @param compute - The `compute` parameter in the `useMemo` function is a callback function that
922
+ * calculates the value that `useMemo` will memoize and return. This function will be called to compute
923
+ * the memoized value when necessary.
924
+ * @param deps - The `deps` parameter in the `useMemo` function refers to an array of dependencies.
925
+ * These dependencies are used to determine whether the memoized value needs to be recalculated or if
926
+ * the previously calculated value can be reused. The `useMemo` hook will recompute the memoized value
927
+ * only if
928
+ * @returns The `useMemo` function is returning the `value` calculated by the `compute` function.
929
+ */
930
+ const useMemo = (compute, deps) => {
931
+ validateHookCall();
932
+
933
+ if (!is.function(compute)) {
934
+ throw new Error('useMemo callback must be a function')
935
+ }
936
+ if (!Array.isArray(deps)) {
937
+ throw new Error('useMemo requires a dependencies array')
737
938
  }
738
939
 
739
- enable() {
740
- this.enabled = true;
940
+ const state = getState();
941
+ const { wipFiber, hookIndex } = state;
942
+ const oldHook = wipFiber.alternate?.hooks?.[hookIndex];
943
+
944
+ let value;
945
+ if (oldHook && !haveDepsChanged(oldHook.deps, deps)) {
946
+ value = oldHook.value;
947
+ } else {
948
+ try {
949
+ value = compute();
950
+ } catch (error) {
951
+ if (process.env.NODE_ENV !== 'production') {
952
+ console.error('Error in useMemo computation:', error);
953
+ }
954
+ value = undefined;
955
+ }
741
956
  }
742
957
 
743
- disable() {
744
- this.enabled = false;
958
+ const hook = {
959
+ hookID: hookIndex,
960
+ type: RYUNIX_TYPES.RYUNIX_MEMO,
961
+ value,
962
+ deps,
963
+ };
964
+
965
+ wipFiber.hooks[hookIndex] = hook;
966
+ state.hookIndex++;
967
+ return value
968
+ };
969
+
970
+ /**
971
+ * The useCallback function in JavaScript ensures that a callback function is memoized based on its
972
+ * dependencies.
973
+ * @param callback - A function that you want to memoize and return for later use.
974
+ * @param deps - The `deps` parameter in the `useCallback` function refers to an array of dependencies.
975
+ * These dependencies are used to determine when the callback function should be re-evaluated and
976
+ * memoized. If any of the dependencies change, the callback function will be re-executed and the
977
+ * memoized value will
978
+ * @returns The useCallback function is returning the memoized version of the callback function passed
979
+ * as the first argument, based on the dependencies array provided as the second argument.
980
+ */
981
+ const useCallback = (callback, deps) => {
982
+ if (!is.function(callback)) {
983
+ throw new Error('useCallback requires a function as first argument')
745
984
  }
746
- }
985
+ return useMemo(() => callback, deps)
986
+ };
747
987
 
748
- // Global profiler instance
749
- const profiler = new Profiler();
988
+ /**
989
+ * The createContext function creates a context provider and useContext hook in JavaScript.
990
+ * @param [contextId] - The `contextId` parameter in the `createContext` function is used to specify
991
+ * the unique identifier for the context being created. It defaults to `RYUNIX_TYPES.RYUNIX_CONTEXT` if
992
+ * not provided.
993
+ * @param [defaultValue] - The `defaultValue` parameter in the `createContext` function is used to
994
+ * specify the default value that will be returned by the `useContext` hook if no provider is found in
995
+ * the component tree. It is an optional parameter, and if not provided, an empty object `{}` will be
996
+ * used as
997
+ * @returns The `createContext` function returns an object with two properties: `Provider` and
998
+ * `useContext`. The `Provider` property is a component that accepts `children` and `value` props, and
999
+ * sets the `_contextId` and `_contextValue` properties on the element. The `useContext` property is a
1000
+ * hook function that retrieves the context value based on the context ID provided, or
1001
+ */
1002
+ const createContext = (
1003
+ contextId = RYUNIX_TYPES.RYUNIX_CONTEXT,
1004
+ defaultValue = {},
1005
+ ) => {
1006
+ const Provider = ({ children, value }) => {
1007
+ const element = Fragment({ children });
1008
+ element._contextId = contextId;
1009
+ element._contextValue = value;
1010
+ return element
1011
+ };
1012
+
1013
+ Provider._contextId = contextId;
1014
+
1015
+ const useContext = (ctxID = contextId) => {
1016
+ validateHookCall();
1017
+
1018
+ const state = getState();
1019
+ let fiber = state.wipFiber;
1020
+
1021
+ while (fiber) {
1022
+ if (fiber._contextId === ctxID && fiber._contextValue !== undefined) {
1023
+ return fiber._contextValue
1024
+ }
1025
+ if (
1026
+ fiber.type?._contextId === ctxID &&
1027
+ fiber.props?.value !== undefined
1028
+ ) {
1029
+ return fiber.props.value
1030
+ }
1031
+ fiber = fiber.parent;
1032
+ }
1033
+ return defaultValue
1034
+ };
1035
+
1036
+ return { Provider, useContext }
1037
+ };
750
1038
 
751
1039
  /**
752
- * Hook to profile component render
1040
+ * The `useQuery` function extracts query parameters from the URL in a browser environment.
1041
+ * @returns An object containing the query parameters from the current URL is being returned.
753
1042
  */
754
- const useProfiler = (componentName) => {
755
- const startTime = performance.now();
1043
+ const useQuery = () => {
1044
+ if (typeof window === 'undefined') return {}
756
1045
 
757
- return () => {
758
- const duration = performance.now() - startTime;
759
- profiler.recordRender(componentName, duration);
1046
+ const searchParams = new URLSearchParams(window.location.search);
1047
+ const query = {};
1048
+ for (const [key, value] of searchParams.entries()) {
1049
+ query[key] = value;
760
1050
  }
1051
+ return query
761
1052
  };
762
1053
 
763
1054
  /**
764
- * HOC to profile component
1055
+ * The function `useHash` in JavaScript is used to manage and update the hash portion of the URL in a
1056
+ * web application.
1057
+ * @returns The `useHash` function returns the current hash value from the window's location. If the
1058
+ * window is undefined (e.g., in a server-side environment), it returns an empty string. The function
1059
+ * also sets up an event listener to update the hash value when the hash in the URL changes and removes
1060
+ * the event listener when the component unmounts.
765
1061
  */
766
- const withProfiler = (Component, name) => {
767
- return (props) => {
768
- profiler.startMeasure(name);
769
- const result = Component(props);
770
- const duration = profiler.endMeasure(name);
771
- if (duration) profiler.recordRender(name, duration);
772
- return result
773
- }
1062
+ const useHash = () => {
1063
+ if (typeof window === 'undefined') return ''
1064
+
1065
+ const [hash, setHash] = useStore(window.location.hash);
1066
+ useEffect(() => {
1067
+ const onHashChange = () => setHash(window.location.hash);
1068
+ window.addEventListener('hashchange', onHashChange);
1069
+ return () => window.removeEventListener('hashchange', onHashChange)
1070
+ }, []);
1071
+ return hash
774
1072
  };
775
1073
 
776
- const workLoop = (deadline) => {
777
- const state = getState();
778
- let shouldYield = false;
1074
+ /**
1075
+ * The `useMetadata` function in JavaScript is used to dynamically update metadata tags in the document
1076
+ * head based on provided tags and options.
1077
+ * @param [tags] - The `tags` parameter in the `useMetadata` function is an object that contains
1078
+ * metadata information for the webpage. It can include properties like `pageTitle`, `canonical`, and
1079
+ * other custom metadata tags like `og:title`, `og:description`, `twitter:title`,
1080
+ * `twitter:description`, etc. These tags
1081
+ * @param [options] - The `options` parameter in the `useMetadata` function is an object that can
1082
+ * contain the following properties:
1083
+ * - `title`: An object that can have the following properties:
1084
+ * - `template`: A string that defines the template for the page title. It can include a placeholder
1085
+ * `%s` that will be replaced with the actual page title.
1086
+ * - `prefix`: A string that will be used as the default title if no specific page title is provided.
1087
+ * @returns The `useMetadata` function does not return anything. It is a custom hook that updates the
1088
+ * document's metadata (such as title and meta tags) based on the provided `tags` and `options` whenever
1089
+ * they change.
1090
+ * This hook can't be reached by google crawler.
1091
+ */
779
1092
 
780
- while (state.nextUnitOfWork && !shouldYield) {
781
- state.nextUnitOfWork = performUnitOfWork(state.nextUnitOfWork);
782
- shouldYield = deadline.timeRemaining() < 1;
783
- }
1093
+ const useMetadata = (tags = {}, options = {}) => {
1094
+ useEffect(() => {
1095
+ if (typeof document === 'undefined') return
784
1096
 
785
- if (!state.nextUnitOfWork && state.wipRoot) {
786
- commitRoot();
787
- }
1097
+ let finalTitle = 'Ryunix App';
1098
+ const template = options.title?.template;
1099
+ const defaultTitle = options.title?.prefix || 'Ryunix App';
1100
+ const pageTitle = tags.pageTitle || tags.title;
788
1101
 
789
- requestIdleCallback(workLoop);
1102
+ if (is.string(pageTitle) && pageTitle.trim()) {
1103
+ finalTitle = template?.includes('%s')
1104
+ ? template.replace('%s', pageTitle)
1105
+ : pageTitle;
1106
+ } else {
1107
+ finalTitle = defaultTitle;
1108
+ }
1109
+
1110
+ document.title = finalTitle;
1111
+
1112
+ if (tags.canonical) {
1113
+ let link = document.querySelector('link[rel="canonical"]');
1114
+ if (!link) {
1115
+ link = document.createElement('link');
1116
+ link.setAttribute('rel', 'canonical');
1117
+ document.head.appendChild(link);
1118
+ }
1119
+ link.setAttribute('href', tags.canonical);
1120
+ }
1121
+
1122
+ Object.entries(tags).forEach(([key, value]) => {
1123
+ if (['title', 'pageTitle', 'canonical'].includes(key)) return
1124
+
1125
+ const isProperty = key.startsWith('og:') || key.startsWith('twitter:');
1126
+ const selector = `meta[${isProperty ? 'property' : 'name'}='${key}']`;
1127
+ let meta = document.head.querySelector(selector);
1128
+
1129
+ if (!meta) {
1130
+ meta = document.createElement('meta');
1131
+ meta.setAttribute(isProperty ? 'property' : 'name', key);
1132
+ document.head.appendChild(meta);
1133
+ }
1134
+ meta.setAttribute('content', value);
1135
+ });
1136
+ }, [JSON.stringify(tags), JSON.stringify(options)]);
790
1137
  };
791
1138
 
792
- requestIdleCallback(workLoop);
1139
+ // Router Context
1140
+ const RouterContext = createContext('ryunix.navigation', {
1141
+ location: '/',
1142
+ params: {},
1143
+ query: {},
1144
+ navigate: (path) => {},
1145
+ route: null,
1146
+ });
793
1147
 
794
- const performUnitOfWork = (fiber) => {
795
- const componentName = fiber.type?.name || fiber.type?.displayName || 'Unknown';
1148
+ const findRoute = (routes, path) => {
1149
+ const pathname = path.split('?')[0].split('#')[0];
1150
+ const notFoundRoute = routes.find((route) => route.NotFound);
1151
+ const notFound = notFoundRoute
1152
+ ? { route: { component: notFoundRoute.NotFound }, params: {} }
1153
+ : { route: { component: null }, params: {} };
796
1154
 
797
- profiler.startMeasure(componentName);
1155
+ for (const route of routes) {
1156
+ if (route.subRoutes) {
1157
+ const childRoute = findRoute(route.subRoutes, path);
1158
+ if (childRoute) return childRoute
1159
+ }
1160
+ if (route.path === '*') return notFound
1161
+ if (!route.path || typeof route.path !== 'string') continue
798
1162
 
799
- const isFunctionComponent = fiber.type instanceof Function;
800
- if (isFunctionComponent) {
801
- updateFunctionComponent(fiber);
802
- } else {
803
- updateHostComponent(fiber);
1163
+ const keys = [];
1164
+ const pattern = new RegExp(
1165
+ `^${route.path.replace(/:\w+/g, (match) => {
1166
+ keys.push(match.substring(1));
1167
+ return '([^/]+)'
1168
+ })}$`,
1169
+ );
1170
+
1171
+ const match = pathname.match(pattern);
1172
+ if (match) {
1173
+ const params = keys.reduce((acc, key, index) => {
1174
+ acc[key] = match[index + 1];
1175
+ return acc
1176
+ }, {});
1177
+ return { route, params }
1178
+ }
804
1179
  }
1180
+ return notFound
1181
+ };
805
1182
 
806
- const duration = profiler.endMeasure(componentName);
807
- if (duration) profiler.recordRender(componentName, duration);
1183
+ /**
1184
+ * The `RouterProvider` component manages routing in a Ryunix application by updating the location based
1185
+ * on window events and providing context for the current route.
1186
+ * @returns The `RouterProvider` component is returning a `RouterContext.Provider` component with a
1187
+ * `value` prop set to `contextValue`, and wrapping the `children` within a `Fragment`.
1188
+ */
1189
+ const RouterProvider = ({ routes, children }) => {
1190
+ const [location, setLocation] = useStore(window.location.pathname);
808
1191
 
809
- if (fiber.child) {
810
- return fiber.child
811
- }
812
- let nextFiber = fiber;
813
- while (nextFiber) {
814
- if (nextFiber.sibling) {
815
- return nextFiber.sibling
1192
+ useEffect(() => {
1193
+ const update = () => setLocation(window.location.pathname);
1194
+ window.addEventListener('popstate', update);
1195
+ window.addEventListener('hashchange', update);
1196
+ return () => {
1197
+ window.removeEventListener('popstate', update);
1198
+ window.removeEventListener('hashchange', update);
816
1199
  }
817
- nextFiber = nextFiber.parent;
818
- }
1200
+ }, [location]);
1201
+
1202
+ const navigate = (path) => {
1203
+ window.history.pushState({}, '', path);
1204
+ setLocation(path);
1205
+ };
1206
+
1207
+ const currentRouteData = findRoute(routes, location) || {};
1208
+ const query = useQuery();
1209
+
1210
+ const contextValue = {
1211
+ location,
1212
+ params: currentRouteData.params || {},
1213
+ query,
1214
+ navigate,
1215
+ route: currentRouteData.route,
1216
+ };
1217
+
1218
+ return createElement(
1219
+ RouterContext.Provider,
1220
+ { value: contextValue },
1221
+ Fragment({ children }),
1222
+ )
819
1223
  };
820
1224
 
821
- const scheduleWork = (root, priority = Priority.NORMAL) => {
822
- const state = getState();
823
- state.nextUnitOfWork = root;
824
- state.wipRoot = root;
825
- state.deletions = [];
826
- state.hookIndex = 0;
827
- state.effects = [];
1225
+ /**
1226
+ * The function `useRouter` returns the context of the Router for navigation in a Ryunix application.
1227
+ * @returns The `useRouter` function is returning the result of calling
1228
+ * `RouterContext.useContext('ryunix.navigation')`. This function is likely attempting to retrieve the
1229
+ * navigation context from the RouterContext.
1230
+ */
1231
+ const useRouter = () => {
1232
+ return RouterContext.useContext('ryunix.navigation')
1233
+ };
828
1234
 
829
- // Higher priority = faster scheduling
830
- if (priority <= Priority.USER_BLOCKING) {
831
- requestIdleCallback(workLoop);
832
- } else {
833
- setTimeout(() => requestIdleCallback(workLoop), 0);
834
- }
1235
+ /**
1236
+ * The `Children` function in JavaScript uses router hooks to handle scrolling to a specific element
1237
+ * based on the hash in the URL.
1238
+ * @returns The `Children` component is returning the result of calling `createElement` with
1239
+ * `route.component` as the first argument and an object with `key`, `params`, `query`, and `hash`
1240
+ * properties as the second argument. The `key` property is set to `location`, and the `params`,
1241
+ * `query`, and `hash` properties are passed as values from the component's props.
1242
+ */
1243
+ const Children = () => {
1244
+ const { route, params, query, location } = useRouter();
1245
+ if (!route || !route.component) return null
1246
+ const hash = useHash();
1247
+
1248
+ useEffect(() => {
1249
+ if (hash) {
1250
+ const id = hash.slice(1);
1251
+ const el = document.getElementById(id);
1252
+ if (el) el.scrollIntoView({ block: 'start', behavior: 'smooth' });
1253
+ }
1254
+ }, [hash]);
1255
+
1256
+ return createElement(route.component, {
1257
+ key: location,
1258
+ params,
1259
+ query,
1260
+ hash,
1261
+ })
835
1262
  };
836
1263
 
837
- const render = (element, container) => {
838
- const state = getState();
839
- state.wipRoot = {
840
- dom: container,
841
- props: {
842
- children: [element],
843
- },
844
- alternate: state.currentRoot,
845
- };
1264
+ /**
1265
+ * The NavLink function in JavaScript is a component that generates a link element with customizable
1266
+ * classes and active state based on the current location.
1267
+ * @returns The `NavLink` component is returning a JSX element representing an anchor (`<a>`) tag with
1268
+ * the following attributes and properties:
1269
+ */
1270
+ const NavLink = ({ to, exact = false, ...props }) => {
1271
+ const { location, navigate } = useRouter();
1272
+ const isActive = exact ? location === to : location.startsWith(to);
846
1273
 
847
- state.nextUnitOfWork = state.wipRoot;
848
- state.deletions = [];
849
- scheduleWork(state.wipRoot);
850
- return state.wipRoot
851
- };
1274
+ const resolveClass = (cls) =>
1275
+ typeof cls === 'function' ? cls({ isActive }) : cls || '';
852
1276
 
853
- const init = (MainElement, root = '__ryunix') => {
854
- const state = getState();
855
- state.containerRoot = document.getElementById(root);
856
- const renderProcess = render(MainElement, state.containerRoot);
857
- return renderProcess
858
- };
1277
+ const handleClick = (e) => {
1278
+ e.preventDefault();
1279
+ navigate(to);
1280
+ };
859
1281
 
860
- const safeRender = (component, props, onError) => {
861
- try {
862
- return component(props)
863
- } catch (error) {
864
- if (process.env.NODE_ENV !== 'production') {
865
- console.error('Component error:', error);
866
- }
867
- if (onError) onError(error);
868
- return null
869
- }
870
- };
1282
+ const classAttrName = props['ryunix-class'] ? 'ryunix-class' : 'className';
1283
+ const classAttrValue = resolveClass(
1284
+ props['ryunix-class'] || props['className'],
1285
+ );
871
1286
 
872
- const validateHookCall = () => {
873
- const state = getState();
874
- if (!state.wipFiber) {
875
- throw new Error(
876
- 'Hooks can only be called inside the body of a function component.',
877
- )
878
- }
879
- if (!Array.isArray(state.wipFiber.hooks)) {
880
- state.wipFiber.hooks = [];
881
- }
882
- };
1287
+ const {
1288
+ ['ryunix-class']: _omitRyunix,
1289
+ className: _omitClassName,
1290
+ ...cleanedProps
1291
+ } = props;
883
1292
 
884
- const haveDepsChanged = (oldDeps, newDeps) => {
885
- if (!oldDeps || !newDeps) return true
886
- if (oldDeps.length !== newDeps.length) return true
887
- return oldDeps.some((dep, i) => !Object.is(dep, newDeps[i]))
1293
+ return createElement(
1294
+ 'a',
1295
+ {
1296
+ href: to,
1297
+ onClick: handleClick,
1298
+ [classAttrName]: classAttrValue,
1299
+ ...cleanedProps,
1300
+ },
1301
+ props.children,
1302
+ )
888
1303
  };
889
1304
 
890
- const useStore = (initialState) => {
1305
+ /**
1306
+ * useStore with priority support
1307
+ */
1308
+ const useStorePriority = (initialState) => {
891
1309
  const reducer = (state, action) =>
892
- is.function(action) ? action(state) : action;
893
- return useReducer(reducer, initialState)
894
- };
1310
+ typeof action === 'function' ? action.value(state) : action.value;
895
1311
 
896
- const useReducer = (reducer, initialState, init) => {
897
- validateHookCall();
1312
+ const [state, baseDispatch] = useReducer(reducer, initialState);
898
1313
 
899
- const state = getState();
900
- const { wipFiber, hookIndex } = state;
901
- const oldHook = wipFiber.alternate?.hooks?.[hookIndex];
1314
+ const dispatch = (action, priority = Priority.NORMAL) => {
1315
+ const wrappedAction = {
1316
+ value: action,
1317
+ priority,
1318
+ };
902
1319
 
903
- const hook = {
904
- hookID: hookIndex,
905
- type: RYUNIX_TYPES.RYUNIX_STORE,
906
- state: oldHook ? oldHook.state : init ? init(initialState) : initialState,
907
- queue: [], // Siempre nueva cola vacía
1320
+ baseDispatch(wrappedAction);
908
1321
  };
909
1322
 
910
- // Procesar acciones del render anterior
911
- if (oldHook?.queue) {
912
- oldHook.queue.forEach((action) => {
913
- try {
914
- hook.state = reducer(hook.state, action);
915
- } catch (error) {
916
- if (process.env.NODE_ENV !== 'production') {
917
- console.error('Error in reducer:', error);
918
- }
919
- }
920
- });
921
- }
1323
+ return [state, dispatch]
1324
+ };
922
1325
 
923
- const dispatch = (action) => {
924
- if (action === undefined) {
925
- if (process.env.NODE_ENV !== 'production') {
926
- console.warn('dispatch called with undefined action');
927
- }
928
- return
929
- }
1326
+ /**
1327
+ * useTransition - Mark updates as non-urgent
1328
+ */
1329
+ const useTransition = () => {
1330
+ const [isPending, setIsPending] = useStorePriority(false);
930
1331
 
931
- hook.queue.push(action);
1332
+ const startTransition = (callback) => {
1333
+ setIsPending(true, Priority.IMMEDIATE);
932
1334
 
933
- const currentState = getState();
934
- currentState.wipRoot = {
935
- dom: currentState.currentRoot.dom,
936
- props: currentState.currentRoot.props,
937
- alternate: currentState.currentRoot,
938
- };
939
- currentState.deletions = [];
940
- currentState.hookIndex = 0;
941
- scheduleWork(currentState.wipRoot);
1335
+ setTimeout(() => {
1336
+ callback();
1337
+ setIsPending(false, Priority.IMMEDIATE);
1338
+ }, 0);
942
1339
  };
943
1340
 
944
- wipFiber.hooks[hookIndex] = hook;
945
- state.hookIndex++;
946
- return [hook.state, dispatch]
1341
+ return [isPending, startTransition]
947
1342
  };
948
1343
 
949
- const useEffect = (callback, deps) => {
950
- validateHookCall();
951
-
952
- if (!is.function(callback)) {
953
- throw new Error('useEffect callback must be a function')
954
- }
955
- if (deps !== undefined && !Array.isArray(deps)) {
956
- throw new Error('useEffect dependencies must be an array or undefined')
957
- }
1344
+ /**
1345
+ * useDeferredValue - Defer value updates
1346
+ */
1347
+ const useDeferredValue = (value) => {
1348
+ const [deferredValue, setDeferredValue] = useStorePriority(value);
958
1349
 
959
- const state = getState();
960
- const { wipFiber, hookIndex } = state;
961
- const oldHook = wipFiber.alternate?.hooks?.[hookIndex];
962
- const hasChanged = haveDepsChanged(oldHook?.deps, deps);
1350
+ useEffect(() => {
1351
+ const timeout = setTimeout(() => {
1352
+ setDeferredValue(value, Priority.LOW);
1353
+ }, 100);
963
1354
 
964
- const hook = {
965
- hookID: hookIndex,
966
- type: RYUNIX_TYPES.RYUNIX_EFFECT,
967
- deps,
968
- effect: hasChanged ? callback : null,
969
- cancel: oldHook?.cancel,
970
- };
1355
+ return () => clearTimeout(timeout)
1356
+ }, [value]);
971
1357
 
972
- wipFiber.hooks[hookIndex] = hook;
973
- state.hookIndex++;
1358
+ return deferredValue
974
1359
  };
975
1360
 
976
- const useRef = (initialValue) => {
977
- validateHookCall();
978
-
979
- const state = getState();
980
- const { wipFiber, hookIndex } = state;
981
- const oldHook = wipFiber.alternate?.hooks?.[hookIndex];
1361
+ /**
1362
+ * The `usePersitentStore` function manages state using local storage in JavaScript, allowing for easy
1363
+ * storage and retrieval of data.
1364
+ * @param key - The `key` parameter in the `usePersitentStore` function is a string that represents the key
1365
+ * under which the data will be stored in the browser's local storage. It is used to retrieve and store
1366
+ * data associated with this specific key.
1367
+ * @param [initialState] - The `initialState` parameter in the `usePersitentStore` function is the initial
1368
+ * value that will be used if there is no data stored in the local storage under the specified `key`.
1369
+ * It serves as the default value for the state if no data is retrieved from the local storage.
1370
+ * @returns The `usePersitentStore` function returns an array containing two elements:
1371
+ * 1. The current state value retrieved from local storage or the initial state if not found.
1372
+ * 2. The `setValue` function that updates the state value and stores it in the local storage as a JSON
1373
+ * string.
1374
+ */
1375
+ const usePersitentStore = (key, initialState = '') => {
1376
+ const [state, dispatch] = useStore(() => {
1377
+ try {
1378
+ const item = window.localStorage.getItem(key);
1379
+ return item ? JSON.parse(item) : initialState
1380
+ } catch (error) {
1381
+ return initialState
1382
+ }
1383
+ });
982
1384
 
983
- const hook = {
984
- hookID: hookIndex,
985
- type: RYUNIX_TYPES.RYUNIX_REF,
986
- value: oldHook ? oldHook.value : { current: initialValue },
1385
+ /**
1386
+ * The function `setValue` dispatches a value and stores it in the local storage as a JSON string,
1387
+ * handling any errors with a console log.
1388
+ * @param value - The `value` parameter in the `setValue` function is the data that you want to set.
1389
+ * It is dispatched to update the state and then stored in the browser's local storage after being
1390
+ * converted to a JSON string.
1391
+ */
1392
+ const setValue = (value) => {
1393
+ try {
1394
+ dispatch(value);
1395
+ window.localStorage.setItem(key, JSON.stringify(value));
1396
+ } catch (error) {
1397
+ console.error(error);
1398
+ }
987
1399
  };
988
1400
 
989
- wipFiber.hooks[hookIndex] = hook;
990
- state.hookIndex++;
991
- return hook.value
1401
+ return [state, setValue]
992
1402
  };
993
1403
 
994
- const useMemo = (compute, deps) => {
995
- validateHookCall();
1404
+ /**
1405
+ * The `useSwitch` function returns a state value and a toggle function to switch the state between
1406
+ * true and false.
1407
+ * @param [initialState=false] - The `initialState` parameter in the `useSwitch` function is used to
1408
+ * set the initial value of the state. If no value is provided when calling `useSwitch`, the default
1409
+ * initial state will be `false`.
1410
+ * @returns An array containing the current state value and a function `toggle` that toggles the state
1411
+ * value.
1412
+ */
1413
+ const useSwitch = (initialState = false) => {
1414
+ const [state, dispatch] = useStore(initialState);
1415
+
1416
+ /**
1417
+ * The function `toggle` toggles the state by dispatching the opposite value of the current state.
1418
+ */
1419
+ const toggle = () => {
1420
+ dispatch(!state);
1421
+ };
996
1422
 
997
- if (!is.function(compute)) {
998
- throw new Error('useMemo callback must be a function')
999
- }
1000
- if (!Array.isArray(deps)) {
1001
- throw new Error('useMemo requires a dependencies array')
1002
- }
1423
+ return [state, toggle]
1424
+ };
1003
1425
 
1004
- const state = getState();
1005
- const { wipFiber, hookIndex } = state;
1006
- const oldHook = wipFiber.alternate?.hooks?.[hookIndex];
1426
+ var hooks = /*#__PURE__*/Object.freeze({
1427
+ __proto__: null,
1428
+ Children: Children,
1429
+ NavLink: NavLink,
1430
+ RouterProvider: RouterProvider,
1431
+ createContext: createContext,
1432
+ useCallback: useCallback,
1433
+ useDeferredValue: useDeferredValue,
1434
+ useEffect: useEffect,
1435
+ useHash: useHash,
1436
+ useMemo: useMemo,
1437
+ useMetadata: useMetadata,
1438
+ usePersitentStore: usePersitentStore,
1439
+ useQuery: useQuery,
1440
+ useReducer: useReducer,
1441
+ useRef: useRef,
1442
+ useRouter: useRouter,
1443
+ useStore: useStore,
1444
+ useStorePriority: useStorePriority,
1445
+ useSwitch: useSwitch,
1446
+ useTransition: useTransition
1447
+ });
1007
1448
 
1008
- let value;
1009
- if (oldHook && !haveDepsChanged(oldHook.deps, deps)) {
1010
- value = oldHook.value;
1011
- } else {
1012
- try {
1013
- value = compute();
1014
- } catch (error) {
1015
- if (process.env.NODE_ENV !== 'production') {
1016
- console.error('Error in useMemo computation:', error);
1017
- }
1018
- value = undefined;
1019
- }
1020
- }
1449
+ const updateFunctionComponent = (fiber) => {
1450
+ const state = getState();
1451
+ state.wipFiber = fiber;
1452
+ state.hookIndex = 0;
1453
+ state.wipFiber.hooks = [];
1021
1454
 
1022
- const hook = {
1023
- hookID: hookIndex,
1024
- type: RYUNIX_TYPES.RYUNIX_MEMO,
1025
- value,
1026
- deps,
1027
- };
1455
+ const children = [fiber.type(fiber.props)];
1028
1456
 
1029
- wipFiber.hooks[hookIndex] = hook;
1030
- state.hookIndex++;
1031
- return value
1457
+ if (fiber.type._contextId && fiber.props.value !== undefined) {
1458
+ fiber._contextId = fiber.type._contextId;
1459
+ fiber._contextValue = fiber.props.value;
1460
+ }
1461
+
1462
+ reconcileChildren(fiber, children);
1032
1463
  };
1033
1464
 
1034
- const useCallback = (callback, deps) => {
1035
- if (!is.function(callback)) {
1036
- throw new Error('useCallback requires a function as first argument')
1465
+ const updateHostComponent = (fiber) => {
1466
+ if (!fiber.dom) {
1467
+ fiber.dom = createDom(fiber);
1037
1468
  }
1038
- return useMemo(() => callback, deps)
1469
+ const children = fiber.props?.children || [];
1470
+ reconcileChildren(fiber, children);
1039
1471
  };
1040
1472
 
1041
- const createContext = (
1042
- contextId = RYUNIX_TYPES.RYUNIX_CONTEXT,
1043
- defaultValue = {},
1044
- ) => {
1045
- const Provider = ({ children, value }) => {
1046
- const element = Fragment({ children });
1047
- element._contextId = contextId;
1048
- element._contextValue = value;
1049
- return element
1050
- };
1473
+ /**
1474
+ * The Component `Image` takes in a `src` and other props, and returns an `img` element with the
1475
+ * specified `src` and props.
1476
+ * @returns The `Image` component is being returned. It is a functional component that renders an `img`
1477
+ * element with the specified `src` and other props passed to it.
1478
+ */
1479
+ const Image = ({ src, ...props }) => {
1480
+ return createElement('img', { ...props, src })
1481
+ };
1051
1482
 
1052
- Provider._contextId = contextId;
1483
+ const { Provider: MDXProvider, useContext: useMDXComponents } = createContext(
1484
+ 'ryunix.mdx',
1485
+ {},
1486
+ );
1053
1487
 
1054
- const useContext = (ctxID = contextId) => {
1055
- validateHookCall();
1488
+ /**
1489
+ * Get merged MDX components from context and provided components
1490
+ * @param {Object} components - Additional components to merge
1491
+ * @returns {Object} Merged components object
1492
+ */
1493
+ const getMDXComponents = (components) => {
1494
+ const contextComponents = useMDXComponents();
1495
+ return {
1496
+ ...contextComponents,
1497
+ ...components,
1498
+ }
1499
+ };
1056
1500
 
1057
- const state = getState();
1058
- let fiber = state.wipFiber;
1501
+ /**
1502
+ * Default MDX components with Ryunix-optimized rendering
1503
+ */
1504
+ const defaultComponents = {
1505
+ // Headings
1506
+ h1: (props) => createElement('h1', { ...props }),
1507
+ h2: (props) => createElement('h2', { ...props }),
1508
+ h3: (props) => createElement('h3', { ...props }),
1509
+ h4: (props) => createElement('h4', { ...props }),
1510
+ h5: (props) => createElement('h5', { ...props }),
1511
+ h6: (props) => createElement('h6', { ...props }),
1512
+
1513
+ // Text
1514
+ p: (props) => createElement('p', { ...props }),
1515
+ a: (props) => createElement('a', { ...props }),
1516
+ strong: (props) => createElement('strong', { ...props }),
1517
+ em: (props) => createElement('em', { ...props }),
1518
+ code: (props) => createElement('code', { ...props }),
1519
+
1520
+ // Lists
1521
+ ul: (props) => createElement('ul', { ...props }),
1522
+ ol: (props) => createElement('ol', { ...props }),
1523
+ li: (props) => createElement('li', { ...props }),
1524
+
1525
+ // Blocks
1526
+ blockquote: (props) => createElement('blockquote', { ...props }),
1527
+ pre: (props) => createElement('pre', { ...props }),
1528
+ hr: (props) => createElement('hr', { ...props }),
1529
+
1530
+ // Tables
1531
+ table: (props) => createElement('table', { ...props }),
1532
+ thead: (props) => createElement('thead', { ...props }),
1533
+ tbody: (props) => createElement('tbody', { ...props }),
1534
+ tr: (props) => createElement('tr', { ...props }),
1535
+ th: (props) => createElement('th', { ...props }),
1536
+ td: (props) => createElement('td', { ...props }),
1537
+
1538
+ // Media
1539
+ img: (props) => createElement('img', { ...props }),
1540
+ };
1059
1541
 
1060
- while (fiber) {
1061
- if (fiber._contextId === ctxID && fiber._contextValue !== undefined) {
1062
- return fiber._contextValue
1063
- }
1064
- if (
1065
- fiber.type?._contextId === ctxID &&
1066
- fiber.props?.value !== undefined
1067
- ) {
1068
- return fiber.props.value
1069
- }
1070
- fiber = fiber.parent;
1071
- }
1072
- return defaultValue
1073
- };
1542
+ /**
1543
+ * MDX Wrapper component
1544
+ * Provides default styling and components for MDX content
1545
+ */
1546
+ const MDXContent = ({ children, components = {} }) => {
1547
+ const mergedComponents = getMDXComponents(components);
1074
1548
 
1075
- return { Provider, useContext }
1549
+ return createElement(
1550
+ MDXProvider,
1551
+ { value: mergedComponents },
1552
+ createElement('div', null, children),
1553
+ )
1076
1554
  };
1077
1555
 
1078
- const useQuery = () => {
1079
- if (typeof window === 'undefined') return {}
1556
+ /**
1557
+ * Performance profiler for Ryunix
1558
+ */
1559
+ class Profiler {
1560
+ constructor() {
1561
+ this.enabled = process.env.NODE_ENV !== 'production';
1562
+ this.measures = new Map();
1563
+ this.renderTimes = [];
1564
+ this.maxSamples = 100;
1565
+ }
1080
1566
 
1081
- const searchParams = new URLSearchParams(window.location.search);
1082
- const query = {};
1083
- for (const [key, value] of searchParams.entries()) {
1084
- query[key] = value;
1567
+ startMeasure(name) {
1568
+ if (!this.enabled) return
1569
+ this.measures.set(name, performance.now());
1085
1570
  }
1086
- return query
1087
- };
1088
1571
 
1089
- const useHash = () => {
1090
- if (typeof window === 'undefined') return ''
1572
+ endMeasure(name) {
1573
+ if (!this.enabled) return
1574
+ const start = this.measures.get(name);
1575
+ if (!start) return
1091
1576
 
1092
- const [hash, setHash] = useStore(window.location.hash);
1093
- useEffect(() => {
1094
- const onHashChange = () => setHash(window.location.hash);
1095
- window.addEventListener('hashchange', onHashChange);
1096
- return () => window.removeEventListener('hashchange', onHashChange)
1097
- }, []);
1098
- return hash
1099
- };
1577
+ const duration = performance.now() - start;
1578
+ this.measures.delete(name);
1100
1579
 
1101
- const useMetadata = (tags = {}, options = {}) => {
1102
- useEffect(() => {
1103
- if (typeof document === 'undefined') return
1580
+ return duration
1581
+ }
1104
1582
 
1105
- let finalTitle = 'Ryunix App';
1106
- const template = options.title?.template;
1107
- const defaultTitle = options.title?.prefix || 'Ryunix App';
1108
- const pageTitle = tags.pageTitle || tags.title;
1583
+ recordRender(componentName, duration) {
1584
+ if (!this.enabled) return
1109
1585
 
1110
- if (is.string(pageTitle) && pageTitle.trim()) {
1111
- finalTitle = template?.includes('%s')
1112
- ? template.replace('%s', pageTitle)
1113
- : pageTitle;
1114
- } else {
1115
- finalTitle = defaultTitle;
1586
+ this.renderTimes.push({
1587
+ component: componentName,
1588
+ duration,
1589
+ timestamp: Date.now(),
1590
+ });
1591
+
1592
+ if (this.renderTimes.length > this.maxSamples) {
1593
+ this.renderTimes.shift();
1116
1594
  }
1595
+ }
1117
1596
 
1118
- document.title = finalTitle;
1597
+ getStats() {
1598
+ if (!this.enabled) return null
1119
1599
 
1120
- if (tags.canonical) {
1121
- let link = document.querySelector('link[rel="canonical"]');
1122
- if (!link) {
1123
- link = document.createElement('link');
1124
- link.setAttribute('rel', 'canonical');
1125
- document.head.appendChild(link);
1126
- }
1127
- link.setAttribute('href', tags.canonical);
1128
- }
1600
+ const total = this.renderTimes.reduce((sum, r) => sum + r.duration, 0);
1601
+ const avg = total / this.renderTimes.length;
1602
+ const max = Math.max(...this.renderTimes.map((r) => r.duration));
1603
+ const min = Math.min(...this.renderTimes.map((r) => r.duration));
1129
1604
 
1130
- Object.entries(tags).forEach(([key, value]) => {
1131
- if (['title', 'pageTitle', 'canonical'].includes(key)) return
1605
+ return { total, avg, max, min, count: this.renderTimes.length }
1606
+ }
1132
1607
 
1133
- const isProperty = key.startsWith('og:') || key.startsWith('twitter:');
1134
- const selector = `meta[${isProperty ? 'property' : 'name'}='${key}']`;
1135
- let meta = document.head.querySelector(selector);
1608
+ getSlowestComponents(limit = 10) {
1609
+ if (!this.enabled) return []
1136
1610
 
1137
- if (!meta) {
1138
- meta = document.createElement('meta');
1139
- meta.setAttribute(isProperty ? 'property' : 'name', key);
1140
- document.head.appendChild(meta);
1611
+ const byComponent = new Map();
1612
+
1613
+ this.renderTimes.forEach(({ component, duration }) => {
1614
+ if (!byComponent.has(component)) {
1615
+ byComponent.set(component, { total: 0, count: 0, max: 0 });
1141
1616
  }
1142
- meta.setAttribute('content', value);
1617
+ const stats = byComponent.get(component);
1618
+ stats.total += duration;
1619
+ stats.count++;
1620
+ stats.max = Math.max(stats.max, duration);
1143
1621
  });
1144
- }, [JSON.stringify(tags), JSON.stringify(options)]);
1145
- };
1146
1622
 
1147
- // Router Context
1148
- const RouterContext = createContext('ryunix.navigation', {
1149
- location: '/',
1150
- params: {},
1151
- query: {},
1152
- navigate: (path) => {},
1153
- route: null,
1154
- });
1623
+ return Array.from(byComponent.entries())
1624
+ .map(([name, stats]) => ({
1625
+ name,
1626
+ avg: stats.total / stats.count,
1627
+ max: stats.max,
1628
+ count: stats.count,
1629
+ }))
1630
+ .sort((a, b) => b.avg - a.avg)
1631
+ .slice(0, limit)
1632
+ }
1155
1633
 
1156
- const findRoute = (routes, path) => {
1157
- const pathname = path.split('?')[0].split('#')[0];
1158
- const notFoundRoute = routes.find((route) => route.NotFound);
1159
- const notFound = notFoundRoute
1160
- ? { route: { component: notFoundRoute.NotFound }, params: {} }
1161
- : { route: { component: null }, params: {} };
1634
+ logStats() {
1635
+ if (!this.enabled) return
1162
1636
 
1163
- for (const route of routes) {
1164
- if (route.subRoutes) {
1165
- const childRoute = findRoute(route.subRoutes, path);
1166
- if (childRoute) return childRoute
1167
- }
1168
- if (route.path === '*') return notFound
1169
- if (!route.path || typeof route.path !== 'string') continue
1637
+ const stats = this.getStats();
1638
+ if (!stats) return
1170
1639
 
1171
- const keys = [];
1172
- const pattern = new RegExp(
1173
- `^${route.path.replace(/:\w+/g, (match) => {
1174
- keys.push(match.substring(1));
1175
- return '([^/]+)'
1176
- })}$`,
1640
+ console.group('🔍 Ryunix Performance Stats');
1641
+ console.log(`Total renders: ${stats.count}`);
1642
+ console.log(`Avg render time: ${stats.avg.toFixed(2)}ms`);
1643
+ console.log(
1644
+ `Min: ${stats.min.toFixed(2)}ms | Max: ${stats.max.toFixed(2)}ms`,
1177
1645
  );
1178
1646
 
1179
- const match = pathname.match(pattern);
1180
- if (match) {
1181
- const params = keys.reduce((acc, key, index) => {
1182
- acc[key] = match[index + 1];
1183
- return acc
1184
- }, {});
1185
- return { route, params }
1186
- }
1187
- }
1188
- return notFound
1189
- };
1190
-
1191
- const RouterProvider = ({ routes, children }) => {
1192
- const [location, setLocation] = useStore(window.location.pathname);
1193
-
1194
- useEffect(() => {
1195
- const update = () => setLocation(window.location.pathname);
1196
- window.addEventListener('popstate', update);
1197
- window.addEventListener('hashchange', update);
1198
- return () => {
1199
- window.removeEventListener('popstate', update);
1200
- window.removeEventListener('hashchange', update);
1647
+ const slowest = this.getSlowestComponents(5);
1648
+ if (slowest.length > 0) {
1649
+ console.log('\n⚠️ Slowest components:');
1650
+ slowest.forEach((comp, i) => {
1651
+ console.log(
1652
+ `${i + 1}. ${comp.name}: ${comp.avg.toFixed(2)}ms avg (${comp.count} renders)`,
1653
+ );
1654
+ });
1201
1655
  }
1202
- }, [location]);
1656
+ console.groupEnd();
1657
+ }
1203
1658
 
1204
- const navigate = (path) => {
1205
- window.history.pushState({}, '', path);
1206
- setLocation(path);
1207
- };
1659
+ clear() {
1660
+ this.renderTimes = [];
1661
+ this.measures.clear();
1662
+ }
1208
1663
 
1209
- const currentRouteData = findRoute(routes, location) || {};
1210
- const query = useQuery();
1664
+ enable() {
1665
+ this.enabled = true;
1666
+ }
1211
1667
 
1212
- const contextValue = {
1213
- location,
1214
- params: currentRouteData.params || {},
1215
- query,
1216
- navigate,
1217
- route: currentRouteData.route,
1218
- };
1668
+ disable() {
1669
+ this.enabled = false;
1670
+ }
1671
+ }
1219
1672
 
1220
- return createElement(
1221
- RouterContext.Provider,
1222
- { value: contextValue },
1223
- Fragment({ children }),
1224
- )
1673
+ // Global profiler instance
1674
+ const profiler = new Profiler();
1675
+
1676
+ /**
1677
+ * Hook to profile component render
1678
+ */
1679
+ const useProfiler = (componentName) => {
1680
+ const startTime = performance.now();
1681
+
1682
+ return () => {
1683
+ const duration = performance.now() - startTime;
1684
+ profiler.recordRender(componentName, duration);
1685
+ }
1225
1686
  };
1226
1687
 
1227
- const useRouter = () => {
1228
- return RouterContext.useContext('ryunix.navigation')
1688
+ /**
1689
+ * HOC to profile component
1690
+ */
1691
+ const withProfiler = (Component, name) => {
1692
+ return (props) => {
1693
+ profiler.startMeasure(name);
1694
+ const result = Component(props);
1695
+ const duration = profiler.endMeasure(name);
1696
+ if (duration) profiler.recordRender(name, duration);
1697
+ return result
1698
+ }
1229
1699
  };
1230
1700
 
1231
- const Children = () => {
1232
- const { route, params, query, location } = useRouter();
1233
- if (!route || !route.component) return null
1234
- const hash = useHash();
1701
+ const workLoop = (deadline) => {
1702
+ const state = getState();
1703
+ let shouldYield = false;
1235
1704
 
1236
- useEffect(() => {
1237
- if (hash) {
1238
- const id = hash.slice(1);
1239
- const el = document.getElementById(id);
1240
- if (el) el.scrollIntoView({ block: 'start', behavior: 'smooth' });
1241
- }
1242
- }, [hash]);
1705
+ while (state.nextUnitOfWork && !shouldYield) {
1706
+ state.nextUnitOfWork = performUnitOfWork(state.nextUnitOfWork);
1707
+ shouldYield = deadline.timeRemaining() < 1;
1708
+ }
1243
1709
 
1244
- return createElement(route.component, {
1245
- key: location,
1246
- params,
1247
- query,
1248
- hash,
1249
- })
1710
+ if (!state.nextUnitOfWork && state.wipRoot) {
1711
+ commitRoot();
1712
+ }
1713
+
1714
+ requestIdleCallback(workLoop);
1250
1715
  };
1251
1716
 
1252
- const NavLink = ({ to, exact = false, ...props }) => {
1253
- const { location, navigate } = useRouter();
1254
- const isActive = exact ? location === to : location.startsWith(to);
1717
+ requestIdleCallback(workLoop);
1255
1718
 
1256
- const resolveClass = (cls) =>
1257
- typeof cls === 'function' ? cls({ isActive }) : cls || '';
1719
+ const performUnitOfWork = (fiber) => {
1720
+ const componentName = fiber.type?.name || fiber.type?.displayName || 'Unknown';
1258
1721
 
1259
- const handleClick = (e) => {
1260
- e.preventDefault();
1261
- navigate(to);
1262
- };
1722
+ profiler.startMeasure(componentName);
1263
1723
 
1264
- const classAttrName = props['ryunix-class'] ? 'ryunix-class' : 'className';
1265
- const classAttrValue = resolveClass(
1266
- props['ryunix-class'] || props['className'],
1267
- );
1724
+ const isFunctionComponent = fiber.type instanceof Function;
1725
+ if (isFunctionComponent) {
1726
+ updateFunctionComponent(fiber);
1727
+ } else {
1728
+ updateHostComponent(fiber);
1729
+ }
1268
1730
 
1269
- const {
1270
- ['ryunix-class']: _omitRyunix,
1271
- className: _omitClassName,
1272
- ...cleanedProps
1273
- } = props;
1731
+ const duration = profiler.endMeasure(componentName);
1732
+ if (duration) profiler.recordRender(componentName, duration);
1274
1733
 
1275
- return createElement(
1276
- 'a',
1277
- {
1278
- href: to,
1279
- onClick: handleClick,
1280
- [classAttrName]: classAttrValue,
1281
- ...cleanedProps,
1282
- },
1283
- props.children,
1284
- )
1734
+ if (fiber.child) {
1735
+ return fiber.child
1736
+ }
1737
+ let nextFiber = fiber;
1738
+ while (nextFiber) {
1739
+ if (nextFiber.sibling) {
1740
+ return nextFiber.sibling
1741
+ }
1742
+ nextFiber = nextFiber.parent;
1743
+ }
1285
1744
  };
1286
1745
 
1287
- /**
1288
- * useStore with priority support
1289
- */
1290
- const useStorePriority = (initialState) => {
1291
- const reducer = (state, action) =>
1292
- typeof action === 'function' ? action.value(state) : action.value;
1293
-
1294
- const [state, baseDispatch] = useReducer(reducer, initialState);
1295
-
1296
- const dispatch = (action, priority = Priority.NORMAL) => {
1297
- const wrappedAction = {
1298
- value: action,
1299
- priority,
1300
- };
1301
-
1302
- baseDispatch(wrappedAction);
1303
- };
1746
+ const scheduleWork = (root, priority = Priority.NORMAL) => {
1747
+ const state = getState();
1748
+ state.nextUnitOfWork = root;
1749
+ state.wipRoot = root;
1750
+ state.deletions = [];
1751
+ state.hookIndex = 0;
1752
+ state.effects = [];
1304
1753
 
1305
- return [state, dispatch]
1754
+ // Higher priority = faster scheduling
1755
+ if (priority <= Priority.USER_BLOCKING) {
1756
+ requestIdleCallback(workLoop);
1757
+ } else {
1758
+ setTimeout(() => requestIdleCallback(workLoop), 0);
1759
+ }
1306
1760
  };
1307
1761
 
1308
1762
  /**
1309
- * useTransition - Mark updates as non-urgent
1763
+ * The `render` function in JavaScript updates the DOM with a new element and schedules work to be done
1764
+ * on the element.
1765
+ * @param element - The `element` parameter in the `render` function is the element that you want to
1766
+ * render in the specified container. It could be a DOM element, a component, or any other valid
1767
+ * element that you want to display on the screen.
1768
+ * @param container - The `container` parameter in the `render` function is the DOM element where the
1769
+ * `element` will be rendered. It is the target container where the element will be appended as a
1770
+ * child.
1771
+ * @returns The `render` function is returning the `state.wipRoot` object.
1310
1772
  */
1311
- const useTransition = () => {
1312
- const [isPending, setIsPending] = useStorePriority(false);
1313
-
1314
- const startTransition = (callback) => {
1315
- setIsPending(true, Priority.IMMEDIATE);
1316
-
1317
- setTimeout(() => {
1318
- callback();
1319
- setIsPending(false, Priority.IMMEDIATE);
1320
- }, 0);
1773
+ const render = (element, container) => {
1774
+ const state = getState();
1775
+ state.wipRoot = {
1776
+ dom: container,
1777
+ props: {
1778
+ children: [element],
1779
+ },
1780
+ alternate: state.currentRoot,
1321
1781
  };
1322
1782
 
1323
- return [isPending, startTransition]
1783
+ state.nextUnitOfWork = state.wipRoot;
1784
+ state.deletions = [];
1785
+ scheduleWork(state.wipRoot);
1786
+ return state.wipRoot
1324
1787
  };
1325
1788
 
1326
1789
  /**
1327
- * useDeferredValue - Defer value updates
1790
+ * The `init` function initializes a rendering process for a main element within a specified container
1791
+ * root element.
1792
+ * @param MainElement - MainElement is the main component or element that you want to render on the
1793
+ * webpage. It could be a React component, a DOM element, or any other element that you want to display
1794
+ * on the page.
1795
+ * @param [root=__ryunix] - The `root` parameter in the `init` function is a default parameter with the
1796
+ * value `'__ryunix'`. If no value is provided for `root` when calling the `init` function, it will
1797
+ * default to `'__ryunix'`.
1798
+ * @returns The `renderProcess` function is being returned from the `init` function.
1328
1799
  */
1329
- const useDeferredValue = (value) => {
1330
- const [deferredValue, setDeferredValue] = useStorePriority(value);
1331
-
1332
- useEffect(() => {
1333
- const timeout = setTimeout(() => {
1334
- setDeferredValue(value, Priority.LOW);
1335
- }, 100);
1336
-
1337
- return () => clearTimeout(timeout)
1338
- }, [value]);
1339
-
1340
- return deferredValue
1800
+ const init = (MainElement, root = '__ryunix') => {
1801
+ const state = getState();
1802
+ state.containerRoot = document.getElementById(root);
1803
+ const renderProcess = render(MainElement, state.containerRoot);
1804
+ return renderProcess
1341
1805
  };
1342
1806
 
1343
- var hooks = /*#__PURE__*/Object.freeze({
1344
- __proto__: null,
1345
- Children: Children,
1346
- NavLink: NavLink,
1347
- RouterProvider: RouterProvider,
1348
- createContext: createContext,
1349
- useCallback: useCallback,
1350
- useDeferredValue: useDeferredValue,
1351
- useEffect: useEffect,
1352
- useHash: useHash,
1353
- useMemo: useMemo,
1354
- useMetadata: useMetadata,
1355
- useQuery: useQuery,
1356
- useReducer: useReducer,
1357
- useRef: useRef,
1358
- useRouter: useRouter,
1359
- useStore: useStore,
1360
- useStorePriority: useStorePriority,
1361
- useTransition: useTransition
1362
- });
1807
+ const safeRender = (component, props, onError) => {
1808
+ try {
1809
+ return component(props)
1810
+ } catch (error) {
1811
+ if (process.env.NODE_ENV !== 'production') {
1812
+ console.error('Component error:', error);
1813
+ }
1814
+ if (onError) onError(error);
1815
+ return null
1816
+ }
1817
+ };
1363
1818
 
1364
1819
  /**
1365
1820
  * memo - Memoize component to prevent unnecessary re-renders
@@ -1431,7 +1886,11 @@ let isBatching = false;
1431
1886
  let pendingUpdates = [];
1432
1887
 
1433
1888
  /**
1434
- * Batch multiple state updates into single render
1889
+ * The `batchUpdates` function in JavaScript allows for batching multiple updates and flushing them all
1890
+ * at once.
1891
+ * @param callback - The `callback` parameter in the `batchUpdates` function is a function that will be
1892
+ * executed within a batch update. This function can contain multiple updates that need to be processed
1893
+ * together in a batch to improve performance and avoid unnecessary re-renders.
1435
1894
  */
1436
1895
  const batchUpdates = (callback) => {
1437
1896
  const wasBatching = isBatching;
@@ -1449,7 +1908,9 @@ const batchUpdates = (callback) => {
1449
1908
  };
1450
1909
 
1451
1910
  /**
1452
- * Flush all pending updates
1911
+ * The `flushUpdates` function processes and executes pending updates stored in an array.
1912
+ * @returns If the `pendingUpdates` array is empty, the `flushUpdates` function will return nothing
1913
+ * (undefined).
1453
1914
  */
1454
1915
  const flushUpdates = () => {
1455
1916
  if (pendingUpdates.length === 0) return
@@ -1463,11 +1924,15 @@ const flushUpdates = () => {
1463
1924
 
1464
1925
  var Ryunix = /*#__PURE__*/Object.freeze({
1465
1926
  __proto__: null,
1927
+ Children: Children,
1466
1928
  Fragment: Fragment,
1467
1929
  Hooks: hooks,
1930
+ NavLink: NavLink,
1468
1931
  Priority: Priority,
1932
+ RouterProvider: RouterProvider,
1469
1933
  Suspense: Suspense,
1470
1934
  batchUpdates: batchUpdates,
1935
+ createContext: createContext,
1471
1936
  createElement: createElement,
1472
1937
  init: init,
1473
1938
  lazy: lazy,
@@ -1475,11 +1940,26 @@ var Ryunix = /*#__PURE__*/Object.freeze({
1475
1940
  profiler: profiler,
1476
1941
  render: render,
1477
1942
  safeRender: safeRender,
1943
+ useCallback: useCallback,
1944
+ useDeferredValue: useDeferredValue,
1945
+ useEffect: useEffect,
1946
+ useHash: useHash,
1947
+ useMemo: useMemo,
1948
+ useMetadata: useMetadata,
1949
+ usePersitentStore: usePersitentStore,
1478
1950
  useProfiler: useProfiler,
1951
+ useQuery: useQuery,
1952
+ useReducer: useReducer,
1953
+ useRef: useRef,
1954
+ useRouter: useRouter,
1955
+ useStore: useStore,
1956
+ useStorePriority: useStorePriority,
1957
+ useSwitch: useSwitch,
1958
+ useTransition: useTransition,
1479
1959
  withProfiler: withProfiler
1480
1960
  });
1481
1961
 
1482
1962
  window.Ryunix = Ryunix;
1483
1963
 
1484
- export { Fragment, hooks as Hooks, Image, Priority, Suspense, batchUpdates, createElement, Ryunix as default, init, lazy, memo, profiler, render, safeRender, useProfiler, withProfiler };
1964
+ export { Children, Fragment, hooks as Hooks, Image, MDXContent, MDXProvider, NavLink, Priority, RouterProvider, Suspense, batchUpdates, createContext, createElement, Ryunix as default, defaultComponents, getMDXComponents, init, lazy, memo, profiler, render, safeRender, useCallback, useDeferredValue, useEffect, useHash, useMDXComponents, useMemo, useMetadata, usePersitentStore, useProfiler, useQuery, useReducer, useRef, useRouter, useStore, useStorePriority, useSwitch, useTransition, withProfiler };
1485
1965
  //# sourceMappingURL=Ryunix.esm.js.map