@unsetsoft/ryunixjs 1.1.37 → 1.2.0-canary.0

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.js CHANGED
@@ -714,16 +714,18 @@
714
714
  };
715
715
 
716
716
  /**
717
- * This is a function that creates a hook for managing side effects in Ryunix components.
718
- * @param effect - The effect function that will be executed after the component has rendered or when
719
- * the dependencies have changed. It can perform side effects such as fetching data, updating the DOM,
720
- * or subscribing to events.
721
- * @param deps - An array of dependencies that the effect depends on. If any of the dependencies change
722
- * between renders, the effect will be re-run. If the array is empty, the effect will only run once on
723
- * mount and never again.
717
+ * useEffect seguro para SSR/SSG: no ejecuta efectos en el servidor.
718
+ * @param effect - Función de efecto.
719
+ * @param deps - Dependencias.
724
720
  */
725
721
 
726
722
  const useEffect = (callback, deps) => {
723
+ // No ejecutar efectos en SSR/SSG
724
+ if (typeof window === 'undefined') {
725
+ vars.hookIndex++;
726
+ return
727
+ }
728
+
727
729
  const oldHook =
728
730
  vars.wipFiber.alternate &&
729
731
  vars.wipFiber.alternate.hooks &&
@@ -856,7 +858,9 @@
856
858
  }
857
859
  };
858
860
 
861
+ // Proteger hooks que usan window/document
859
862
  const useQuery = () => {
863
+ if (typeof window === 'undefined') return {}
860
864
  const searchParams = new URLSearchParams(window.location.search);
861
865
  const query = {};
862
866
  for (let [key, value] of searchParams.entries()) {
@@ -866,6 +870,7 @@
866
870
  };
867
871
 
868
872
  const useHash = () => {
873
+ if (typeof window === 'undefined') return ''
869
874
  const [hash, setHash] = useStore(window.location.hash);
870
875
  useEffect(() => {
871
876
  const onHashChange = () => {
@@ -933,31 +938,46 @@
933
938
  return notFound
934
939
  };
935
940
 
936
- const RouterProvider = ({ routes, children }) => {
937
- const [location, setLocation] = useStore(window.location.pathname);
938
-
941
+ /**
942
+ * RouterProvider universal: acepta contexto externo (SSR/SSG) o usa window.location (SPA).
943
+ * @param {Object} props - { routes, children, ssrContext }
944
+ * @example
945
+ * // SSR/SSG:
946
+ * <RouterProvider routes={routes} ssrContext={{ location: '/about', query: { q: 'x' } }}>
947
+ * ...
948
+ * </RouterProvider>
949
+ */
950
+ const RouterProvider = ({ routes, children, ssrContext }) => {
951
+ // Si hay contexto externo (SSR/SSG), úsalo
952
+ const isSSR = typeof window === 'undefined' || !!ssrContext;
953
+ const location = isSSR
954
+ ? (ssrContext && ssrContext.location) || '/'
955
+ : window.location.pathname;
956
+ const [loc, setLoc] = useStore(location);
957
+
958
+ // SPA: escucha cambios de ruta
939
959
  useEffect(() => {
940
- const update = () => setLocation(window.location.pathname);
941
-
960
+ if (isSSR) return
961
+ const update = () => setLoc(window.location.pathname);
942
962
  window.addEventListener('popstate', update);
943
963
  window.addEventListener('hashchange', update);
944
964
  return () => {
945
965
  window.removeEventListener('popstate', update);
946
966
  window.removeEventListener('hashchange', update);
947
967
  }
948
- }, [location]);
968
+ }, [loc]);
949
969
 
950
970
  const navigate = (path) => {
971
+ if (isSSR) return
951
972
  window.history.pushState({}, '', path);
952
- setLocation(path);
973
+ setLoc(path);
953
974
  };
954
975
 
955
- const currentRouteData = findRoute(routes, location) || {};
956
-
957
- const query = useQuery();
976
+ const currentRouteData = findRoute(routes, loc) || {};
977
+ const query = isSSR ? (ssrContext && ssrContext.query) || {} : useQuery();
958
978
 
959
979
  const contextValue = {
960
- location,
980
+ location: loc,
961
981
  params: currentRouteData.params || {},
962
982
  query,
963
983
  navigate,
@@ -967,12 +987,16 @@
967
987
  return createElement(
968
988
  RouterContext.Provider,
969
989
  { value: contextValue },
970
- Fragment({
971
- children: children,
972
- }),
990
+ Fragment({ children }),
973
991
  )
974
992
  };
975
993
 
994
+ /**
995
+ * Universal useRouter: obtiene el contexto de ruta, compatible con SSR/SSG y SPA.
996
+ * @returns {Object} { location, params, query, navigate, route }
997
+ * @example
998
+ * const { location, params, query } = useRouter();
999
+ */
976
1000
  const useRouter = () => {
977
1001
  return RouterContext.useContext('ryunix.navigation')
978
1002
  };
@@ -1035,6 +1059,148 @@
1035
1059
  )
1036
1060
  };
1037
1061
 
1062
+ /**
1063
+ * useMetadata: Hook para gestionar dinámicamente metadatos SEO y recursos en <head>.
1064
+ * Ahora soporta title, meta, link, script, style y bloques personalizados en SSR/SSG.
1065
+ *
1066
+ * @param {Object} tags - Metadatos y recursos a insertar/actualizar.
1067
+ * Soporta: title, canonical, meta, og:*, twitter:*, script, style, custom.
1068
+ * - script/style/custom pueden ser string o array de strings (contenido o etiquetas completas).
1069
+ * @param {Object} options - Opcional. Permite definir template y default para el título.
1070
+ *
1071
+ * Ejemplo avanzado SSR/SSG:
1072
+ * useMetadata({
1073
+ * title: 'Mi página',
1074
+ * description: 'Desc',
1075
+ * script: [
1076
+ * '<script src="/analytics.js"></script>',
1077
+ * '<script>console.log("hi")</script>'
1078
+ * ],
1079
+ * style: '<style>body{background:#000}</style>',
1080
+ * custom: '<!-- Bloque personalizado -->'
1081
+ * })
1082
+ */
1083
+ const useMetadata = (tags = {}, options = {}) => {
1084
+ // SSR/SSG: collect metadata in global context if available
1085
+ const ssrContext =
1086
+ typeof globalThis !== 'undefined' && globalThis.__RYUNIX_SSR_CONTEXT__;
1087
+ if (ssrContext) {
1088
+ let finalTitle = '';
1089
+ let template = undefined;
1090
+ let defaultTitle = 'Ryunix App';
1091
+ if (options.title && typeof options.title === 'object') {
1092
+ template = options.title.template;
1093
+ if (typeof options.title.prefix === 'string') {
1094
+ defaultTitle = options.title.prefix;
1095
+ }
1096
+ }
1097
+ let pageTitle = tags.pageTitle || tags.title;
1098
+ if (typeof pageTitle === 'string') {
1099
+ if (pageTitle.trim() === '') {
1100
+ finalTitle = defaultTitle;
1101
+ } else if (template && template.includes('%s')) {
1102
+ finalTitle = template.replace('%s', pageTitle);
1103
+ } else {
1104
+ finalTitle = pageTitle;
1105
+ }
1106
+ } else if (typeof pageTitle === 'object' && pageTitle !== null) {
1107
+ finalTitle = defaultTitle;
1108
+ } else if (!pageTitle) {
1109
+ finalTitle = defaultTitle;
1110
+ }
1111
+ if (finalTitle) {
1112
+ ssrContext.head.push(`<title>${finalTitle}</title>`);
1113
+ }
1114
+ if (tags.canonical) {
1115
+ ssrContext.head.push(`<link rel="canonical" href="${tags.canonical}" />`);
1116
+ }
1117
+ Object.entries(tags).forEach(([key, value]) => {
1118
+ if (key === 'title' || key === 'pageTitle' || key === 'canonical') return
1119
+ if (key.startsWith('og:') || key.startsWith('twitter:')) {
1120
+ ssrContext.head.push(`<meta property="${key}" content="${value}" />`);
1121
+ } else {
1122
+ ssrContext.head.push(`<meta name="${key}" content="${value}" />`);
1123
+ }
1124
+ });
1125
+ // Soporte para script/style/custom
1126
+ if (tags.script) {
1127
+ (Array.isArray(tags.script) ? tags.script : [tags.script]).forEach(
1128
+ (s) => {
1129
+ ssrContext.head.push(s);
1130
+ },
1131
+ );
1132
+ }
1133
+ if (tags.style) {
1134
+ (Array.isArray(tags.style) ? tags.style : [tags.style]).forEach((s) => {
1135
+ ssrContext.head.push(s);
1136
+ });
1137
+ }
1138
+ if (tags.custom) {
1139
+ (Array.isArray(tags.custom) ? tags.custom : [tags.custom]).forEach(
1140
+ (c) => {
1141
+ ssrContext.head.push(c);
1142
+ },
1143
+ );
1144
+ }
1145
+ return
1146
+ }
1147
+ // SPA: fallback to DOM logic
1148
+ useEffect(() => {
1149
+ if (typeof document === 'undefined') return // SSR safe
1150
+ let finalTitle = '';
1151
+ let template = undefined;
1152
+ let defaultTitle = 'Ryunix App';
1153
+ if (options.title && typeof options.title === 'object') {
1154
+ template = options.title.template;
1155
+ if (typeof options.title.prefix === 'string') {
1156
+ defaultTitle = options.title.prefix;
1157
+ }
1158
+ }
1159
+ let pageTitle = tags.pageTitle || tags.title;
1160
+ if (typeof pageTitle === 'string') {
1161
+ if (pageTitle.trim() === '') {
1162
+ finalTitle = defaultTitle;
1163
+ } else if (template && template.includes('%s')) {
1164
+ finalTitle = template.replace('%s', pageTitle);
1165
+ } else {
1166
+ finalTitle = pageTitle;
1167
+ }
1168
+ } else if (typeof pageTitle === 'object' && pageTitle !== null) {
1169
+ finalTitle = defaultTitle;
1170
+ } else if (!pageTitle) {
1171
+ finalTitle = defaultTitle;
1172
+ }
1173
+ document.title = finalTitle;
1174
+ if (tags.canonical) {
1175
+ let link = document.querySelector('link[rel="canonical"]');
1176
+ if (!link) {
1177
+ link = document.createElement('link');
1178
+ link.setAttribute('rel', 'canonical');
1179
+ document.head.appendChild(link);
1180
+ }
1181
+ link.setAttribute('href', tags.canonical);
1182
+ }
1183
+ Object.entries(tags).forEach(([key, value]) => {
1184
+ if (key === 'title' || key === 'pageTitle' || key === 'canonical') return
1185
+ let selector = `meta[name='${key}']`;
1186
+ if (key.startsWith('og:') || key.startsWith('twitter:')) {
1187
+ selector = `meta[property='${key}']`;
1188
+ }
1189
+ let meta = document.head.querySelector(selector);
1190
+ if (!meta) {
1191
+ meta = document.createElement('meta');
1192
+ if (key.startsWith('og:') || key.startsWith('twitter:')) {
1193
+ meta.setAttribute('property', key);
1194
+ } else {
1195
+ meta.setAttribute('name', key);
1196
+ }
1197
+ document.head.appendChild(meta);
1198
+ }
1199
+ meta.setAttribute('content', value);
1200
+ });
1201
+ }, [JSON.stringify(tags), JSON.stringify(options)]);
1202
+ };
1203
+
1038
1204
  var Hooks = /*#__PURE__*/Object.freeze({
1039
1205
  __proto__: null,
1040
1206
  Children: Children,
@@ -1045,6 +1211,7 @@
1045
1211
  useEffect: useEffect,
1046
1212
  useHash: useHash,
1047
1213
  useMemo: useMemo,
1214
+ useMetadata: useMetadata,
1048
1215
  useQuery: useQuery,
1049
1216
  useRef: useRef,
1050
1217
  useRouter: useRouter,
@@ -1071,6 +1238,7 @@
1071
1238
  exports.useEffect = useEffect;
1072
1239
  exports.useHash = useHash;
1073
1240
  exports.useMemo = useMemo;
1241
+ exports.useMetadata = useMetadata;
1074
1242
  exports.useQuery = useQuery;
1075
1243
  exports.useRef = useRef;
1076
1244
  exports.useRouter = useRouter;
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("lodash")):"function"==typeof define&&define.amd?define(["exports","lodash"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Ryunix={},e.lodash)}(this,(function(e,t){"use strict";let o={containerRoot:null,nextUnitOfWork:null,currentRoot:null,wipRoot:null,deletions:null,wipFiber:null,hookIndex:null,effects:null};const n=/[A-Z]/g,r=Object.freeze({TEXT_ELEMENT:Symbol("text.element").toString(),Ryunix_ELEMENT:Symbol("ryunix.element").toString(),RYUNIX_EFFECT:Symbol("ryunix.effect").toString(),RYUNIX_MEMO:Symbol("ryunix.memo").toString(),RYUNIX_URL_QUERY:Symbol("ryunix.urlQuery").toString(),RYUNIX_REF:Symbol("ryunix.ref").toString(),RYUNIX_STORE:Symbol("ryunix.store").toString(),RYUNIX_REDUCE:Symbol("ryunix.reduce").toString(),RYUNIX_FRAGMENT:Symbol("ryunix.fragment").toString(),RYUNIX_CONTEXT:Symbol("ryunix.context").toString()}),i=Object.freeze({object:"object",function:"function",style:"ryunix-style",className:"ryunix-class",children:"children",boolean:"boolean",string:"string"}),s=Object.freeze({style:"style",className:"className"}),l=Object.freeze({PLACEMENT:Symbol("ryunix.reconciler.status.placement").toString(),UPDATE:Symbol("ryunix.reconciler.status.update").toString(),DELETION:Symbol("ryunix.reconciler.status.deletion").toString(),NO_EFFECT:Symbol("ryunix.reconciler.status.no_efect").toString()}),a=(e,t,...o)=>({type:e,props:{...t,children:o.flat().map((e=>typeof e===i.object?e:c(e)))}}),c=e=>({type:r.TEXT_ELEMENT,props:{nodeValue:e,children:[]}}),u=e=>{const t=Array.isArray(e.children)?e.children:[e.children];return a(r.RYUNIX_FRAGMENT,{},...t)},p=e=>e.startsWith("on"),d=e=>e!==i.children&&!p(e),h=(e,t)=>o=>e[o]!==t[o],f=e=>t=>!(t in e),y=e=>{e&&(e.hooks&&e.hooks.length>0&&e.hooks.filter((e=>e.type===r.RYUNIX_EFFECT&&typeof e.cancel===i.function)).forEach((e=>{e.cancel()})),e.child&&y(e.child),e.sibling&&y(e.sibling))},m=e=>{if(e.hooks&&0!==e.hooks.length)for(let t=0;t<e.hooks.length;t++){const o=e.hooks[t];if(o.type===r.RYUNIX_EFFECT&&typeof o.effect===i.function&&null!==o.effect){typeof o.cancel===i.function&&o.cancel();const e=o.effect();o.cancel="function"==typeof e?e:void 0}}},E=(e,t,o)=>{Object.keys(t).filter(p).filter((e=>f(o)(e)||h(t,o)(e))).forEach((o=>{const n=o.toLowerCase().substring(2);e.removeEventListener(n,t[o])})),Object.keys(t).filter(d).filter(f(o)).forEach((t=>{e[t]=""})),Object.keys(o).filter(d).filter(h(t,o)).forEach((n=>{if(n===i.style)w(e,o["ryunix-style"]);else if(n===s.style)w(e,o.style);else if(n===i.className){if(""===o["ryunix-class"])throw new Error("data-class cannot be empty.");t["ryunix-class"]&&e.classList.remove(...t["ryunix-class"].split(/\s+/).filter(Boolean)||[]),e.classList.add(...o["ryunix-class"].split(/\s+/).filter(Boolean))}else if(n===s.className){if(""===o.className)throw new Error("className cannot be empty.");t.className&&e.classList.remove(...t.className.split(/\s+/).filter(Boolean)||[]),e.classList.add(...o.className.split(/\s+/).filter(Boolean))}else e[n]=o[n]})),Object.keys(o).filter(p).filter(h(t,o)).forEach((t=>{const n=t.toLowerCase().substring(2);e.addEventListener(n,o[t])}))},w=(e,t)=>{e.style=Object.keys(t).reduce(((e,o)=>e+=`${o.replace(n,(function(e){return"-"+e.toLowerCase()}))}: ${t[o]};`),"")},b=e=>{if(!e)return;let t=e.parent;for(;!t.dom;)t=t.parent;const o=t.dom;if(e.effectTag===l.PLACEMENT)null!=e.dom&&o.appendChild(e.dom),m(e);else if(e.effectTag===l.UPDATE)(e=>{e.hooks&&e.hooks.length>0&&e.hooks.filter((e=>e.type===r.RYUNIX_EFFECT&&e.cancel)).forEach((e=>{e.cancel()}))})(e),null!=e.dom&&E(e.dom,e.alternate.props,e.props),m(e);else if(e.effectTag===l.DELETION)return y(e),void x(e,o);b(e.child),b(e.sibling)},x=(e,t)=>{if(e.dom)t.removeChild(e.dom);else{let o=e.child;for(;o;)x(o,t),o=o.sibling}},g=(e,t)=>{let n,r=0,i=e.alternate&&e.alternate.child;for(;r<t.length||null!=i;){const s=t[r];let a;const c=i&&s&&s.type==i.type;c&&(a={type:i.type,props:s.props,dom:i.dom,parent:e,alternate:i,effectTag:l.UPDATE,hooks:i.hooks}),s&&!c&&(a={type:s.type,props:s.props,dom:null,parent:e,alternate:null,effectTag:l.PLACEMENT}),i&&!c&&(i.effectTag=l.DELETION,o.deletions.push(i)),i&&(i=i.sibling),0===r?e.child=a:s&&(n.sibling=a),n=a,r++}},k=e=>{const t=Array.isArray(e.props.children)?e.props.children.flat():[e.props.children];e.type===r.RYUNIX_FRAGMENT||e.dom||(e.dom=(e=>{if(e.type===r.RYUNIX_FRAGMENT)return null;const t=e.type==r.TEXT_ELEMENT?document.createTextNode(""):document.createElement(e.type);return E(t,{},e.props),t})(e)),g(e,t)},R=({src:e,props:t})=>{const o=new URLSearchParams,n=!e.startsWith("http")||!e.startsWith("https");t.width&&o.set("width",t.width),t.height&&o.set("height",t.height),t.quality&&o.set("quality",t.quality);const r=t.extension?`@${t.extension}`:"";return n?(()=>{const{hostname:e}=window.location;return"localhost"===e||"127.0.0.1"===e})()?(console.warn("Image optimizations only work with full links and must not contain localhost."),e):`${window.location.origin}/${e}`:`https://image.unsetsoft.com/image/${e}${r}?${o.toString()}`},N=e=>{let t=!1;for(;o.nextUnitOfWork&&!t;)o.nextUnitOfWork=I(o.nextUnitOfWork),t=e.timeRemaining()<1;!o.nextUnitOfWork&&o.wipRoot&&(o.deletions.forEach(b),b(o.wipRoot.child),o.currentRoot=o.wipRoot,o.wipRoot=null),requestIdleCallback(N)};requestIdleCallback(N);const I=e=>{if(e.type instanceof Function?(e=>{o.wipFiber=e,o.hookIndex=0,o.wipFiber.hooks=[];const t=[e.type(e.props)];e.type._contextId&&void 0!==e.props.value&&(e._contextId=e.type._contextId,e._contextValue=e.props.value),g(e,t)})(e):k(e),e.child)return e.child;let t=e;for(;t;){if(t.sibling)return t.sibling;t=t.parent}},v=e=>{o.nextUnitOfWork=e,o.wipRoot=e,o.deletions=[],o.hookIndex=0,o.effects=[],requestIdleCallback(N)},F=(e,t)=>(o.wipRoot={dom:t,props:{children:[e]},alternate:o.currentRoot},o.nextUnitOfWork=o.wipRoot,o.deletions=[],v(o.wipRoot),o.wipRoot),T=(e,t)=>_(((e,t)=>"function"==typeof t?t(e):t),e,t),_=(e,t,n)=>{const s=o.wipFiber.alternate&&o.wipFiber.alternate.hooks&&o.wipFiber.alternate.hooks[o.hookIndex],l={hookID:o.hookIndex,type:r.RYUNIX_STORE,state:s?s.state:n?n(t):t,queue:s&&Array.isArray(s.queue)?s.queue.slice():[]};s&&Array.isArray(s.queue)&&s.queue.forEach((t=>{l.state=e(l.state,t)}));return l.queue.forEach((t=>{l.state=e(l.state,t)})),o.wipFiber.hooks[o.hookIndex]=l,o.hookIndex++,[l.state,e=>{l.queue.push(typeof e===i.function?e:t=>e),o.wipRoot={dom:o.currentRoot.dom,props:o.currentRoot.props,alternate:o.currentRoot},o.deletions=[],o.hookIndex=0,v(o.wipRoot)}]},S=(e,t)=>{const n=o.wipFiber.alternate&&o.wipFiber.alternate.hooks&&o.wipFiber.alternate.hooks[o.hookIndex],i=(s=n?.deps,l=t,!s||!l||s.length!==l.length||s.some(((e,t)=>e!==l[t])));var s,l;const a={hookID:o.hookIndex,type:r.RYUNIX_EFFECT,deps:t,effect:i?e:null,cancel:n?.cancel};o.wipFiber.hooks[o.hookIndex]=a,o.hookIndex++},U=e=>{const t=o.wipFiber.alternate&&o.wipFiber.alternate.hooks&&o.wipFiber.alternate.hooks[o.hookIndex],n={type:r.RYUNIX_REF,value:t?t.value:{current:e}};return o.wipFiber.hooks[o.hookIndex]=n,o.hookIndex++,n.value},C=(e,n)=>{const i=o.wipFiber.alternate&&o.wipFiber.alternate.hooks&&o.wipFiber.alternate.hooks[o.hookIndex],s={type:r.RYUNIX_MEMO,value:null,deps:n};return i&&t.isEqual(i.deps,s.deps)?s.value=i.value:s.value=e(),o.wipFiber.hooks[o.hookIndex]=s,o.hookIndex++,s.value},L=(e,t)=>C((()=>e),t),O=(e=r.RYUNIX_CONTEXT,t={})=>{const n=({children:e})=>u({children:e});n._contextId=e;return{Provider:n,useContext:(e=r.RYUNIX_CONTEXT)=>{let n=o.wipFiber;for(;n;){if(n.type&&n.type._contextId===e)return n.props&&"value"in n.props?n.props.value:void 0;n=n.parent}return t}}},X=()=>{const e=new URLSearchParams(window.location.search),t={};for(let[o,n]of e.entries())t[o]=n;return t},Y=()=>{const[e,t]=T(window.location.hash);return S((()=>{const e=()=>{t(window.location.hash)};return window.addEventListener("hashchange",e),()=>window.removeEventListener("hashchange",e)}),[]),e},A=O("ryunix.navigation",{location:"/",params:{},query:{},navigate:e=>{},route:null}),q=(e,t)=>{const o=t.split("?")[0].split("#")[0],n=e.find((e=>e.NotFound)),r=n?{route:{component:n.NotFound},params:{}}:{route:{component:null},params:{}};for(const n of e){if(n.subRoutes){const e=q(n.subRoutes,t);if(e)return e}if("*"===n.path)return r;if(!n.path||"string"!=typeof n.path){console.warn("Invalid route detected:",n),console.info("if you are using { NotFound: NotFound } please add { path: '*', NotFound: NotFound }");continue}const e=[],i=new RegExp(`^${n.path.replace(/:\w+/g,(t=>(e.push(t.substring(1)),"([^/]+)")))}$`),s=o.match(i);if(s){return{route:n,params:e.reduce(((e,t,o)=>(e[t]=s[o+1],e)),{})}}}return r},M=({routes:e,children:t})=>{const[o,n]=T(window.location.pathname);S((()=>{const e=()=>n(window.location.pathname);return window.addEventListener("popstate",e),window.addEventListener("hashchange",e),()=>{window.removeEventListener("popstate",e),window.removeEventListener("hashchange",e)}}),[o]);const r=q(e,o)||{},i=X(),s={location:o,params:r.params||{},query:i,navigate:e=>{window.history.pushState({},"",e),n(e)},route:r.route};return a(A.Provider,{value:s},u({children:t}))},j=()=>A.useContext("ryunix.navigation"),P=()=>{const{route:e,params:t,query:o,location:n}=j();if(!e||!e.component)return null;const r=Y();return S((()=>{if(r){const e=r.slice(1),t=document.getElementById(e);t&&t.scrollIntoView({block:"start",behavior:"smooth"})}}),[r]),a(e.component,{key:n,params:t,query:o,hash:r})},W=({to:e,exact:t=!1,...o})=>{const{location:n,navigate:r}=j(),i=t?n===e:n.startsWith(e),s=o["ryunix-class"]?"ryunix-class":"className",l="function"==typeof(c=o["ryunix-class"]||o.className)?c({isActive:i}):c||"";var c;const{"ryunix-class":u,className:p,...d}=o;return a("a",{href:e,onClick:t=>{t.preventDefault(),r(e)},[s]:l,...d},o.children)};var D={createElement:a,render:F,init:(e,t="__ryunix")=>{o.containerRoot=document.getElementById(t);return F(e,o.containerRoot)},Fragment:u,Hooks:Object.freeze({__proto__:null,Children:P,NavLink:W,RouterProvider:M,createContext:O,useCallback:L,useEffect:S,useHash:Y,useMemo:C,useQuery:X,useRef:U,useRouter:j,useStore:T})};window.Ryunix=D,e.Children=P,e.Image=({src:e,...t})=>{const o={src:"true"===t.optimization?R({src:e,props:t}):e,...t};return a("img",o,null)},e.NavLink=W,e.RouterProvider=M,e.createContext=O,e.default=D,e.useCallback=L,e.useEffect=S,e.useHash=Y,e.useMemo=C,e.useQuery=X,e.useRef=U,e.useRouter=j,e.useStore=T,Object.defineProperty(e,"__esModule",{value:!0})}));
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("lodash")):"function"==typeof define&&define.amd?define(["exports","lodash"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Ryunix={},e.lodash)}(this,function(e,t){"use strict";let o={containerRoot:null,nextUnitOfWork:null,currentRoot:null,wipRoot:null,deletions:null,wipFiber:null,hookIndex:null,effects:null};const n=/[A-Z]/g,r=Object.freeze({TEXT_ELEMENT:Symbol("text.element").toString(),Ryunix_ELEMENT:Symbol("ryunix.element").toString(),RYUNIX_EFFECT:Symbol("ryunix.effect").toString(),RYUNIX_MEMO:Symbol("ryunix.memo").toString(),RYUNIX_URL_QUERY:Symbol("ryunix.urlQuery").toString(),RYUNIX_REF:Symbol("ryunix.ref").toString(),RYUNIX_STORE:Symbol("ryunix.store").toString(),RYUNIX_REDUCE:Symbol("ryunix.reduce").toString(),RYUNIX_FRAGMENT:Symbol("ryunix.fragment").toString(),RYUNIX_CONTEXT:Symbol("ryunix.context").toString()}),i=Object.freeze({object:"object",function:"function",style:"ryunix-style",className:"ryunix-class",children:"children",boolean:"boolean",string:"string"}),s=Object.freeze({style:"style",className:"className"}),a=Object.freeze({PLACEMENT:Symbol("ryunix.reconciler.status.placement").toString(),UPDATE:Symbol("ryunix.reconciler.status.update").toString(),DELETION:Symbol("ryunix.reconciler.status.deletion").toString(),NO_EFFECT:Symbol("ryunix.reconciler.status.no_efect").toString()}),l=(e,t,...o)=>({type:e,props:{...t,children:o.flat().map(e=>typeof e===i.object?e:c(e))}}),c=e=>({type:r.TEXT_ELEMENT,props:{nodeValue:e,children:[]}}),u=e=>{const t=Array.isArray(e.children)?e.children:[e.children];return l(r.RYUNIX_FRAGMENT,{},...t)},p=e=>e.startsWith("on"),d=e=>e!==i.children&&!p(e),h=(e,t)=>o=>e[o]!==t[o],f=e=>t=>!(t in e),y=e=>{e&&(e.hooks&&e.hooks.length>0&&e.hooks.filter(e=>e.type===r.RYUNIX_EFFECT&&typeof e.cancel===i.function).forEach(e=>{e.cancel()}),e.child&&y(e.child),e.sibling&&y(e.sibling))},m=e=>{if(e.hooks&&0!==e.hooks.length)for(let t=0;t<e.hooks.length;t++){const o=e.hooks[t];if(o.type===r.RYUNIX_EFFECT&&typeof o.effect===i.function&&null!==o.effect){typeof o.cancel===i.function&&o.cancel();const e=o.effect();o.cancel="function"==typeof e?e:void 0}}},E=(e,t,o)=>{Object.keys(t).filter(p).filter(e=>f(o)(e)||h(t,o)(e)).forEach(o=>{const n=o.toLowerCase().substring(2);e.removeEventListener(n,t[o])}),Object.keys(t).filter(d).filter(f(o)).forEach(t=>{e[t]=""}),Object.keys(o).filter(d).filter(h(t,o)).forEach(n=>{if(n===i.style)w(e,o["ryunix-style"]);else if(n===s.style)w(e,o.style);else if(n===i.className){if(""===o["ryunix-class"])throw new Error("data-class cannot be empty.");t["ryunix-class"]&&e.classList.remove(...t["ryunix-class"].split(/\s+/).filter(Boolean)||[]),e.classList.add(...o["ryunix-class"].split(/\s+/).filter(Boolean))}else if(n===s.className){if(""===o.className)throw new Error("className cannot be empty.");t.className&&e.classList.remove(...t.className.split(/\s+/).filter(Boolean)||[]),e.classList.add(...o.className.split(/\s+/).filter(Boolean))}else e[n]=o[n]}),Object.keys(o).filter(p).filter(h(t,o)).forEach(t=>{const n=t.toLowerCase().substring(2);e.addEventListener(n,o[t])})},w=(e,t)=>{e.style=Object.keys(t).reduce((e,o)=>e+=`${o.replace(n,function(e){return"-"+e.toLowerCase()})}: ${t[o]};`,"")},b=e=>{if(!e)return;let t=e.parent;for(;!t.dom;)t=t.parent;const o=t.dom;if(e.effectTag===a.PLACEMENT)null!=e.dom&&o.appendChild(e.dom),m(e);else if(e.effectTag===a.UPDATE)(e=>{e.hooks&&e.hooks.length>0&&e.hooks.filter(e=>e.type===r.RYUNIX_EFFECT&&e.cancel).forEach(e=>{e.cancel()})})(e),null!=e.dom&&E(e.dom,e.alternate.props,e.props),m(e);else if(e.effectTag===a.DELETION)return y(e),void g(e,o);b(e.child),b(e.sibling)},g=(e,t)=>{if(e.dom)t.removeChild(e.dom);else{let o=e.child;for(;o;)g(o,t),o=o.sibling}},x=(e,t)=>{let n,r=0,i=e.alternate&&e.alternate.child;for(;r<t.length||null!=i;){const s=t[r];let l;const c=i&&s&&s.type==i.type;c&&(l={type:i.type,props:s.props,dom:i.dom,parent:e,alternate:i,effectTag:a.UPDATE,hooks:i.hooks}),s&&!c&&(l={type:s.type,props:s.props,dom:null,parent:e,alternate:null,effectTag:a.PLACEMENT}),i&&!c&&(i.effectTag=a.DELETION,o.deletions.push(i)),i&&(i=i.sibling),0===r?e.child=l:s&&(n.sibling=l),n=l,r++}},k=e=>{const t=Array.isArray(e.props.children)?e.props.children.flat():[e.props.children];e.type===r.RYUNIX_FRAGMENT||e.dom||(e.dom=(e=>{if(e.type===r.RYUNIX_FRAGMENT)return null;const t=e.type==r.TEXT_ELEMENT?document.createTextNode(""):document.createElement(e.type);return E(t,{},e.props),t})(e)),x(e,t)},R=({src:e,props:t})=>{const o=new URLSearchParams,n=!e.startsWith("http")||!e.startsWith("https");t.width&&o.set("width",t.width),t.height&&o.set("height",t.height),t.quality&&o.set("quality",t.quality);const r=t.extension?`@${t.extension}`:"";return n?(()=>{const{hostname:e}=window.location;return"localhost"===e||"127.0.0.1"===e})()?(console.warn("Image optimizations only work with full links and must not contain localhost."),e):`${window.location.origin}/${e}`:`https://image.unsetsoft.com/image/${e}${r}?${o.toString()}`},N=e=>{let t=!1;for(;o.nextUnitOfWork&&!t;)o.nextUnitOfWork=I(o.nextUnitOfWork),t=e.timeRemaining()<1;!o.nextUnitOfWork&&o.wipRoot&&(o.deletions.forEach(b),b(o.wipRoot.child),o.currentRoot=o.wipRoot,o.wipRoot=null),requestIdleCallback(N)};requestIdleCallback(N);const I=e=>{if(e.type instanceof Function?(e=>{o.wipFiber=e,o.hookIndex=0,o.wipFiber.hooks=[];const t=[e.type(e.props)];e.type._contextId&&void 0!==e.props.value&&(e._contextId=e.type._contextId,e._contextValue=e.props.value),x(e,t)})(e):k(e),e.child)return e.child;let t=e;for(;t;){if(t.sibling)return t.sibling;t=t.parent}},T=e=>{o.nextUnitOfWork=e,o.wipRoot=e,o.deletions=[],o.hookIndex=0,o.effects=[],requestIdleCallback(N)},v=(e,t)=>(o.wipRoot={dom:t,props:{children:[e]},alternate:o.currentRoot},o.nextUnitOfWork=o.wipRoot,o.deletions=[],T(o.wipRoot),o.wipRoot),_=(e,t)=>F((e,t)=>"function"==typeof t?t(e):t,e,t),F=(e,t,n)=>{const s=o.wipFiber.alternate&&o.wipFiber.alternate.hooks&&o.wipFiber.alternate.hooks[o.hookIndex],a={hookID:o.hookIndex,type:r.RYUNIX_STORE,state:s?s.state:n?n(t):t,queue:s&&Array.isArray(s.queue)?s.queue.slice():[]};s&&Array.isArray(s.queue)&&s.queue.forEach(t=>{a.state=e(a.state,t)});return a.queue.forEach(t=>{a.state=e(a.state,t)}),o.wipFiber.hooks[o.hookIndex]=a,o.hookIndex++,[a.state,e=>{a.queue.push(typeof e===i.function?e:t=>e),o.wipRoot={dom:o.currentRoot.dom,props:o.currentRoot.props,alternate:o.currentRoot},o.deletions=[],o.hookIndex=0,T(o.wipRoot)}]},S=(e,t)=>{if("undefined"==typeof window)return void o.hookIndex++;const n=o.wipFiber.alternate&&o.wipFiber.alternate.hooks&&o.wipFiber.alternate.hooks[o.hookIndex],i=(s=n?.deps,a=t,!s||!a||s.length!==a.length||s.some((e,t)=>e!==a[t]));var s,a;const l={hookID:o.hookIndex,type:r.RYUNIX_EFFECT,deps:t,effect:i?e:null,cancel:n?.cancel};o.wipFiber.hooks[o.hookIndex]=l,o.hookIndex++},U=e=>{const t=o.wipFiber.alternate&&o.wipFiber.alternate.hooks&&o.wipFiber.alternate.hooks[o.hookIndex],n={type:r.RYUNIX_REF,value:t?t.value:{current:e}};return o.wipFiber.hooks[o.hookIndex]=n,o.hookIndex++,n.value},C=(e,n)=>{const i=o.wipFiber.alternate&&o.wipFiber.alternate.hooks&&o.wipFiber.alternate.hooks[o.hookIndex],s={type:r.RYUNIX_MEMO,value:null,deps:n};return i&&t.isEqual(i.deps,s.deps)?s.value=i.value:s.value=e(),o.wipFiber.hooks[o.hookIndex]=s,o.hookIndex++,s.value},O=(e,t)=>C(()=>e,t),A=(e=r.RYUNIX_CONTEXT,t={})=>{const n=({children:e})=>u({children:e});n._contextId=e;return{Provider:n,useContext:(e=r.RYUNIX_CONTEXT)=>{let n=o.wipFiber;for(;n;){if(n.type&&n.type._contextId===e)return n.props&&"value"in n.props?n.props.value:void 0;n=n.parent}return t}}},L=()=>{if("undefined"==typeof window)return{};const e=new URLSearchParams(window.location.search),t={};for(let[o,n]of e.entries())t[o]=n;return t},X=()=>{if("undefined"==typeof window)return"";const[e,t]=_(window.location.hash);return S(()=>{const e=()=>{t(window.location.hash)};return window.addEventListener("hashchange",e),()=>window.removeEventListener("hashchange",e)},[]),e},q=A("ryunix.navigation",{location:"/",params:{},query:{},navigate:e=>{},route:null}),Y=(e,t)=>{const o=t.split("?")[0].split("#")[0],n=e.find(e=>e.NotFound),r=n?{route:{component:n.NotFound},params:{}}:{route:{component:null},params:{}};for(const n of e){if(n.subRoutes){const e=Y(n.subRoutes,t);if(e)return e}if("*"===n.path)return r;if(!n.path||"string"!=typeof n.path){console.warn("Invalid route detected:",n),console.info("if you are using { NotFound: NotFound } please add { path: '*', NotFound: NotFound }");continue}const e=[],i=new RegExp(`^${n.path.replace(/:\w+/g,t=>(e.push(t.substring(1)),"([^/]+)"))}$`),s=o.match(i);if(s){return{route:n,params:e.reduce((e,t,o)=>(e[t]=s[o+1],e),{})}}}return r},j=({routes:e,children:t,ssrContext:o})=>{const n="undefined"==typeof window||!!o,r=n?o&&o.location||"/":window.location.pathname,[i,s]=_(r);S(()=>{if(n)return;const e=()=>s(window.location.pathname);return window.addEventListener("popstate",e),window.addEventListener("hashchange",e),()=>{window.removeEventListener("popstate",e),window.removeEventListener("hashchange",e)}},[i]);const a=Y(e,i)||{},c=n?o&&o.query||{}:L(),p={location:i,params:a.params||{},query:c,navigate:e=>{n||(window.history.pushState({},"",e),s(e))},route:a.route};return l(q.Provider,{value:p},u({children:t}))},M=()=>q.useContext("ryunix.navigation"),$=()=>{const{route:e,params:t,query:o,location:n}=M();if(!e||!e.component)return null;const r=X();return S(()=>{if(r){const e=r.slice(1),t=document.getElementById(e);t&&t.scrollIntoView({block:"start",behavior:"smooth"})}},[r]),l(e.component,{key:n,params:t,query:o,hash:r})},W=({to:e,exact:t=!1,...o})=>{const{location:n,navigate:r}=M(),i=t?n===e:n.startsWith(e),s=o["ryunix-class"]?"ryunix-class":"className",a="function"==typeof(c=o["ryunix-class"]||o.className)?c({isActive:i}):c||"";var c;const{"ryunix-class":u,className:p,...d}=o;return l("a",{href:e,onClick:t=>{t.preventDefault(),r(e)},[s]:a,...d},o.children)},P=(e={},t={})=>{const o="undefined"!=typeof globalThis&&globalThis.__RYUNIX_SSR_CONTEXT__;if(o){let n,r="",i="Ryunix App";t.title&&"object"==typeof t.title&&(n=t.title.template,"string"==typeof t.title.prefix&&(i=t.title.prefix));let s=e.pageTitle||e.title;return"string"==typeof s?r=""===s.trim()?i:n&&n.includes("%s")?n.replace("%s",s):s:"object"==typeof s&&null!==s?r=i:s||(r=i),r&&o.head.push(`<title>${r}</title>`),e.canonical&&o.head.push(`<link rel="canonical" href="${e.canonical}" />`),Object.entries(e).forEach(([e,t])=>{"title"!==e&&"pageTitle"!==e&&"canonical"!==e&&(e.startsWith("og:")||e.startsWith("twitter:")?o.head.push(`<meta property="${e}" content="${t}" />`):o.head.push(`<meta name="${e}" content="${t}" />`))}),e.script&&(Array.isArray(e.script)?e.script:[e.script]).forEach(e=>{o.head.push(e)}),e.style&&(Array.isArray(e.style)?e.style:[e.style]).forEach(e=>{o.head.push(e)}),void(e.custom&&(Array.isArray(e.custom)?e.custom:[e.custom]).forEach(e=>{o.head.push(e)}))}S(()=>{if("undefined"==typeof document)return;let o,n="",r="Ryunix App";t.title&&"object"==typeof t.title&&(o=t.title.template,"string"==typeof t.title.prefix&&(r=t.title.prefix));let i=e.pageTitle||e.title;if("string"==typeof i?n=""===i.trim()?r:o&&o.includes("%s")?o.replace("%s",i):i:"object"==typeof i&&null!==i?n=r:i||(n=r),document.title=n,e.canonical){let t=document.querySelector('link[rel="canonical"]');t||(t=document.createElement("link"),t.setAttribute("rel","canonical"),document.head.appendChild(t)),t.setAttribute("href",e.canonical)}Object.entries(e).forEach(([e,t])=>{if("title"===e||"pageTitle"===e||"canonical"===e)return;let o=`meta[name='${e}']`;(e.startsWith("og:")||e.startsWith("twitter:"))&&(o=`meta[property='${e}']`);let n=document.head.querySelector(o);n||(n=document.createElement("meta"),e.startsWith("og:")||e.startsWith("twitter:")?n.setAttribute("property",e):n.setAttribute("name",e),document.head.appendChild(n)),n.setAttribute("content",t)})},[JSON.stringify(e),JSON.stringify(t)])};var D={createElement:l,render:v,init:(e,t="__ryunix")=>{o.containerRoot=document.getElementById(t);return v(e,o.containerRoot)},Fragment:u,Hooks:Object.freeze({__proto__:null,Children:$,NavLink:W,RouterProvider:j,createContext:A,useCallback:O,useEffect:S,useHash:X,useMemo:C,useMetadata:P,useQuery:L,useRef:U,useRouter:M,useStore:_})};window.Ryunix=D,e.Children=$,e.Image=({src:e,...t})=>{const o={src:"true"===t.optimization?R({src:e,props:t}):e,...t};return l("img",o,null)},e.NavLink=W,e.RouterProvider=j,e.createContext=A,e.default=D,e.useCallback=O,e.useEffect=S,e.useHash=X,e.useMemo=C,e.useMetadata=P,e.useQuery=L,e.useRef=U,e.useRouter=M,e.useStore=_,Object.defineProperty(e,"__esModule",{value:!0})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unsetsoft/ryunixjs",
3
- "version": "1.1.37",
3
+ "version": "1.2.0-canary.0",
4
4
  "license": "MIT",
5
5
  "main": "./dist/Ryunix.min.js",
6
6
  "types": "./dist/Ryunix.d.ts",
@@ -22,16 +22,16 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@rollup/plugin-terser": "0.4.4",
25
- "eslint": "8.56.0",
25
+ "eslint": "9.29.0",
26
26
  "lodash": "^4.17.21",
27
27
  "rollup": "^4.41.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@babel/core": "7.23.2",
30
+ "@babel/core": "7.27.4",
31
31
  "@babel/preset-env": "^7.27.2",
32
- "babel-jest": "29.5.0",
33
- "jest": "29.5.0",
34
- "jest-environment-jsdom": "^29.7.0"
32
+ "babel-jest": "30.0.2",
33
+ "jest": "30.0.2",
34
+ "jest-environment-jsdom": "^30.0.2"
35
35
  },
36
36
  "engines": {
37
37
  "node": "^18 || ^20 || ^22"